Jak zapobiec zapisywaniu plików przez proces


13

Chcę uruchomić polecenie w systemie Linux w taki sposób, że nie można utworzyć ani otworzyć żadnych plików do zapisu. Nadal powinien móc normalnie odczytywać pliki (więc pusty chroot nie jest opcją) i nadal móc zapisywać do plików już otwartych (szczególnie standardowe wyjście).

Punkty bonusowe, jeśli zapis plików do niektórych katalogów (tj. Bieżącego katalogu) jest nadal możliwy.

Szukam rozwiązania, które jest lokalne dla procesu, tj. Nie wymaga konfigurowania takich rzeczy jak AppArmor lub SELinux dla całego systemu, ani uprawnień roota. Może to jednak wymagać instalacji ich modułów jądra.

Patrzyłem na możliwości, które byłyby przyjemne i łatwe, gdyby istniała możliwość tworzenia plików. ulimit to inne podejście, które byłoby wygodne, gdyby obejmowało ten przypadek użycia.


Zbyt wiele programów zakłada, że ​​są w stanie zapisywać pliki jako coś oczywistego (i zawodzą w dziwny sposób, gdy nie mogą). straceinformuje, jakie pliki otwiera program. Dlaczego chcesz to zrobić? Czy to konkretny program, czy chcesz to do testowania, czy coś innego? Czy możesz uruchomić program jako użytkownik / grupa, która nie ma uprawnień do pisania prawie wszędzie, z wyjątkiem bieżącego katalogu? Nowoczesne dystrybucje Linuksa wykorzystują ideę grupy dla każdego użytkownika, dlatego konfiguracja powinna być stosunkowo łatwa.
vonbrand

Jest to specjalny program (Isabelle), który już interpretuje kod w nieco bezpieczny sposób (bez wykonywania dowolnego kodu), ale nadal pozwala na tworzenie plików w dowolnych miejscach. Ponieważ kod jest niezaufany, chcę temu zapobiec (przerywając program). Program działa już jako specjalny użytkownik, ale czułbym się bezpieczniejszy, gdyby kod nie mógł się zablokować, powiedzmy, / tmp lub podobnych miejsc.
Joachim Breitner

Możesz dodać nowego użytkownika, aby uruchomić aplikację.
ctrl-alt-delor

Odpowiedzi:


9

Co powiesz na utworzenie pustego chroota, a następnie podłączenie głównego systemu plików do wiązania jako chroot tylko do odczytu?

Powinno to być coś takiego do utworzenia montowania tylko do odczytu:

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

Możesz podłączyć także inne katalogi, w których więzienie ma mieć dostęp do zapisu. Zachowaj ostrożność, jeśli potrzebujesz zamontować specjalne katalogi (/ dev /, / proc /, / sys /), montowanie ich w stanie, w jakim jest, może być niepewne.


Ponownie potrzebuje uprawnień roota i innych „ustawień globalnych”. Ale opcja tak.
Joachim Breitner

Czy /foo/ścieżka do głównego systemu plików?
Wayne Conrad

5

Wydaje się, że właściwym narzędziem do tego zadania jest fseccompoparty na sync-ignoringkodzie f Bastian Blank, wymyśliłem ten stosunkowo mały plik, który powoduje, że wszystkie jego dzieci nie mogą otworzyć pliku do zapisu:

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

Tutaj możesz zobaczyć, że nadal można czytać pliki:

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

Nie zapobiega usuwaniu plików, ich przenoszeniu ani innym operacjom związanym z plikami oprócz otwierania, ale można je dodać.

Narzędziem, które umożliwia to bez konieczności pisania kodu C, jest syscall_limiter .


4
Pamiętaj, że bezpiecznym podejściem jest umieszczanie na białej liście połączeń systemowych, a nie umieszczanie ich na czarnej liście. Jeśli odmówiono zbyt wiele, do pomocy programu można użyć zewnętrznych pomocników nieskasowanych. Dzięki LD_PRELOAD pomocnicy mogą stać się transparentni dla uruchomionego programu.
Vi.

4

Czy rozważysz napisanie substytutu open(…)funkcji i załadowanie go za pomocą LD_PRELOAD?


2
Prawdopodobnie masz na myśli open... Cóż, rozważyłbym użycie istniejącego rozwiązania, które wykorzystuje to podejście, tak.
Joachim Breitner

2
Jest coś takiego na github.com/certik/restrict , ale jest konfigurowany przez kompilację i nie wydaje się być w powszechnym użyciu.
Joachim Breitner

Tak, przepraszam, mój błąd, aktualizacja odpowiedzi… Ale wydaje mi się, że będziesz musiał zastąpić jedną z nich write(…).
Leonid

Jeśli chodzi o github.com/certik/restrict , tak, masz całkowitą rację.
Leonid

3

Najprostszym rozwiązaniem jest prawdopodobnie program otoki, który tworzy nową przestrzeń nazw systemu plików z odpowiednimi systemami plików zamontowanymi tylko do odczytu, a następnie uruchamia program, który próbujesz ograniczyć.

Dzieje się tak systemd, gdy używasz ReadOnlyDirectories=do oznaczania niektórych katalogów jako usługi tylko do odczytu dla usługi. Istnieje również unsharepolecenie, util-linuxktóre może wykonać zadanie tworzenia nowej przestrzeni nazw, dzięki czemu możesz zrobić coś takiego:

unshare -m <wrapper>

gdzie wrappermusiałby po prostu ponownie zamontować systemy plików zgodnie z wymaganiami przed uruchomieniem prawdziwego programu docelowego.

Jedynym problemem jest to, że musisz rootutworzyć nową przestrzeń nazw ...


Myślałem o tym. Ale czy jest to możliwe bez rootowania? Czy jest dostępny gotowy skrypt / program do tego?
Joachim Breitner

1
Tak, wydaje się, że musisz być rootem, przynajmniej z jądrem 3.7.
TomH

Patrzyłem dalej na to rozwiązanie. Możliwe jest rekurencyjne powiązanie mount / z nowym /, ale nie i rekurencyjne oznaczenie go jako tylko do odczytu.
Joachim Breitner


2

Maszyna wirtualna umożliwiłaby skryptowi pisanie w dowolnym miejscu bez wpływu na system hosta oraz sprawdzanie, gdzie faktycznie próbuje pisać, co wydaje się być celem.

Na przykład możesz łatwo uruchomić Arch Linux

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

1
Nadal chcę uruchamiać program na bieżącym komputerze, aby uniknąć konieczności konfigurowania nowego systemu, nowego środowiska itp. Maszyna wirtualna jest zbyt ciężka dla mojego przypadku użycia.
Joachim Breitner

2

Wykonanie wstępnej konfiguracji jako root jest naprawdę najłatwiejszym sposobem. W szczególności chroot w mocowaniu do wiązania tylko do odczytu to ścieżka najmniejszego oporu.

Możesz użyć bindfs zamiast mount --bindtworzyć widok tylko do odczytu bez konieczności rootowania . Jednak musisz zrobić coś jako root, aby uniemożliwić dostęp do innych plików, takich jak chroot.

Innym podejściem jest LD_PRELOADbiblioteka, która zaczepia się o otwieranie plików i odmawia zapisu. Nie wymaga to żadnych specjalnych uprawnień. Z punktu widzenia bezpieczeństwa można to obejść, ale jest to przydatne w przypadku użycia, w którym wystarczy tylko zawierać konkretną funkcję, a nie dowolny kod macierzysty. Nie wiem jednak o istniejącej bibliotece. LD_PRELOADmoże być również użyty do ograniczenia programu do widoku tylko do odczytu utworzonego za pomocą mount --bindlub bindfs; znowu nie wiem o istniejącej bibliotece.

W Debianie i pochodnych możesz skonfigurować środowisko schroot . Schroot jest setuid root i musi być skonfigurowany jako root, ale może być wykonany przez dowolnego autoryzowanego użytkownika.

Metodą, która nie wymaga współpracy z rootem, jest uruchomienie procesu na maszynie wirtualnej. Możesz skonfigurować KVM, VirtualBox lub Linux w trybie użytkownika . Jest nieco ciężki i będzie oznaczał dodatkowe zużycie pamięci, ale nie powinien znacząco wpływać na szybkość obliczeń symbolicznych.

Jak „uwięzić” proces bez rootowania? może dostarczyć inspiracji.


1

Jednym ze sposobów, aby przynajmniej uniemożliwić procesowi zapisywanie plików (ale nie tworzenie ich), jest wywołanie w ulimit -f 0pierwszej kolejności. Spowoduje to przerwanie procesu, gdy tylko spróbuje on zapisać plik, ale tworzenie pustych plików jest nadal możliwe.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.