Как предотвратить процесс от записи файлов

13

Я хочу выполнить команду в Linux так, чтобы она не могла создавать или открывать какие-либо файлы для записи. Он по-прежнему должен иметь возможность читать файлы в обычном режиме (таким образом, пустой chroot-файл не является опцией) и при этом иметь возможность записи в уже открытые файлы (особенно в stdout).

Бонусные баллы, если запись файлов в определенные каталоги (т.е. в текущий каталог) все еще возможна.

Я ищу решение, которое является локальным для процесса, то есть не включает настройку таких вещей, как AppArmor или SELinux для всей системы, а также привилегий root. Это может включать установку их модулей ядра.

Я смотрел на возможности, и они были бы хороши и просты, если бы была возможность создавать файлы. ulimit - другой подход, который был бы удобен, если бы он охватывал этот вариант использования.

Иоахим Брейтнер
источник
Слишком много программ предполагают, что они могут записывать файлы как само собой разумеющееся (и терпят неудачу странным образом, когда не могут). straceговорит, какие файлы открывает программа. почему ты хочешь сделать это? Это конкретная программа, или вы хотите это для тестирования или что-то еще? Можете ли вы запустить программу как пользователь / группа, у которой нет прав на запись почти везде, кроме текущей директории? Современные дистрибутивы Linux используют идею группы для каждого пользователя, поэтому это должно быть относительно легко настроить.
vonbrand
Это специальная программа (Isabelle), которая уже интерпретирует код в некоторой степени безопасным способом (без выполнения произвольного кода), но все же позволяет коду создавать файлы в произвольных местах. Поскольку код не заслуживает доверия, я хочу предотвратить это (путем прерывания программы). Программа уже запускается как специальный пользователь, но я чувствовал бы себя безопаснее, если бы код не мог забить, скажем, / tmp или подобные места.
Иоахим Брейтнер
Вы можете добавить нового пользователя, чтобы запустить приложение.
Ctrl-Alt-Delor

Ответы:

9

Как насчет создания пустого chroot, а затем связать и смонтировать основную файловую систему как доступную только для чтения внутри chroot?

Вероятно, должно быть что-то вроде этого для создания привязки только для чтения:

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

Вы можете связать и смонтировать другие каталоги, к которым у джейла также будет доступ на запись. Будьте осторожны, если вам нужно привязать-монтировать специальные каталоги (/ dev /, / proc /, / sys /), монтировать их как есть может быть небезопасно.

Ли Райан
источник
Опять же, нужны права суперпользователя и другие «глобальные настройки». Но вариант да.
Иоахим Брейтнер
Это /foo/путь к основной файловой системе?
Уэйн Конрад
5

Кажется, что правильный инструмент для этой работы fseccompоснован на sync-ignoringf-коде Бастиана Бланка, я придумал этот относительно небольшой файл, который не позволяет всем его дочерним элементам открыть файл для записи:

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

Здесь вы можете видеть, что все еще можно читать файлы:

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

Это не препятствует удалению файлов, их перемещению или другим файловым операциям, кроме открытия, но это можно добавить.

Инструментом, который позволяет это без необходимости писать код на C, является syscall_limiter .

Иоахим Брейтнер
источник
4
Обратите внимание, что безопасный подход заключается в внесении системных вызовов в белый список, а не в черный список. Если слишком много отрицают, внешние помощники могут быть использованы для помощи программе. С помощью LD_PRELOAD такие помощники могут быть прозрачными для программы, которую мы запускаем.
Ви.
4

Не могли бы вы написать замену open(…)функции и загрузить ее с помощью LD_PRELOAD?

Леонид
источник
2
Вы, вероятно, имеете в виду open... Ну, я бы рассмотрел использование существующего решения, которое использует этот подход, да.
Иоахим Брейтнер
2
Есть что-то вроде этого на github.com/certik/restrict , но оно настраивается путем компиляции и, похоже, не используется широко.
Иоахим Брейтнер
Да, извините, моя ошибка, обновление ответа ... Но мне кажется, вам также придется заменить его на write(…)другое.
Леонид
Что касается github.com/certik/restrict , да, вы совершенно правы.
Леонид
3

Самым простым решением, вероятно, является программа-обертка, которая создает новое пространство имен файловой системы с соответствующими файловыми системами, смонтированными только для чтения, а затем исполняет программу, которую вы пытаетесь ограничить.

Это то, что systemdпроисходит, когда вы используете ReadOnlyDirectories=для пометки определенные каталоги только для чтения для службы. Существует также unshareкоманда, util-linuxкоторая может выполнить работу по созданию нового пространства имен, так что вы можете сделать что-то вроде:

unshare -m <wrapper>

где wrapperтогда просто нужно перемонтировать файловые системы, как требуется, перед запуском реальной целевой программы.

Единственная проблема заключается в том, что вам нужно быть, rootчтобы создать новое пространство имен ...

TomH
источник
Я думал об этом. Но возможно ли это без прав root? Есть ли готовый скрипт / программа для этого?
Иоахим Брейтнер
1
Да, похоже, вам нужен root, по крайней мере с ядром 3.7.
TomH
Я смотрел дальше на это решение. Можно рекурсивно связывать / монтировать / с новым /, но нет и рекурсивно помечать его как доступное только для чтения.
Иоахим Брейтнер
2

Вы можете запустить его в chroot, монтируя специальные версии /tmp и тому подобное внутри. Возможно, systemd поможет, и особенно systemd-nspawn (1) , который выглядит так, как вы хотите.

vonbrand
источник
2

Виртуальная машина позволила бы сценарию писать в любом месте, не влияя на хост-систему, и проверять, куда она на самом деле пытается писать, что, кажется, является целью.

Например, вы можете легко запустить Arch Linux с

kvm -boot d -m 512 -cdrom archlinux-*.iso
l0b0
источник
1
Я все еще хочу запустить программу на текущей машине, чтобы избежать необходимости устанавливать новую систему, новую среду и т. Д. Виртуальная машина слишком тяжелая для моего варианта использования.
Иоахим Брейтнер
2

Выполнение первоначальной настройки от имени пользователя root - действительно самый простой способ. В частности, chroot в монтируемом монтировании только для чтения - это путь наименьшего сопротивления.

Вы можете использовать bindfs вместо того, mount --bindчтобы создавать представление только для чтения без необходимости быть пользователем root. Тем не менее, вам нужно сделать что-то как root, чтобы предотвратить доступ к другим файлам, например, chroot.

Другой подход заключается в том, LD_PRELOADчто библиотека подключается к открытию файла и не позволяет писать. Это не требует особых привилегий. С точки зрения безопасности это можно обойти, но это нормально для вашего случая использования, когда вам нужно только содержать определенную функцию, а не произвольный собственный код. Однако я не знаю существующей библиотеки для этого. LD_PRELOADможет также использоваться, чтобы ограничить программу представлением только для чтения, созданным с помощью mount --bindили bindfs; опять же, я не знаю о существующей библиотеке.

В Debian и его производных вы можете настроить среду schroot . Schroot является setuid root и должен быть настроен как root, но может быть выполнен любым авторизованным пользователем.

Метод, который не требует какого-либо взаимодействия со стороны root - это запустить процесс на виртуальной машине. Вы можете настроить KVM или VirtualBox, или пользовательский режим Linux . Он немного тяжелый и будет означать дополнительное потребление памяти, но не должно существенно влиять на скорость необработанных символьных вычислений.

Как "заточить" процесс, не будучи root? может дать некоторое вдохновение.

Жиль "ТАК - прекрати быть злым"
источник
1

Один из способов по крайней мере предотвратить процесс от записи файлов (но не от их создания) - ulimit -f 0сначала вызвать . Это прервет процесс, как только он попытается записать файл, но создание пустых файлов все еще возможно.

Иоахим Брейтнер
источник