近日,我将一个基于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 TEST
static 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:我依然喜欢用####来做打印前缀,因为它一目了然,容易在日志文件中快速匹配。可能是我不懂更正规的做法吧,希望有人能教我。
浙江温州皮鞋湿,下雨进水不会胖!
转载:https://blog.csdn.net/dog250/article/details/104055905
查看评论