Назад | Перейти на главную страницу

Overlayroot: как полностью защитить систему Linux от изменений?

Я хотел бы защитить систему с помощью overlayroot, чтобы все, что делается - даже пользователем с правами root - не выдержит перезагрузки. Я нашел несколько руководств, как это сделать, но ни одно из них не говорит мне, насколько это безопасно и есть ли уловки для преодоления защиты.

Мой подход: GRUB заблокирован и предлагает только опцию overlayroot без пароля.

Но этого, скорее всего, недостаточно для защиты системы, как мог бы сделать кто-то (ну, кто-то с правами root) dd if=/dev/zero of=/dev/sda и я предполагаю, что система откажется загружаться после перезапуска.

Согласно моим исследованиям, единственный способ ограничить root от прямого доступа /dev/sda, /dev/sda1 и так далее - это SELinux. 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/sda1 к dev_t тип, который является просто целым числом, как это связано с block_device?

Может кто-нибудь подсказать, как написать этот патч ядра? Я все еще открыт для других идей.

Защита системы от любых модификаций и в то же время разрешение «root» использовать полный доступ кажется проигранной битвой. Вся суть root в том, что он может делать что угодно. Как вы упомянули, вы можете вставить любой код ядра и, следовательно, получить доступ к любой области памяти, ... ко всему.

Есть два сценария: 1) вы хотите защитить от случайной записи и 2) вы хотите защитить от злонамеренной записи. Во-первых, да, такой трюк, как overlayfs, уже может защитить от большинства случайных записей. Во-вторых, запустите ненадежное программное обеспечение не как root.

Однако есть очевидный способ сделать это - просто использовать аппаратную защиту (чтобы программное обеспечение, даже root / kernel / ..) не могло писать в него. (например, запуск с компакт-диска или носителя только для чтения)

Я продлил патч, упомянутый ранее для проверки по списку блочных устройств только для чтения, настроенному в командной строке загрузки.

/*
 * 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 Я добавил это:

static char __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};

static int __init readonly_bdev_setup(char *line)
{
    strlcpy(saved_readonly_dev, line, sizeof(saved_readonly_dev));
    return 1;
}

__setup("forcero=", readonly_bdev_setup);

/*
 * Setup list with read-only devices
 */
void __init setup_readonly_bdev(void)
{
    int i = 0;
    char *dev_name, *readonly_dev;

    if (saved_readonly_dev[0]) {
        readonly_dev = saved_readonly_dev;
        do {
            dev_name = strsep(&readonly_dev, ",");
            if (dev_name) {
                READONLY_DEV[i] = name_to_dev_t(dev_name);
                if (READONLY_DEV[i]) {
                    i++;
                    printk(KERN_NOTICE "Set %s to read-only.\n",dev_name);
                }
                else
                    printk(KERN_WARNING "Error setting read-only: Could not identify block-device '%s'\n",dev_name);
            }
        } while (dev_name);
    }
}

Дополнительно в main.c в функции kernel_init_freeable Я добавил одну строку для вызова моей функции (функция объявлена ​​в init.h):

   if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }

    setup_readonly_bdev();

Теперь вы можете загрузить ядро ​​с аргументом командной строки forcero=8:16,8:17 и он заблокирует все вызовы записи на эти устройства. /dev/sdb у меня не работает, функция ядра не может разрешить dev_t-ид этого. Обратите внимание, что ядро ​​не знает, что устройство предназначено только для чтения, вы можете писать на нем, и даже dd не сообщит вам о каких-либо проблемах, но если вы посмотрите в kern.log, тогда вы увидите множество ошибок ввода-вывода. Если вы удалите файл в разделе только для чтения в nemo (файловый менеджер графического интерфейса пользователя Cinnamon по умолчанию), он исчез, но затем вы нажимаете F5, и он возвращается. Также важно: Блокировка /dev/sda не будет автоматически блокировать такие устройства, как /dev/sda1, вы должны перечислить все блочные устройства. Но это означает, что вы можете защитить загрузочный сектор / таблицу разделов, пока некоторые разделы доступны для записи.

Я уверен, что этот патч не соответствует стандартам качества кода ядра для слияния, я был бы очень рад, если бы кто-нибудь мог его почистить / улучшить или сказать мне, что мне делать.