Overlayroot: Como proteger completamente um sistema Linux contra alterações?

1

Eu gostaria de proteger um sistema com overlayroot, então tudo que é feito - mesmo por um usuário com permissão de root - não sobreviverá a uma reinicialização. Eu encontrei vários guias como fazer isso, mas nenhum deles me diz o quão seguro é e se há truques para superar a proteção.

Minha abordagem: o GRUB está bloqueado e oferece apenas a opção overlayroot sem senha.

Mas isso provavelmente não é suficiente para proteger o sistema, já que alguém (bem, alguém com permissão de root) poderia fazer dd if=/dev/zero of=/dev/sda e eu assumo que o sistema irá se recusar a inicializar após o reinício.

De acordo com a minha pesquisa, a única maneira de restringir o root de acessar diretamente o /dev/sda , /dev/sda1 e assim por diante é o SELinux. O SELinux parece ser terrivelmente complicado e um exagero total apenas para restringir o acesso a alguns arquivos, mas parece ser a única maneira de restringir o root.

Então, minhas perguntas são:

  1. Existem outras possibilidades para um usuário com privilégio de root superar o overlayroot do que acessar /dev/sda(X) ?

  2. Existem outras opções para impedir o acesso a /dev/sda(X) e, caso contrário, existe um exemplo / guia simples para uma política do SELinux que bloqueia apenas o acesso a determinados arquivos?

Adição 2016-09-12:
Eu encontrei isto: link
É um patch de kernel muito pequeno (7 linhas de código) e simples que faz com que o kernel Linux realmente respeite o sinalizador somente leitura de um dispositivo de bloco (caso contrário, este sinalizador é mais informativo para um driver fs).

Este é um ótimo ponto de partida, mas um problema: o root pode facilmente alterar o sinalizador somente leitura. Minha ideia agora:

  1. O kernel é inicializado com opções adicionais forcero=/dev/sda forcero=/dev/sda1

  2. Em algum momento, isso é analisado e uma lista existente de dispositivos de bloco é estendida com um sinalizador somente leitura ou uma nova lista de dispositivos de bloco somente leitura é criada.

  3. O código desse patch é estendido para verificar esse sinalizador

Eu sei que isso não é perfeitamente seguro, porque algum módulo do kernel personalizado pode redefinir esse sinalizador (exceto que você assina todos os módulos e permite somente módulos assinados).

Na verdade, nunca escrevi código do kernel, o primeiro problema que encontrei é: não consegui encontrar a definição da estrutura block_device ou da função bdevname . Eu usei o link para encontrá-lo, mas sem sorte. Meu segundo pensamento: Se há uma lista em algum lugar, é estável ou poderia uma nova varredura para dispositivos limpá-lo? Existe a função name_to_dev_t que traduz um nome como /dev/sda1 para o tipo dev_t , que é apenas um inteiro, como isso está relacionado a block_device ?

Alguém pode me dar algumas dicas sobre como escrever o patch do kernel? Eu também ainda estou aberto para outras ideias.

    
por Stefan 11.09.2016 / 01:21

2 respostas

0

Eu estendi o patch mencionado antes para verificar uma lista de dispositivos de bloco somente leitura configurados pela linha de comando de inicialização.

/*
 * 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;
}

Este é o patch original estendido para verificar o parâmetro de inicialização com esta função:

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;
}

No do_mounts.c , adicionei isto:

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);
    }
}

Além disso, no main.c na função kernel_init_freeable eu adicionei uma linha para chamar minha função (a função é declarada em init.h ):

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

    setup_readonly_bdev();

Agora você pode inicializar o kernel com o argumento de linha de comando forcero=8:16,8:17 e ele bloqueará todas as chamadas de gravação para esses dispositivos. /dev/sdb não funciona para mim, a função kernel não pode resolver um dev_t -id disso. Note que o kernel não sabe que o dispositivo é somente de leitura, você pode escrever nele e até mesmo o dd não vai te informar nenhum problema, mas se você olhar no kern.log , você verá muitos erros de I / O então. Se você excluir um arquivo na partição somente leitura em nemo (o gerenciador de arquivos padrão da GUI do Cinnamon), ele será removido, mas você pressionará F5 e voltará. Também é importante: o bloqueio de /dev/sda não bloqueará automaticamente dispositivos como /dev/sda1 , você precisa listar todos os dispositivos de bloco. Mas isso significa que você pode proteger o setor de inicialização / tabela de partição enquanto algumas partições são graváveis.

Esse patch com certeza não atende aos padrões de qualidade de código do kernel a serem mesclados, ficaria muito feliz se alguém pudesse limpá-lo / melhorá-lo ou me dizer o que devo fazer.

    
por 13.09.2016 / 21:09
0

Proteger um sistema de qualquer modificação e, ao mesmo tempo, permitir a "raiz" usando acesso total parece uma batalha perdida. O ponto principal da raiz é que ela pode fazer qualquer coisa. Como você mencionou, você pode inserir qualquer código do kernel e, portanto, acessar qualquer localização de memória, tudo.

Existem dois cenários: 1) Você quer proteger contra gravações acidentais e 2) proteger contra gravações maliciosas. Para 1, sim, um truque como o overlayfs já pode proteger contra a maioria das gravações acidentais. Para 2, execute seu software não confiável como algo diferente de root.

Há, no entanto, uma maneira óbvia de fazer isso ainda, e é simplesmente usar proteção de hardware (para que o software, mesmo root / kernel / ..) não possa gravar nele. (por exemplo, executar a partir de um CD ou mídia somente leitura)

    
por 02.05.2017 / 15:48

Tags