我想使用 overlayroot 保护系统,这样所做的一切(即使是具有 root 权限的用户执行的操作)在重启后都不会保留。我找到了几个指南来说明如何做到这一点,但没有一个指南告诉我这样做有多安全,以及是否有技巧可以克服保护。
我的方法:GRUB 被锁定并且只提供没有密码的 overlayroot 选项。
但这很可能不足以保护系统,因为某些人(嗯,具有 root 权限的人)可以这样做dd if=/dev/zero of=/dev/sda
,而且我认为系统在重启后将拒绝启动。
根据我的研究,限制 root 直接访问 等的唯一方法/dev/sda
是/dev/sda1
SELinux。SELinux 似乎非常复杂,而且仅仅为了限制对几个文件的访问就完全是小题大做,但它似乎是限制 root 的唯一方法。
我的问题是:
除了访问之外,具有 root 权限的用户还有其他克服 overlayroot 的可能性吗
/dev/sda(X)
?是否有其他选项可以阻止访问
/dev/sda(X)
,如果没有,是否有一个仅阻止访问某些文件的 SELinux 策略的简单示例/指南?
补充 2016-09-12:
我找到了这个:https://github.com/msuhanov/Linux-write-blocker/
这是一个非常小(7 行代码)且简单的内核补丁,它使 Linux 内核实际上尊重块设备的只读标志(否则该标志对于 fs 驱动程序更具信息性)。
这是一个很好的起点,但有一个问题:root 可以轻松更改只读标志。我现在的想法是:
内核启动时带有附加选项
forcero=/dev/sda forcero=/dev/sda1
在某个时候,这会被解析并且要么使用只读标志扩展现有的块设备列表,要么创建一个新的只读块设备列表。
该补丁中的代码已扩展以检查该标志
我知道这并不完全安全,因为某些自定义内核模块可能会重置该标志(除非您签署所有模块并仅允许签署的模块)。
我实际上从未编写过内核代码,我遇到的第一个问题是:我找不到结构block_device
或函数的定义bdevname
。我用http://lxr.free-electrons.com/ident找到它,但没有运气。我的第二个想法是:如果某处有一个列表,它是否稳定,或者重新扫描设备是否可以清除它?有一个函数name_to_dev_t
可以将名称转换/dev/sda1
为dev_t
类型,它只是一个整数,这与有什么关系block_device
?
有人能给我一些关于如何编写内核补丁的提示吗?我还愿意听取其他想法。
答案1
我延长了之前提到的补丁检查启动命令行设置的只读块设备列表。
/*
* Block write and discard commands going to a read-only device.
* We do this because kernel drivers often lack necessary checks
* and send write/discard commands to read-only block devices.
*/
if (unlikely((bio->bi_rw & (REQ_WRITE | REQ_WRITE_SAME | REQ_DISCARD))
&& (bdev_read_only(bio->bi_bdev) || bdev_check_readonly_boot_param(bio->bi_bdev->bd_inode->i_rdev)))) {
pr_warn("unexpected %s command to %s blocked\n",
(bio->bi_rw & REQ_DISCARD) ? "discard" : "write",
bdevname(bio->bi_bdev, b));
goto end_io;
}
这是扩展的原始补丁,用于使用此功能检查启动参数:
extern dev_t READONLY_DEV[];
static inline int bdev_check_readonly_boot_param(dev_t bd)
{
dev_t *dev = READONLY_DEV;
while (*dev) {
if (*dev == bd) return 1;
dev++;
}
return 0;
}
在do_mounts.c
我添加了这个:
静态字符__initdata saved_readonly_dev[64]; dev_t READONLY_DEV[32] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; 静态 int __init readonly_bdev_setup(char *line) { strlcpy(saved_readonly_dev,行,sizeof(saved_readonly_dev)); 返回1; } __setup(“forcero=”,readonly_bdev_setup); /* * 包含只读设备的设置列表 */ void __init setup_readonly_bdev(void) { int i = 0; char *dev_name,*readonly_dev; 如果(saved_readonly_dev[0]){ 只读_dev = 已保存的只读_dev; 做 { dev_name = strsep(&readonly_dev, ","); 如果(设备名称){ READONLY_DEV[i] = name_to_dev_t(dev_name); 如果 (READONLY_DEV[i]) { 我++; printk(KERN_NOTICE "将 %s 设置为只读。\n",dev_name); } 别的 printk(KERN_WARNING "设置只读时错误:无法识别块设备‘%s’\n",dev_name); } } 当(设备名称); } }
此外,在main.c
函数中kernel_init_freeable
我添加了一行来调用我的函数(该函数在中声明init.h
):
如果(sys_access((const char __user *)ramdisk_execute_command,0)!= 0){ ramdisk_execute_command = NULL; 准备命名空间(); } 设置_readonly_bdev();
现在您可以使用命令行参数启动内核forcero=8:16,8:17
,它将阻止对此设备的所有写调用。/dev/sdb
对我来说不起作用,内核函数无法解析dev_t
该设备的 -id。请注意,内核不知道该设备是只读的,您可以在其上写入,甚至 dd 也不会告诉您任何问题,但如果您查看kern.log
,您会看到很多 I/O 错误。如果您删除 (Cinnamon 的默认 GUI 文件管理器)中只读分区上的文件nemo
,该文件就消失了,但是您按 F5 它又回来了。同样重要的是:阻止/dev/sda
不会自动阻止像这样的设备/dev/sda1
,您必须列出所有块设备。但这意味着您可以在某些分区可写时保护引导扇区/分区表。
我非常确定这个补丁不符合要合并的内核代码质量标准,如果有人可以清理/改进它或者告诉我应该怎么做我会很高兴。
答案2
保护系统不受任何修改,同时允许“root”使用完全访问权限,这似乎是一场失败的战斗。root 的全部意义在于它可以做任何事情。正如您所提到的,您可以插入任何内核代码,从而访问任何内存位置,..一切。
有两种情况:1) 您想要防止意外写入,2) 您想要防止恶意写入。对于 1,是的,像 overlayfs 这样的技巧已经可以防止大多数意外写入。对于 2,请以非 root 身份运行不受信任的软件。
但是仍然有一个显而易见的方法可以做到这一点,那就是简单地使用硬件保护(以便软件,甚至 root/kernel/..)无法对其进行写入。(例如从 CD 或只读介质运行)