在Linux中运行一个不受信任的C程序,并将其置于沙箱中,以防止它打开文件、分叉等,该怎么做?

117

我想知道是否存在在Linux下以沙盒方式运行不受信任的C程序的方法。这样可以防止程序打开文件或网络连接,或进行fork、exec等操作吗?

这将是一个小程序,是一个家庭作业任务,上传到服务器并执行单元测试。因此,程序的生命周期很短。


这是像一个单独的C程序,您需要运行1次5分钟,还是需要持续运行的东西? - bwawok
这将是一个小程序,上传后会执行单元测试。因此,该程序的生命周期很短。 - Frank
这个系统使用什么发行版?一些发行版有预先制作的沙箱工具。你的系统是否启用了类似SELinux或AppArmor的安全模型? - thkala
我正在使用Fedora 13。我正在研究SELinux沙盒策略。我想知道还有哪些选项存在。 - Frank
如果您的系统已经启用了安全模型(由于它是 Fedora,可能会是 SELinux),建议使用该模型。我没有尝试过 Fedora 沙盒策略,但我认为它做得非常好。 - thkala
6
关于Linux或Unix中的沙盒(sandboxing)/监禁(jailing)进程的类似问题:
  • http://unix.stackexchange.com/q/6433/4319
  • https://dev59.com/HW865IYBdhLWcg3wWtOz
  • https://dev59.com/tVLTa4cB1Zd3GeqPbpAO
  • https://dev59.com/OnNA5IYBdhLWcg3wS70m
- imz -- Ivan Zakharyaschev
9个回答

48

我曾使用Systrace在交互和自动模式下对不受信任的程序进行沙箱隔离。它基于ptrace()的后端,可以在Linux系统上不需要特殊权限进行使用,同时还有一个更快、更强大的后端,需要打补丁修改内核。

在类Unix系统上,也可以使用chroot(1)创建沙箱,但这并不够简单或安全。Linux ContainersFreeBSD jails是比chroot更好的选择。在Linux上,另一种选择是使用像SELinuxAppArmor这样的安全框架,对于生产系统,我会推荐使用它们。

如果您告诉我们您想做什么,我们就能提供更多帮助。

编辑:

Systrace适用于您的情况,但我认为基于Linux安全模型的东西,如AppArmor或SELinux是更标准、因此更受欢迎的选择,取决于您的发行版。

编辑2:

虽然chroot(1)在大多数Unix类系统上都可用,但它有很多问题:

  • 可以被打破。如果您要在系统上实际编译或运行不受信任的C程序,您尤其容易受到这个问题的影响。而且,如果你的学生和我的一样,有人肯定会试图打破监狱。

  • 您需要创建一个完整的独立文件系统层次结构,包括执行编译后程序所需的一切。在chroot中不必安装编译器,但应该包含运行编译后程序所需的所有内容。虽然有一些实用工具可以帮助完成这个过程,但仍然不是一件轻松的事情。

  • 您需要维护chroot。由于它是独立的,chroot文件不会随着您的发行版更新而更新。您需要定期重新创建chroot,或者将必要的更新工具包含在其中,这基本上要求它成为一个完整的Linux发行版。您还需要与主机系统同步系统和用户数据(密码、输入文件等)。

  • chroot() 只能保护文件系统,无法防止恶意程序打开网络套接字或糟糕编写的程序耗尽所有可用资源。

资源使用问题是所有替代方案共同面临的问题。 文件系统配额 可以防止程序填满磁盘。适当的ulimit(C语言中的setrlimit())设置可以防止内存过度使用和任何fork炸弹,以及停止CPU占用。 nice(1) 可以降低这些程序的优先级,使计算机可以用于被认为更重要的任何任务。


systrace 对于简单的程序对我很有用,但是当 GCC 运行 GNU as(1) 时,它会无限期地卡住。所以我放弃了它。这是 systrace 中一个未修复的 bug:http://forum.soft32.com/linux/strace-wait4-pending-SIGALRM-ftopict484715.html - pts
有没有办法确保共享内存、消息队列和信号量不在沙盒进程之间共享? - daveagp
1
systrace链接已损坏。 - Talia
2
那么Firejail呢?你不再需要使用fs来维护它。 - m3nda

18

我最近写了一篇关于Linux沙盒技术综述的文章。如果你不介意分叉等问题,那么使用Linux容器(lxc)可能是最简单的方法,这在该环境下并不重要。您可以为进程提供只读根文件系统、独立的回环网络连接,而且仍然可以轻松地杀死它并设置内存限制等。

Seccomp会有点困难,因为代码甚至无法分配内存。

Selinux是另一个选择,但我认为它可能比容器更费力。


8

这个答案非常棒,它真的值得更多的赞,因为firejail正在积极维护,并且有着出色的文档,涵盖了大部分甚至所有其他答案,并且被设计成相对容易使用。 - Jeff Hykin
firejail 会影响程序的 CPU 运行时间吗? - alper

7

您可以使用Qemu快速测试作业。以下步骤在我的五年旧笔记本电脑上只需不到5秒钟。

假设学生需要开发一个程序,该程序接受无符号整数,每个整数占一行,直到出现带有“-1”的行。然后,程序应计算所有整数的平均值并输出“平均值:%f”。以下是如何完全隔离测试程序的方法:

  1. First, get root.bin from Jslinux, we'll use that as the userland (it has the tcc C-compiler):

    wget https://github.com/levskaya/jslinux-deobfuscated/raw/master/root.bin

  2. We want to put the student's submission in root.bin, so set up the loop device:

    sudo losetup /dev/loop0 root.bin

    (you could use fuseext2 for this too, but it's not very stable. If it stabilizes, you won't need root for any of this)

  3. Make an empty directory:

    mkdir mountpoint

  4. Mount root.bin:

    sudo mount /dev/loop0 mountpoint

  5. Enter the mounted filesystem:

    cd mountpoint.

  6. Fix rights:

    sudo chown -R `whoami` .

  7. mkdir -p etc/init.d
  8. vi etc/init.d:

    #!/bin/sh
    cd /root
    echo READY 2>&1 > /dev/ttyS0
    tcc assignment.c 2>&1 > /dev/ttyS0
    ./a.out 2>&1 > /dev/ttyS0
    
  9. chmod +x etc/init.d/rcS

  10. Copy the submission to the VM:

    cp ~/student_assignment.c root/assignment.c

  11. Exit the VM's root FS:

    cd ..

  12. sudo umount mountpoint
  13. Now the image is ready, we just need to run it. It will compile and run the submission after booting.
  14. mkfifo /tmp/guest_output
  15. Open a seperate terminal and start listening for guest output:

    dd if=/tmp/guest_output bs=1

  16. In another terminal:

    qemu-system-i386 -kernel vmlinuz-3.5.0-27-generic -initrd root.bin -monitor stdio -nographic -serial pipe:/tmp/guestoutput (I just used the Ubuntu kernel here, but many kernels will work)

  17. When the guest output shows "READY", you can send keys to the VM from the qemu prompt. For example, to test this assignment, you could do

    (qemu) sendkey 1
    (qemu) sendkey 4
    (qemu) sendkey ret
    (qemu) sendkey 1
    (qemu) sendkey 0
    (qemu) sendkey ret
    (qemu) sendkey minus
    (qemu) sendkey 1
    (qemu) sendkey ret
    
  18. Now Average = 12.000000 should appear on the guest output pipe. If it doesn't, the student failed.

  19. Quit qemu: quit
这里有一个通过测试的程序:https://stackoverflow.com/a/14424295/309483。只需使用tcclib.h代替stdio.h即可。

5
尝试使用User-mode Linux。它对于CPU密集型任务有约1%的性能开销,但对于I/O密集型任务可能会慢6倍。

3
在虚拟机中运行应用程序可以提供您所需的所有安全性和限制。 QEMU 是一个很好的选择,所有工作(下载应用程序、更新磁盘映像、启动 QEMU、在其中运行应用程序并保存输出以便稍后检索)都可以编写脚本进行自动化测试运行。

2
我不知道 OP 的情况,但是针对每个测试程序启动一个虚拟机在许多情况下是不能接受的。在我的环境中(我是 TA),在 2 小时内可能会有多达 200 名学生提交每人 10-12 个程序。没有任何一个程序的 CPU 时间超过 10 秒,但当提交堆积时,我们得到的周转时间为 15 分钟或更长时间。为每个程序引入一个虚拟机将使 CPU 时间每个程序增加到 60 秒或更长,而我根本不想考虑周转时间。也许每个会话一个虚拟机,但绝对不能针对每个程序这样做... - thkala
@thkala 这是一个很好的观点。我喜欢QEMU的想法,但为每个提交启动一个虚拟机并不好。 - Frank
那么,在这种情况下,保持同一虚拟机始终运行。 - Laurent Parenteau
你能否使用一个已经启动并准备好编译和运行代码的虚拟机快照来完成某些操作?顺便提一下,虚拟机并不一定免疫攻击。你也可以构建一个硬件版本 - 一个小型系统,它从只读媒体或网络上引导恢复映像,并通过网络或串口提供输出,然后重新启动进行下一步操作。有一些快速引导技术可以在几秒钟内启动Linux。 - Chris Stratton
这意味着如果您按顺序运行它们,每个提交所需的时间不到3秒。我发布的方法可能在现代计算机上(串行地)需要大约3秒钟。如果您并行化(也可以这样做),它将足够快。 - Janus Troelsen

3
当涉及基于ptrace(strace)的沙盒时:
"sydbox" 沙盒和 "pinktrace" 编程库(它是C99,但据我所知有Python和Ruby绑定)。
与该主题相关的链接: http://www.diigo.com/user/wierzowiecki/sydbox
(抱歉没有直接链接,但是还没有足够的声望点数)

3

-2

好的,感谢所有回答,它们对我帮助很大。但是我建议不要将它们作为原问题提出者的解决方案。所有提到的工具都需要太多的工作来测试学生代码,作为教师、导师或教授。在我看来,在这种情况下最好的方法是使用虚拟机。好吧,它模拟了一个完整的x68系统,与沙盒的意义没有任何关系,但如果我想象一下我的编程老师,这将是最好的选择。因此,在基于Debian的系统上执行“apt-get install virtualbox”,其他系统请访问http://virtualbox.org/,创建虚拟机,添加ISO,点击安装,等待一段时间并且会很幸运。使用起来比设置用户模式Linux或进行一些繁重的strace操作要容易得多...

如果您担心学生会黑客攻击您,我想您可能有权威问题,解决方法是威胁他们,如果您可以证明他们的作品中有恶意软件,您将起诉他们...

如果有一个班级,其中1%的学生能够做出这样的事情,那么不要让他们感到无聊,给他们一些更大的任务,让他们编写更多的代码。综合性学习对每个人都是最好的选择,所以不要依赖旧的僵化结构...

当然,永远不要使用同一台电脑进行重要的事情(比如写证明和考试),而你又用它来浏览网页和测试软件。

对于重要的事情,请使用离线电脑;对于其他所有事情,请使用联网电脑。

然而,对于其他不是偏执的老师(我不想冒犯任何人,我只是认为在成为程序员老师之前,你应该学习有关安全和我们社会的基础知识...)

...我在哪里...对于其他人:

愉快的黑客行动!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接