So verhindern Sie, dass ein Prozess Dateien schreibt


13

Ich möchte einen Befehl unter Linux so ausführen, dass keine zu schreibenden Dateien erstellt oder geöffnet werden können. Es sollte weiterhin in der Lage sein, Dateien wie gewohnt zu lesen (eine leere Chroot ist also keine Option) und weiterhin in bereits geöffnete Dateien schreiben zu können (insbesondere in stdout).

Bonuspunkte, wenn das Schreiben von Dateien in bestimmte Verzeichnisse (dh das aktuelle Verzeichnis) weiterhin möglich ist.

Ich suche nach einer Lösung, die prozesslokal ist, dh weder Dinge wie AppArmor oder SELinux für das gesamte System konfiguriert noch Root-Rechte. Möglicherweise müssen jedoch die Kernelmodule installiert werden.

Ich habe mir Funktionen angesehen, und diese wären nett und einfach gewesen, wenn es eine Funktion zum Erstellen von Dateien gegeben hätte. ulimit ist ein weiterer Ansatz, der praktisch wäre, wenn er diesen Anwendungsfall abdecken würde.


Zu viele Programme gehen davon aus, dass sie selbstverständlich Dateien schreiben können (und auf seltsame Weise versagen, wenn sie dies nicht können). straceHier erfahren Sie, welche Dateien das Programm öffnet. Warum willst du das tun? Ist es ein bestimmtes Programm oder möchten Sie dies zum Testen oder etwas anderes? Können Sie das Programm als Benutzer / Gruppe ausführen, die fast überall nur im aktuellen Verzeichnis schreiben darf? Moderne Linux-Distributionen verwenden die Idee einer Gruppe für jeden Benutzer, daher sollte dies relativ einfach einzurichten sein.
vonbrand

Es ist ein spezielles Programm (Isabelle), das Code bereits auf eine etwas sichere Weise interpretiert (keine willkürliche Codeausführung), dem Code jedoch weiterhin ermöglicht, Dateien an beliebigen Stellen zu erstellen. Da der Code nicht vertrauenswürdig ist, möchte ich dies verhindern (indem ich das Programm abbreche). Das Programm wird bereits als spezieller Benutzer ausgeführt, aber ich würde mich sicherer fühlen, wenn der Code beispielsweise / tmp oder ähnliche Stellen nicht überladen könnte.
Joachim Breitner

Sie können einen neuen Benutzer hinzufügen, um die App
auszuführen

Antworten:


9

Wie wäre es, wenn Sie eine leere Chroot erstellen und dann das Hauptdateisystem als schreibgeschützt in die Chroot einbinden?

Sollte wahrscheinlich so etwas sein, um ein schreibgeschütztes Bind-Mount zu erstellen:

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

Sie können andere Verzeichnisse binden, auf die das Gefängnis auch Schreibzugriff haben soll. Seien Sie vorsichtig, wenn Sie spezielle Verzeichnisse (/ dev /, / proc /, / sys /) binden müssen. Das Mounten kann unverändert sein.


Benötigt wieder Root-Rechte und andere "globale Einrichtung". Aber eine Option, ja.
Joachim Breitner

Ist /foo/der Pfad zum Hauptdateisystem?
Wayne Conrad

5

Es scheint, dass das richtige Werkzeug für diesen Job fseccompauf sync-ignoringf-Code von Bastian Blank basiert. Ich habe diese relativ kleine Datei entwickelt, die dazu führt, dass alle untergeordneten Dateien keine Datei zum Schreiben öffnen können:

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

Hier sehen Sie, dass es immer noch möglich ist, Dateien zu lesen:

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

Es verhindert nicht das Löschen oder Verschieben von Dateien oder andere Dateivorgänge neben dem Öffnen, aber das könnte hinzugefügt werden.

Ein Tool, das dies ermöglicht, ohne C-Code schreiben zu müssen, ist syscall_limiter .


4
Beachten Sie, dass ein sicherer Ansatz darin besteht, Syscalls auf die Whitelist zu setzen und nicht auf die schwarze Liste zu setzen. Wenn zu viel verweigert wird, können externe Helfer ohne Box zur Unterstützung des Programms verwendet werden. Mit LD_PRELOAD können solche Helfer für das von uns ausgeführte Programm transparent gemacht werden.
Vi.

4

Würden Sie in Betracht ziehen, einen Ersatz für die open(…)Funktion zu schreiben und ihn mit LD_PRELOAD zu laden?


2
Sie meinen wahrscheinlich open... Nun, ich würde in Betracht ziehen, eine vorhandene Lösung zu verwenden, die diesen Ansatz verwendet, ja.
Joachim Breitner

2
Es gibt so etwas unter github.com/certik/restrict , aber es wird durch Kompilieren konfiguriert und scheint nicht weit verbreitet zu sein.
Joachim Breitner

Ja, tut mir leid, mein Fehler, die Antwort zu aktualisieren ... Aber es scheint mir, dass Sie auch eine durch die ersetzen müssen write(…).
Leonid

Was github.com/certik/restrict betrifft , ja, Sie haben völlig Recht.
Leonid

3

Die einfachste Lösung ist wahrscheinlich ein Wrapper-Programm, das einen neuen Dateisystem-Namespace mit den entsprechenden Dateisystemen erstellt, die schreibgeschützt bereitgestellt sind, und dann das Programm ausführt, das Sie einschränken möchten.

Dies ist der systemdFall, wenn Sie ReadOnlyDirectories=bestimmte Verzeichnisse als schreibgeschützt für einen Dienst markieren. Es gibt auch einen unshareBefehl util-linux, mit dem Sie einen neuen Namespace erstellen können. Sie können also Folgendes tun:

unshare -m <wrapper>

Wo wrappermüsste dann nur Dateisysteme nach Bedarf neu gemountet werden, bevor das eigentliche Zielprogramm gestartet wird.

Das einzige Problem ist, dass Sie sein müssen root, um den neuen Namespace zu erstellen ...


Ich habe darüber nachgedacht. Aber ist das möglich, ohne Wurzel zu sein? Gibt es dafür ein fertiges Skript / Programm?
Joachim Breitner

1
Ja, es scheint, dass Sie root sein müssen, zumindest mit dem 3.7-Kernel.
TomH

Ich habe mich weiter mit dieser Lösung befasst. Es ist möglich, rekursiv / an ein neues / zu binden, aber nicht und rekursiv als schreibgeschützt zu markieren.
Joachim Breitner

2

Sie könnten es in einer Chroot laufen lassen /tmp und spezielle Versionen von und dergleichen darin montieren. Vielleicht hilft systemd , insbesondere systemd-nspawn (1) , das genau so aussieht, wie Sie es möchten.


2

Eine virtuelle Maschine würde es dem Skript ermöglichen, überall zu schreiben, ohne das Hostsystem zu beeinträchtigen, und zu überprüfen, wohin es tatsächlich zu schreiben versucht, was die Ziele zu sein scheinen.

Sie können Arch Linux beispielsweise problemlos mit starten

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

1
Ich möchte das Programm weiterhin auf der aktuellen Maschine ausführen, um zu vermeiden, dass ein neues System, eine neue Umgebung usw. eingerichtet werden muss. Eine virtuelle Maschine ist für meinen Anwendungsfall viel zu schwer.
Joachim Breitner

2

Eine anfängliche Einrichtung als Root ist wirklich der einfachste Weg. Insbesondere ist ein Chroot in einem schreibgeschützten Bind-Mount der Weg des geringsten Widerstands.

Sie können bindfs verwenden, anstatt mount --binddie schreibgeschützte Ansicht zu erstellen, ohne root sein zu müssen. Sie müssen jedoch etwas als root tun, um den Zugriff auf andere Dateien wie chroot zu verhindern.

Ein anderer Ansatz ist LD_PRELOADeine Bibliothek, die sich in das Öffnen von Dateien einhakt und das Schreiben verweigert. Dies erfordert keine besonderen Berechtigungen. Aus Sicherheitsgründen kann dies umgangen werden, aber es ist in Ordnung für Ihren Anwendungsfall, in dem Sie nur eine bestimmte Funktion und keinen beliebigen nativen Code enthalten müssen. Ich kenne jedoch keine vorhandene Bibliothek dafür. LD_PRELOADkann auch verwendet werden, um das Programm auf die schreibgeschützte Ansicht zu beschränken, die mit mount --bindoder erstellt wurde bindfs; Auch hier kenne ich keine vorhandene Bibliothek.

Unter Debian und Derivaten können Sie eine Schroot- Umgebung einrichten . Schroot ist setuid root und muss als root konfiguriert werden, kann aber von jedem autorisierten Benutzer ausgeführt werden.

Eine Methode, die keine Zusammenarbeit von root erfordert, besteht darin, den Prozess in einer virtuellen Maschine auszuführen. Sie können KVM oder VirtualBox oder Linux im Benutzermodus einrichten . Es ist ein bisschen schwergewichtig und bedeutet zusätzlichen Speicherverbrauch, sollte aber die Geschwindigkeit der symbolischen Rohberechnung nicht wesentlich beeinflussen.

Wie kann man einen Prozess "einsperren", ohne root zu sein? könnte Inspiration geben.


1

Eine Möglichkeit, um zumindest zu verhindern, dass der Prozess die Dateien schreibt (aber nicht erstellt), besteht darin, ulimit -f 0zuerst aufzurufen . Dadurch wird der Vorgang abgebrochen, sobald versucht wird, in eine Datei zu schreiben. Das Erstellen leerer Dateien ist jedoch weiterhin möglich.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.