Overlayroot:如何全面保护 Linux 系统免受更改?

Overlayroot:如何全面保护 Linux 系统免受更改?

我想使用 overlayroot 保护系统,这样所做的一切(即使是具有 root 权限的用户执行的操作)在重启后都不会保留。我找到了几个指南来说明如何做到这一点,但没有一个指南告诉我这样做有多安全,以及是否有技巧可以克服保护。

我的方法:GRUB 被锁定并且只提供没有密码的 overlayroot 选项。

但这很可能不足以保护系统,因为某些人(嗯,具有 root 权限的人)可以这样做dd if=/dev/zero of=/dev/sda,而且我认为系统在重启后将拒绝启动。

根据我的研究,限制 root 直接访问 等的唯一方法/dev/sda/dev/sda1SELinux。SELinux 似乎非常复杂,而且仅仅为了限制对几个文件的访问就完全是小题大做,但它似乎是限制 root 的唯一方法。

我的问题是:

  1. 除了访问之外,具有 root 权限的用户还有其他克服 overlayroot 的可能性吗/dev/sda(X)

  2. 是否有其他选项可以阻止访问/dev/sda(X),如果没有,是否有一个仅阻止访问某些文件的 SELinux 策略的简单示例/指南?

补充 2016-09-12:
我找到了这个:https://github.com/msuhanov/Linux-write-blocker/
这是一个非常小(7 行代码)且简单的内核补丁,它使 Linux 内核实际上尊重块设备的只读标志(否则该标志对于 fs 驱动程序更具信息性)。

这是一个很好的起点,但有一个问题:root 可以轻松更改只读标志。我现在的想法是:

  1. 内核启动时带有附加选项forcero=/dev/sda forcero=/dev/sda1

  2. 在某个时候,这会被解析并且要么使用只读标志扩展现有的块设备列表,要么创建一个新的只读块设备列表。

  3. 该补丁中的代码已扩展以检查该标志

我知道这并不完全安全,因为某些自定义内核模块可能会重置该标志(除非您签署所有模块并仅允许签署的模块)。

我实际上从未编写过内核代码,我遇到的第一个问题是:我找不到结构block_device或函数的定义bdevname。我用http://lxr.free-electrons.com/ident找到它,但没有运气。我的第二个想法是:如果某处有一个列表,它是否稳定,或者重新扫描设备是否可以清除它?有一个函数name_to_dev_t可以将名称转换/dev/sda1dev_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 或只读介质运行)

相关内容