Come impedire a un processo di scrivere file


13

Voglio eseguire un comando su Linux in modo tale da non poter creare o aprire alcun file da scrivere. Dovrebbe essere ancora in grado di leggere i file normalmente (quindi un chroot vuoto non è un'opzione) e dovrebbe comunque essere in grado di scrivere su file già aperti (specialmente stdout).

Punti bonus se la scrittura di file in determinate directory (cioè la directory corrente) è ancora possibile.

Sto cercando una soluzione locale di processo, cioè che non implichi la configurazione di cose come AppArmor o SELinux per l'intero sistema, né i privilegi di root. Tuttavia, può comportare l'installazione dei loro moduli del kernel.

Stavo esaminando le funzionalità e queste sarebbero state belle e facili, se ci fosse stata la possibilità di creare file. ulimit è un altro approccio che sarebbe conveniente, se riguardasse questo caso d'uso.


Troppi programmi presumono che siano in grado di scrivere file come una cosa ovvia (e falliscono in modi strani quando non possono). straceti dice quali file sta aprendo il programma. Perchè vuoi fare questo? È un programma specifico o lo vuoi per testare o qualcos'altro? Puoi eseguire il programma come un utente / gruppo che non ha il permesso di scrivere quasi ovunque tranne nella directory corrente? Le moderne distribuzioni Linux usano l'idea di un gruppo per ciascun utente, quindi questo dovrebbe essere relativamente facile da configurare.
vonbrand,

È un programma speciale (Isabelle) che interpreta il codice in un modo un po 'sicuro già (nessuna esecuzione di codice arbitrario), ma consente comunque al codice di creare file in luoghi arbitrari. Poiché il codice non è attendibile, desidero impedire che ciò accada (interrompendo il programma). Il programma funziona già come un utente speciale, ma mi sentirei più sicuro se il codice non riuscisse a intasare, diciamo, / tmp o posti simili.
Joachim Breitner,

Puoi aggiungere un nuovo utente, per eseguire l'app.
ctrl-alt-delor

Risposte:


9

Che ne dici di creare un chroot vuoto, quindi collegare il filesystem principale come sola lettura all'interno del chroot?

Probabilmente dovrebbe essere qualcosa del genere per creare un bind-mount di sola lettura:

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

È possibile associare anche altre directory alle quali si desidera che la jail abbia accesso in scrittura. Fare attenzione se è necessario eseguire il binding di directory speciali (/ dev /, / proc /, / sys /), il loro montaggio così com'è potrebbe non essere sicuro.


Ancora una volta, necessita dei privilegi di root e di altre "impostazioni globali". Ma un'opzione, sì.
Joachim Breitner,

Il /foo/percorso è al file system principale?
Wayne Conrad,

5

Sembra che lo strumento giusto per questo lavoro sia fseccompbasato sul sync-ignoringcodice f di Bastian Blank, ho trovato questo file relativamente piccolo che impedisce a tutti i suoi figli di aprire un file per la scrittura:

/*
 * Copyright (C) 2013 Joachim Breitner <mail@joachim-breitner.de>
 *
 * Based on code Copyright (C) 2013 Bastian Blank <waldi@debian.org>
 *
 * 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;
}

Qui puoi vedere che è ancora possibile leggere i file:

[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
/*

Non impedisce l'eliminazione di file, il loro spostamento o altre operazioni sui file oltre all'apertura, ma potrebbero essere aggiunte.

Uno strumento che consente questo senza dover scrivere il codice C è syscall_limiter .


4
Si noti che l'approccio sicuro consiste nella whitelist di syscalls, non nella blacklist. Se viene negato troppo, è possibile utilizzare helper esterni non in scatola per assistere il programma. Con LD_PRELOAD tali helper possono essere resi trasparenti al programma che stiamo eseguendo.
Vi.

4

Considereresti di scrivere un sostituto per open(…)funzionare e caricarlo usando LD_PRELOAD?


2
Probabilmente intendi open... Beh, prenderei in considerazione l'utilizzo di una soluzione esistente che utilizza questo approccio, sì.
Joachim Breitner,

2
C'è qualcosa del genere su github.com/certik/restrict , ma è configurato tramite la compilazione e non sembra essere molto diffuso.
Joachim Breitner,

Sì, scusa, errore mio, aggiornare la risposta ... Ma mi sembra che dovrai sostituirne anche una write(…).
Leonid,

Per quanto riguarda github.com/certik/restrict , sì, hai perfettamente ragione.
Leonid,

3

La soluzione più semplice è probabilmente un programma wrapper che crea un nuovo spazio dei nomi di filesystem con i relativi filesystem montati in sola lettura e quindi esegue il programma che si sta tentando di limitare.

Questo è ciò che systemdaccade quando si utilizza ReadOnlyDirectories=per contrassegnare determinate directory come di sola lettura per un servizio. C'è anche un unsharecomando util-linuxche può fare il lavoro di creazione di un nuovo spazio dei nomi, quindi puoi fare qualcosa del tipo:

unshare -m <wrapper>

dove wrappersarebbe quindi sufficiente rimontare i filesystem come richiesto prima di avviare il vero programma di destinazione.

L'unico problema è che devi rootcreare il nuovo spazio dei nomi ...


Ci ho pensato. Ma è possibile senza essere root? Esiste uno script / programma già pronto per quello disponibile?
Joachim Breitner,

1
Sì, sembra che tu abbia bisogno di essere root, almeno con il kernel 3.7.
Tom

Stavo esaminando ulteriormente questa soluzione. È possibile ricorrere in modo ricorsivo a mount / a un nuovo /, ma non ricorsivamente e contrassegnarlo come di sola lettura.
Joachim Breitner,

2

Potresti eseguirlo in un chroot, montando versioni speciali /tmp e simili all'interno. Forse systemd è di aiuto, e in particolare systemd-nspawn (1) , che sembra proprio quello che vuoi.


2

Una macchina virtuale consentirebbe allo script di scrivere ovunque senza influire sul sistema host e di ispezionare dove sta effettivamente cercando di scrivere, che sembrano essere gli obiettivi.

Ad esempio, è possibile avviare facilmente Arch Linux con

kvm -boot d -m 512 -cdrom archlinux-*.iso

1
Voglio ancora eseguire il programma nella macchina corrente per evitare di dover impostare un nuovo sistema, un nuovo ambiente, ecc. Una macchina virtuale è troppo pesante per il mio caso d'uso.
Joachim Breitner,

2

Fare una configurazione iniziale come root è davvero il modo più semplice. In particolare, un chroot in un attacco di rilegatura di sola lettura è il percorso di minor resistenza.

È possibile utilizzare bindfs invece di mount --bindcreare la vista di sola lettura senza che sia necessario essere root. Tuttavia, devi fare qualcosa come root per impedire l'accesso ad altri file, come chroot.

Un altro approccio è quello di LD_PRELOADuna libreria che si aggancia all'apertura del file e rifiuta di consentire la scrittura. Ciò non richiede privilegi speciali. Dal punto di vista della sicurezza, questo può essere ignorato, ma va bene per il tuo caso d'uso in cui devi solo contenere una funzione specifica e non un codice nativo arbitrario. Non conosco una biblioteca esistente per questo, comunque. LD_PRELOADpotrebbe anche essere usato per limitare il programma alla vista di sola lettura creata con mount --bindo bindfs; di nuovo, non conosco una biblioteca esistente.

Su Debian e derivati, puoi impostare un ambiente schroot . Schroot è setuid root e deve essere configurato come root, ma può essere eseguito da qualsiasi utente autorizzato.

Un metodo che non richiede alcuna collaborazione da parte di root consiste nell'eseguire il processo in una macchina virtuale. È possibile configurare KVM o VirtualBox o Linux in modalità utente . È un po 'pesante e significherà un consumo extra di memoria, ma non dovrebbe influire in modo significativo sulla velocità del calcolo simbolico grezzo.

Come "imprigionare" un processo senza essere root? potrebbe fornire qualche ispirazione.


1

Un modo per impedire almeno al processo di scrivere i file (ma non di crearli) è chiamare ulimit -f 0prima. Ciò interromperà il processo non appena tenta di scrivere su un file, ma è ancora possibile creare file vuoti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.