如何防止进程写入文件


13

我想以无法创建或打开任何要写入的文件的方式在Linux上运行命令。它仍然应该能够正常读取文件(因此不能选择空的chroot),并且仍然能够写入已经打开的文件(尤其是stdout)。

如果仍然可以将文件写入某些目录(即当前目录),则可以获得加分。

我正在寻找一种本地解决方案,即不涉及为整个系统配置AppArmor或SELinux之类的东西,也不涉及root特权。不过,这可能涉及安装其内核模块。

我正在研究功能,如果有创建文件的功能,这些功能将很容易实现。如果ulimit涵盖了此用例,则它是另一种方便的方法。


太多的程序假定它们能够写文件是理所当然的事情(如果不能,它们就会以奇怪的方式失败)。strace告诉您程序正在打开哪些文件。为什么要这样做?它是一个特定的程序,还是您想要对此进行测试或其他?您是否可以以无权在除当前目录之外几乎所有地方进行写操作的用户/组身份运行该程序?现代Linux发行版为每个用户使用组的概念,因此应该相对容易地进行设置。
vonbrand

这是一个特殊的程序(Isabelle),它已经以某种安全的方式解释了代码(没有执行任意代码),但是仍然允许代码在任意位置创建文件。由于代码不受信任,因此我想防止这种情况的发生(通过中止程序)。该程序已经以特殊用户身份运行,但是如果代码无法破坏/ tmp或类似的地方,我会感到更加安全。
Joachim Breitner

你可以添加一个新用户,运行的应用程序。
CTRL-ALT-delor

Answers:


9

如何创建一个空的chroot,然后将主文件系统以只读方式绑定安装在chroot中?

创建只读绑定安装可能应该是这样的:

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

您可以绑定挂载您希望监狱也具有写权限的其他目录。如果需要绑定安装特殊目录(/ dev /,/ proc /,/ sys /),请小心,按原样安装可能不安全。


同样,需要root特权和其他“全局设置”。但是,是一种选择。
Joachim Breitner

/foo/主文件系统的路径吗?
韦恩·康拉德

5

看来适合此工作的正确工具是fseccomp基于sync-ignoringBastian Blank的f代码,我想到了一个相对较小的文件,该文件导致其所有子级无法打开文件进行写入:

/*
 * 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...嗯,我会考虑使用使用这种方法的现有解决方案,是的。
Joachim Breitner

2
github.com/certik/restrict上有类似的东西,但是它是通过编译配置的,似乎没有被广泛使用。
Joachim Breitner

是的,很抱歉,我的错误,正在更新答案……但是在我看来,您也必须用一个代替write(…)
Leonid

至于github.com/certik/restrict,是的,你是完全正确的。
Leonid

3

最简单的解决方案可能是包装程序,该程序创建一个新文件系统名称空间,并以只读方式挂载相关文件系统,然后执行您尝试限制的程序。

这是systemd当您ReadOnlyDirectories=用来将某些目录标记为服务的只读时的行为。还有一个unshare命令util-linux可以完成创建新名称空间的工作,因此您可以执行以下操作:

unshare -m <wrapper>

在那里wrapper,然后就只需要重新挂载文件系统开始真正的目标程序之前,如有必要。

唯一的问题是您需要root创建新的名称空间...


我想到了 但是,如果没有根,这可能吗?是否有现成的脚本/程序可用?
Joachim Breitner

1
是的,看来您确实需要成为root用户,至少要使用3.7内核。
TomH

我正在进一步研究此解决方案。可以递归地将mount /绑定到新的/,但是不能并将其递归地标记为只读。
Joachim Breitner


2

虚拟机将使脚本可以在不影响主机系统的情况下在任何地方编写,并检查其实际尝试写入的位置,这似乎是目标。

例如,您可以使用以下命令轻松启动Arch Linux

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

1
我仍然希望在当前计算机上运行该程序,以避免必须设置新系统,新环境等。对于我的用例而言,虚拟机的重量太大。
Joachim Breitner

2

以root身份进行一些初始设置实际上是最简单的方法。特别是,将chroot放入只读绑定安装是阻力最小的路径。

您可以使用bindfs代替mount --bind创建只读视图而无需成为root用户。但是,您确实需要以root用户身份执行操作,以防止访问其他文件,例如chroot。

另一种方法是使用LD_PRELOAD一种可以打开文件并拒绝写入的库。这不需要特殊的特权。从安全的角度来看,可以绕开它,但是对于您只需要包含特定功能而不需要包含任意本机代码的用例来说,这是可以的。但是,我不知道这个已有的库。LD_PRELOAD也可以用于将程序限制为使用mount --bind或创建的只读视图bindfs。再次,我不知道现有的图书馆。

在Debian和衍生产品上,您可以设置schroot环境。Schroot是setuid root,需要配置为root,但是可以由任何授权用户执行。

一种不需要根本合作的方法是在虚拟机中运行进程。您可以设置KVM或VirtualBox或用户模式Linux。它有点重量级,将意味着额外的内存消耗,但不应显着影响原始符号计算的速度。

如何在没有root的情况下“监禁”进程?可能会提供一些启发。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.