做网站的赢点公司整站优化工具
近日,我将一个基于eBPF实现的SandFS中的eBPF部分摘除了,形成了一个单纯的SandFS框架:
https://github.com/marywangran/sandfs_with_no_ebpf
纳尼?eBPF大红大紫之时,我竟然反其道而行之,这不犯了形而上学的错误了吗?
没错,事实上我摘除它的原因是因为这个原生的eBPF-based-SandFS在我的两个开发环境中均没有编译通过:
- CentOS 7-3.10内核:llvm 8.0.1未安装成功,因为glibc版本太低。
- Ubuntu 19.10-5.3内核,SandFS-Kernel编译失败,没时间折腾。
然而我又非常喜欢SandFS这个框架:
- 没有修改VFS本身的代码,而是通过实现一个wrap fs来嫁接eBPF字节码。
我想让它运行起来,我在去深圳的火车上就想让它运行起来。
同时,我又特别特别喜欢Netfilter,于是我就想让为SandFS嫁接一个类似的Filter,来完成我无能为力的eBPF Program的功能,就这样。
完成代码后,暂时没有时间写类似iptables的用户态程序用来灌入策略,那就写了几个内核态的测试case,完成下面的小功能:
- 用户zhaoya的uid为1000,以zhaoya登录一次不能读取超过10字节的文件内容,一次只能读取最多10字节。
- 不能往任何文件里写skinshoe这个单词。
- 不能读取根目录下名称为key的文件。
效果如下:
其中testread的代码如下:
// gcc testread.c -o testread
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>int main(int argc, char **argv)
{int fd;int len, ret;char buf[1024] = {0};len = atoi(argv[2]);fd = open(argv[1], O_RDONLY);ret = read(fd, buf, len);perror("read");printf("%s\n", buf);return 0;
}
我的三个测试case代码如下:
#define TEST
#ifdef TESTstatic int test_read_func(unsigned int hook, struct sandfs_args *args, void *priv, int *handled)
{int ret = FS_ACCEPT;struct sandfs_args *largs = args;kuid_t id;size_t count;struct cred *cred;cred = (struct cred *)largs->args[SANDFS_IDX_CRED].value;id = cred->uid;count = *((size_t *)largs->args[SANDFS_IDX_COUNT].value);if (id.val == 1000 && count > 10) {printk("#### test read deny request\n");ret = FS_DROP;}*handled = 1;return ret;
}static int test_write_func(unsigned int hook, struct sandfs_args *args, void *priv, int *handled)
{int ret = FS_ACCEPT;struct sandfs_args *largs = args;size_t count;char *buf;count = largs->args[SANDFS_IDX_BUF].size;buf = (char *)largs->args[SANDFS_IDX_BUF].value;if (strnstr(buf, "skinshoe", count)) {printk("#### test write deny request\n");ret = FS_DROP;}*handled = 1;return ret;
}static int test_lookup_func(unsigned int hook, struct sandfs_args *args, void *priv, int *handled)
{int ret = FS_ACCEPT;struct sandfs_args *largs = args;size_t len;char *path;len = largs->args[SANDFS_IDX_PATH].size;path = (char *)largs->args[SANDFS_IDX_PATH].value;if (!strncmp(path, "/key", len)) {printk("#### test lookup deny request\n");ret = FS_DROP;}*handled = 1;return ret;
}static struct vfs_rule test_read_rule = {.name = "test read",.func = test_read_func,.hooknum = SANDFS_READ,
};static struct vfs_rule test_write_rule = {.name = "test write",.func = test_write_func,.hooknum = SANDFS_WRITE,
};static struct vfs_rule test_lookup_rule = {.name = "test lookup",.func = test_lookup_func,.hooknum = SANDFS_LOOKUP,
};
#endif
完成的代码在sandfs_with_no_ebpf的devel分支,等待实现了用户态配置程序之后再进行一次merge:
https://github.com/marywangran/sandfs_with_no_ebpf
当然,很多人会觉得这个东西没有意义,没有技术含量,太简单,这些都无所谓,它的意义是特定于我自己的:
- 可以精细化特定mount namespace中的文件系统访问控制。
- 过年回家前治愈无聊缓解乏力。
- 有时间还能写写用户态配置程序,学学怎么编程。
对于不会编程的我来讲,这就是小惊喜,小收获了。
PS:我依然喜欢用####来做打印前缀,因为它一目了然,容易在日志文件中快速匹配。可能是我不懂更正规的做法吧,希望有人能教我。
浙江温州皮鞋湿,下雨进水不会胖!