Como evitar que um processo grave arquivos

11

Eu quero executar um comando no Linux de uma maneira que ele não possa criar ou abrir nenhum arquivo para escrever. Ele ainda deve ser capaz de ler arquivos normalmente (então um chroot vazio não é uma opção), e ainda ser capaz de escrever em arquivos já abertos (especialmente stdout).

Pontos de bônus se escrever arquivos em determinados diretórios (ou seja, o diretório atual) ainda é possível.

Estou procurando uma solução que seja process-local, ou seja, não envolva configurações como AppArmor ou SELinux para todo o sistema, nem privilégios de root. Pode envolver a instalação de seus módulos do kernel, no entanto.

Eu estava procurando recursos e isso teria sido bom e fácil, se houvesse a capacidade de criar arquivos. ulimit é outra abordagem que seria conveniente, se abrangesse este caso de uso.

    
por Joachim Breitner 13.02.2013 / 12:31

8 respostas

4

Parece que a ferramenta certa para este trabalho é fseccomp Baseado no código sync-ignoring f de Bastian Blank, eu criei esse arquivo relativamente pequeno que faz com que todos os seus filhos não consigam abrir um arquivo para escrever :

/*
 * Copyright (C) 2013 Joachim Breitner <[email protected]>
 *
 * Based on code Copyright (C) 2013 Bastian Blank <[email protected]>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#define _GNU_SOURCE 1
#include <errno.h>
#include <fcntl.h>
#include <seccomp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#define filter_rule_add(action, syscall, count, ...) \
  if (seccomp_rule_add(filter, action, syscall, count, ##__VA_ARGS__)) abort();

static int filter_init(void)
{
  scmp_filter_ctx filter;

  if (!(filter = seccomp_init(SCMP_ACT_ALLOW))) abort();
  if (seccomp_attr_set(filter, SCMP_FLTATR_CTL_NNP, 1)) abort();
  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_WRONLY, O_WRONLY));
  filter_rule_add(SCMP_ACT_ERRNO(EACCES), SCMP_SYS(open), 1, SCMP_A1(SCMP_CMP_MASKED_EQ, O_RDWR, O_RDWR));
  return seccomp_load(filter);
}

int main(__attribute__((unused)) int argc, char *argv[])
{
  if (argc <= 1)
  {
    fprintf(stderr, "usage: %s COMMAND [ARG]...\n", argv[0]);
    return 2;
  }

  if (filter_init())
  {
    fprintf(stderr, "%s: can't initialize seccomp filter\n", argv[0]);
    return 1;
  }

  execvp(argv[1], &argv[1]);

  if (errno == ENOENT)
  {
    fprintf(stderr, "%s: command not found: %s\n", argv[0], argv[1]);
    return 127;
  }

  fprintf(stderr, "%s: failed to execute: %s: %s\n", argv[0], argv[1], strerror(errno));
  return 1;
}

Aqui você pode ver que ainda é possível ler arquivos:

[jojo@kirk:1] Wed, der 06.03.2013 um 12:58 Uhr Keep Smiling :-)
> ls test
ls: cannot access test: No such file or directory
> echo foo > test
bash: test: Permission denied
> ls test
ls: cannot access test: No such file or directory
> touch test
touch: cannot touch 'test': Permission denied
> head -n 1 no-writes.c # reading still works
/*

Ele não impede a exclusão de arquivos, nem a movimentação deles, ou outras operações de arquivo além da abertura, mas isso pode ser adicionado.

Uma ferramenta que permite isso sem ter que escrever código C é syscall_limiter .

    
por 06.03.2013 / 13:01
9

Que tal criar um chroot vazio, então montar o sistema de arquivos principal como somente leitura dentro do chroot?

Provavelmente deveria ser algo assim para criar uma montagem de bind somente leitura:

mount --bind /foo/ /path/to/chroot/
mount -o remount,ro /path/to/chroot/

Você pode vincular outros diretórios aos quais deseja que a cadeia tenha acesso de gravação. Tenha cuidado se você precisar montar diretórios especiais (/ dev /, / proc /, / sys /), montá-los como estão pode ser inseguro.

    
por 13.02.2013 / 15:36
4

Você consideraria escrever um substituto para a função open(…) e carregá-lo usando LD_PRELOAD?

    
por 13.02.2013 / 13:16
3

A solução mais simples é provavelmente um programa wrapper que cria um novo namespace de sistema de arquivos com os sistemas de arquivos relevantes montados como somente leitura e, em seguida, executa o programa que você está tentando restringir.

Isso é o que o systemd faz quando você usa ReadOnlyDirectories= para marcar determinados diretórios como somente leitura para um serviço. Há também um comando unshare em util-linux que pode fazer o trabalho de criar um novo namespace, para que você possa fazer algo como:

unshare -m <wrapper>

onde wrapper teria apenas que remontar os sistemas de arquivos conforme necessário antes de iniciar o programa de destino real.

O único problema é que você precisa ser root para criar o novo namespace ...

    
por 13.02.2013 / 13:08
2

Você pode executá-lo em um chroot, montando versões especiais de /tmp e dentro delas. Talvez o systemd seja útil, e particularmente systemd-nspawn(1) , que se parece com o que você quer.

    
por 13.02.2013 / 13:13
2

Uma máquina virtual possibilitaria que o script fosse escrito em qualquer lugar sem afetar o sistema host e inspecionar onde está realmente tentando gravar, que parecem ser os objetivos.

Por exemplo, você pode facilmente iniciar o Arch Linux com

kvm -boot d -m 512 -cdrom archlinux-*.iso
    
por 13.02.2013 / 13:31
2

Fazer algumas configurações iniciais como root é realmente a maneira mais fácil. Especificamente, um chroot em um bind somente-leitura mount é o caminho de menor resistência.

Você pode usar bindfs em vez de mount --bind para criar a visualização somente leitura sem precisar ser raiz. No entanto, você precisa fazer algo como root para impedir o acesso a outros arquivos, como o chroot.

Outra abordagem é LD_PRELOAD uma biblioteca que é conectada ao arquivo abertura e recusa-se a permitir a escrita. Isso não requer privilégios especiais. Do ponto de vista de segurança, isso pode ser ignorado, mas não há problema em seu caso de uso, onde você precisa apenas conter um recurso específico e não um código nativo arbitrário. Eu não sei de uma biblioteca existente para isso, no entanto. LD_PRELOAD também pode ser usado para limitar o programa à visualização somente leitura criada com mount --bind ou bindfs ; mais uma vez, eu não sei de uma biblioteca existente.

Em Debian e derivados, você pode configurar um ambiente schroot . O Schroot é root setuid e precisa ser configurado como root, mas pode ser executado por qualquer usuário autorizado.

Um método que não requer cooperação do root é executar o processo em uma máquina virtual. Você pode configurar o KVM ou o VirtualBox, ou o Linux no modo de usuário . É um pouco pesado e significa consumo extra de memória, mas não deve afetar significativamente a velocidade da computação simbólica bruta.

Como "prender" um processo sem ser root? pode fornecer alguma inspiração.

    
por 14.02.2013 / 01:53
1

Uma maneira de pelo menos evitar que o processo grave os arquivos (mas não de criá-los) é chamar ulimit -f 0 primeiro. Isso abortará o processo assim que ele tentar gravar em um arquivo, mas a criação de arquivos vazios ainda é possível.

    
por 04.03.2013 / 14:50