什么是监狱?

什么是监狱?

我目前使用的是 FreeBSD 11.2(可能很快就会升级到 12)。我需要一个小型的纯权威 DNS 服务器(不需要查找或缓存,少于 10 个域,每小时查询次数少于 10 个,几乎没有记录更改)。

我可能会选择TinyDNS, 的一部分djbdns,它以安全性而闻名,而且看起来也很小,并且可以满足我的需要。

安全性的部分原因是它会暴露在互联网上,尽管有 IP/端口过滤器和非常低的速率、速率限制器(用于pf此目的)。但出于这个原因,我确实想特别注意守护进程的设置方式,以避免明显的漏洞。我指的是所需的用户和组、启动/停止脚本、jail/chrooting 以及最小化/禁用/拒绝攻击者可以利用的主要非必要访问/功能等方面。

(我应该提到的是,我在测试系统上“通常”安装tinydns,并创建所需的.conf文件,因此这纯粹是如何以安全的方式运行它,这是缺失的)

我在设置软件来运行 chrooted/jailed 或审查 chrooted/jailed 包以实现适当的安全实践方面没有经验,这也是我第一次尝试做这样的事情,尽管我选择了特定的DNS 服务器包专门因其明显的设置简单性而设计。

忽略该.conf文件,“配方”会是什么样子,将TinyDNS设置为作为服务正确运行,并且理想情况下最大限度地减少对“守护进程不重要但对攻击者有帮助的其他内容”的访问?

答案1

这是相当长的,所以对于那些懒得读完的人来说,这是一个非常简短的版本:

echo "testjail { }" >> /etc/jail.conf
mkdir -p /usr/local/jails/testjail
bsdinstall jail /usr/local/jails/testjail
service jail start testjail

pkg -j testjail install -y nginx
sysrc -j testjail "nginx_enable"=YES
service -j testjail nginx start

我通常会认为这个问题“太宽泛”,并建议人们直接参考FreeBSD 手册。但我自己发现该部分写得相当糟糕且令人困惑。一切都在那里 - 但事情比看起来容易得多!我只是希望他们更多地关注概念而不是列出命令。你可能会享受阅读的乐趣Jails——高价值但糟糕的虚拟化

相反,我要做的是谦虚地概述我个人所做的事情以及我遇到的绊脚石。我的失败可能不是你的,但由于我曾经遇到过和你一样的情况,我希望我的旅程能有所帮助。你对 FreeBSD 整体了解得越多,越容易陷入监狱。

什么是监狱?

许多描述掩盖了监狱的重要部分。它对理解内核的作用有很大帮助。它“运行”代码并跟踪 PID(进程 ID)和 UID(用户 ID)。对于许多 Unix 用户来说,这是常识。 FreeBSD 内核随后添加了 JID(Jail ID)的概念。然后内核能够将进程划分为监狱。这实际上意味着 FreeBSD 内核能够“虚拟化”系统而无需任何开销。您仍然只有一个内核,但可以有多个系统。这是我理解这个概念的关键。

如果你运行一个“没有”监狱的普通盒子。那么所有进程都属于 JID 0。当您开始使用监狱时,我们将其称为“监狱主机”。

考虑到这一点,我的下一步就是了解与 FreeBSD 实际工作原理的联系。如果您有 Linux 背景,您可能知道 Linux“只是”内核。构成系统的是发行版(Ubuntu、Debian、Slackware 等)提供的用户空间。 FreeBSD 是内核和用户态的结合体。它是完整的操作系统(OS)。

所以一个非常粗略的总结第 12 章 FreeBSD 引导过程是:

  1. BIOS/UEFI 引导程序
  2. FreeBSD 引导加载程序
  3. FreeBSD 内核
  4. 自由BSD在里面运行RC脚本和所有魔​​法

你可能知道 FreeBSD 是多么模块化,并且你可以用类似的东西替换 rc 系统开放式RC但为了简单起见,我们暂时忽略它。

然后,当您启动(引导)监狱时,内核会使用新的 JID(即 1)进行 init。这里的所有内容都被限制在监狱 1 中。init 需要接触的内容必须位于 chroot 后的文件系统内。如果你想让事情变得简单那就是满的用户区。但我们希望将监狱与主机系统分开,所以这就是完整副本用户区的!

这些事物如何相互关联的重要性不容低估。当您完全理解后,您会发现监狱直接受到许多 FreeBSD 工具的支持。不仅仅是ps -J“但是” bsdinstallfreebsd-update“并且”这样简单的事情pkg!如果维护 FreeBSD 系统是你的第二天性,那么监狱对你来说就像在公园里散步一样简单!

但对于我们大多数人来说,我们处于通往涅槃之路的中间位置,可能不得不与一些概念作斗争才能“正确”地完成它。

希尔

我非常喜欢希尔。它对于事物应该放置的位置给出了清晰一致的视图。不幸的是,权力机构从未对监狱的默认位置做出任何决定。当您将其与使用不同位置的许多不同教程结合起来并混合使用各种术语作为“基本安装”和“模板”时,事情很快就会变得混乱!

本手册不讨论这一点,仅提及/here/is/the/jail。在最后一个例子中,他们使用/home/j/home/j/mroot。我更喜欢保留用户目录并且仅保留用户目录/home。在我的书中,简单地使用像这样的速记符号j是一个很大的禁忌。

我想说最常见和“正确”的位置是/usr/local/jails.对于使用 ZFS 的人来说,一个强有力的竞争者是/jails

在这个位置我会放置我的监狱的 chroot。我就是这样讲的。从此以后,监狱将设在这里。

当我刚开始时,我发现这非常令人困惑。给我把它放在任何地方的自由让我更加不安全,因为我不明白后果。

为了增加这种混乱,许多教程使用“basejail”、“骨架目录”、“模板”。这增加了人们对监狱到底是什么的困惑,并将其与许多管理人员混为一谈。

文件系统

我们并不真正关心我们正在使用什么文件系统。它可以是 UFS 或 ZFS。为了监狱的目的,我们只需要一个 chroot 目录。

使用 UFS 时,需要注意的重要一点是如何排列切片。初学者常常没有考虑这么多。哪个切片有足够的空间来容纳 chroot。这就是为什么我特别不喜欢用于/home此目的。也许您为此目的创建了一个切片,或者只是在某处创建了一个目录,然后再确定是否有足够的空间。

如果您使用 ZFS,问题实际上是相同的。由于 ZFS 的构造方式,您可能会认为创建一个新数据集比仅仅执行 更好mkdir。但对于初学者来说,你不必担心。使用 ZFS 执行“mkdir”更适合您。这使事情变得更简单,您可以在没有 ZFS 的情况下继续前进。

下一个问题是,许多教程还使用“basejail”进行操作,这是一个完整的普通系统,准备复制以创建新的监狱。有些教程会告诉您进行 ZFS 克隆而不是复制。在很长一段时间内,您将更新基本监狱并自然地为其创建快照。但随后您会注意到,您无法删除任何具有其克隆的快照。所以在实际使用中我更喜欢使用zfs send/receive。

在我看来,教程应该只涉及 ZFS 作为附录。 ZFS 本身就是一个很大的话题,应该这样对待。当您对监狱和 ZFS 感到满意时,您就可以随心所欲地结合起来并获得好处。如果不这样做,你最终会建造一座非常脆弱的塔,而在损坏时却没有任何能力修复它们。

胖/厚 vs 瘦

FreeBSD 手册谈论“完整”监狱和“服务”监狱。大多数其他地方使用术语“厚”(胖/满)和“瘦”监狱。厚监狱和瘦监狱都是“完全”虚拟化 FreeBSD 系统,FreeBSD 手册将其称为“完整”监狱。然而,“服务”监狱更加难以捉摸

厚厚的监狱就是完整操作系统的副本。但这到底有多少脂肪呢?带有 base/lib32/ports 的 FreeBSD 11.2 的重量为 1.4G,但对于监狱来说,单独使用“base”就可能使你保持在相对较小的 512M(与 Windows 10 的 C:\Windows 的 20G 相比)。

精简监狱是一种最小化“完整”监狱的磁盘使用量的技巧。通过使用空文件系统(环回文件系统子树)来安装只读文件系统和几个放置良好的符号链接以启用读/写部分,从而减少磁盘使用量。不幸的是我手头没有任何数字。当您拥有磁盘结构后,您只需在启动监狱时添加mount.fstab到您的文件系统即可挂载文件系统。jail.conf

我得到了如何做到这一点的想法FreeBSD 的艰难之路。这教会了我如何在没有任何第三方实用程序的情况下处理监狱,并最终让我明白了这一点。尽管有标题,但这实际上是最简单的方法。然而他们确实做错了一件事。您不应该执行 zfs 克隆,而应该执行 zfs 发送/接收,参见纯粹的监狱,简单的方法。两人可能都从稍微过时的作品中找到了灵感多个带有 nullfs 的 FreeBSD Jails。另一个有价值的来源是FreeBSD 瘦监狱。最后一个更新的资源也涵盖了 VNET(我们将回到这个问题!)是如何配置带有 vnet 和 ZFS 的 FreeBSD Jail

上面的内容通常被称为精简监狱,但也可以在其他地方完成方法。条条大路通罗马,您自己决定如何在当地实施。

所有这些都将我们引向难以捉摸的“服务”监狱。在最封锁的情况下仅运行一项特定服务的监狱。只是实际的应用程序,只有一个薄层来支持它。这是可以做到的,但我还没有看到很多这方面的工作。

典型的内容jail.conf包括:

exec.start = "/bin/sh /etc/rc";            # Start command
exec.stop = "/bin/sh /etc/rc.shutdown";    # Stop command

这可以确保 rc 系统在启动/停止时运行在监狱中。如果您有一个非常简单的可执行文件,您可以简单地指向它。所以问题是:我的服务需要访问多少操作系统?需要哪些共享库以及您运行脚本吗?

据我所知,没有任何工作可以为您做到这一点。因此,您从一个完整的监狱开始,然后删除您的特定服务不需要的内容。许多命令行实用程序(例如topps和 )tail可以安全删除,因为守护程序通常不使用它们。但如果您自己在监狱内进行调试,您可能会错过它们。如果你直接启动一个守护进程exec.start你就不需要rc.subr和啦。

如果您走这条路线并开始疯狂杀戮,以确定可以从操作系统中删除多少内容(以减少攻击面),同时服务仍保持功能,那么您应该意识到纳米BSD。这可以帮助您根据需要定制安装。

所以——绝对可行。但据我所知没有公共工程。大多数人都会选择厚监狱或薄监狱,并在那里运行指定的服务。

依赖地狱

当做精简监狱时,你需要非常小心,确保所有事情都正确,否则事情就会崩溃。当您更新系统时,您需要记住更新所有不同的部分。你开始的基地监狱或来源。模板和实际的监狱。这是很多移动的轮子,这使得管理变得更加困难。这是可以做到的,但你应该权衡付出的努力和回报。磁盘空间非常便宜 - 因此您需要相当多的服务才能使其值得付出努力。

然后我会建议使用简单的胖监狱,其中包含完整的操作系统。

如果您对 ZFS 非常满意,那么一定要尽情发挥。但如果没有,我强烈建议将 chroot 放在一个简单的目录中。当你觉得在监狱里工作很舒服时,你就可以开始添加奶油了。

关键是将监狱视为一个独立的系统。如果你今天正确维护你的系统,你应该已经有使用的习惯freebsd-update fetch install,你会知道它更新内核和用户空间。

将监狱添加到组合中时,您只需要记住也更新它们:

freebsd-update -b /usr/local/jails/testjail install

如果您正在进行升级 - 那么也请记住监狱:

 freebsd-update -b /usr/local/jails/testjail --currently-running 10.3-RELEASE -r 11.0-RELEASE upgrade

就像保持包裹新鲜一样

 pkg upgrade
 pkg -j testjail upgrade

嗨,老兄!我只是想把一个服务关进监狱!

好的!考虑到所有买者自负,让我们在完全普通的系统上以简单的方式完成此操作。 TinyDNS 是一个不好的例子,所以让我们安装一下nginx

  1. 创建一个 zane 监狱配置/etc/jail.conf
  2. 允许监狱在启动时启动
  3. 添加监狱名称“testjail”
  4. 添加“testjail”chroot 目录
  5. 将操作系统安装到 chroot(仅选择基础。取消选择lib32/ ports
  6. 开始监狱
  7. 将包添加nginx到监狱。
  8. 告诉监狱在启动时启动 nginx
  9. 立即启动 nginx!

完毕!

1 和 2 仅用于为入狱做好准备。每个新监狱有 3 - 6 个。每项服务 7 - 9 个。

cat <<'EOF'>/etc/jail.conf
# Global settings applied to all jails.
host.hostname = "${name}.jail";
ip4 = inherit;
ip6 = inherit;
path = "/usr/local/jails/${name}";
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
mount.devfs;

# Specific seetings can be applied for each jail:
'EOF'
sysrc "jail_enable"=YES

echo "testjail { }" >> /etc/jail.conf
mkdir -p /usr/local/jails/testjail
bsdinstall jail /usr/local/jails/testjail
service jail start testjail

pkg -j testjail install -y nginx
sysrc -j testjail "nginx_enable"=YES
service -j testjail nginx start

这就是我所说的进行监狱和简单管理的“正确”方法。有很多变体 - 最后一行可以替换为service jail restart testjail.此时我可以在我的被监禁的 nginx 实例上浏览网页。

某些 FreeBSD 默认设置不太适合监狱。因此也请考虑设置这些:

sysrc -j testjail sendmail_enable="NONE"
sysrc -j testjail sendmail_submit_enable="NO"
sysrc -j testjail sendmail_outbound_enable="NO"
sysrc -j testjail sendmail_msp_queue_enable="NO"

移除监狱

删除监狱:

  1. 停止监狱
  2. 从“/etc/jail.conf”中删除监狱名称
  3. 重置标志以便您可以删除文件
  4. 删除文件
service jail stop testjail
sed -i '' '/^testjail {/ d' /etc/jail.conf
chflags -R noschg /usr/local/jails/testjail
rm -rf /usr/local/jails/testjail

上面的内容是为了表明,使用现有的工具来管理监狱并不难,而不需要第三部分监狱管理工具。

嗨,老兄!我只是想把 TinyDNS 关进监狱!

您写道您可以“通常”安装tinydns - 因此我将把它留给您。对于其他可能关注的人来说,他们可能会猜测它应该如此简单:

pkg -j testjail install djbdns

但是创建该包的人还不够好,无法为/usr/local/etc/rc.d.然后您需要弄清楚如何将其作为守护进程运行。您可以选择使用替代服务管理,例如导师或者一个简单的技巧将其添加到/etc/rc.local。或者为了获得奖励积分,创建一个 rc 脚本并将其贡献给我们所有人享受!

这让我们知道您真正需要知道的唯一两个特定的监狱命令:吉林斯其中列出了正在运行的监狱和执行程序它允许你在监狱里执行一些东西。

root@test:~ # jls
   JID  IP Address      Hostname                      Path
     3                  testjail.jail                 /usr/local/jails/testjail

要以 root 身份获取命令行,我们只需执行 root shell(记住那是 tcsh!)

root@test:~ # jexec testjail /bin/tcsh
root@testjail:/ # tinydns
tinydns: fatal: $IP not set
root@testjail:/ # exit
exit
root@test:~ #

当在该 shell 中工作时,一切都在你的监狱/chroot 中。

他们不告诉你什么!

到目前为止,我并没有偏离大多数教程所告诉您的内容。在文件系统上浪费了大量时间之后,他们跳过了令人兴奋的部分。如今,没有网络访问的计算机就没有多少乐趣了。在上面的示例中,我们与主机共享网络堆栈。这意味着您无法在使用与监狱相同端口的监狱主机上运行任何内容。在我的示例中,这是网络服务器的端口 80。

另一方面,现在设置防火墙非常容易。您只需将所有端口视为本地端口即可。如果您使用 IPv6,一切都会变得轻而易举。但我们大多数人(所有人?)仍然需要与 IPv4 作斗争。 IPv4 很简单,但您可能没有所需的地址,然后我们需要诉诸某种 NAT。

如果您想真正喜欢网络,我们有虚拟网络接口互联网络。它已经成熟了但要使用它,您需要编译一个支持 VIMAGE 的内核。这是在通用内核中启用的自由BSD 12.0及以后。

当使用 VNET 时,您将有一个很好的虚拟接口,您可以在其中运行防火墙里面监狱。

我目前不使用 VNET,因为监狱主机上的统一防火墙对我和我想要的已经足够了。通过这个我可以控制进出监狱的流量。这使得从监狱中破解东西变得更加困难。

在监狱主机上,我经常允许一些出站流量(http/ftp/nameresolution)。但是我的监狱中的所有流量我都明确指定了双向和监狱之间允许使用哪些端口。

诀窍是将流量转移到另一个接口。本地环回接口lo0是一个很好的选择。但为了更好地分离规则,最好将其克隆到新的接口名称lo1。您可以使用 进行设置并在监狱主机中预填充 IP 地址ifconfig。然而没有必要,因为监狱子系统会自动为您处理所有这些。

为此,我们/etc/jail.conf现在看起来像这样:

# Global settings applied to all jails.
host.hostname = "${name}.jail";
interface = "lo1";
path = "/usr/local/jails/${name}";
mount.fstab = "/usr/local/jails/${name}.fstab";
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown";
exec.clean;
mount.devfs;

# Specific seetings can be applied for each jail:

testjail { ip4.addr = 172.17.2.1; }

请注意如何ip4/6 = inherit;更改为interface="lo1".另请注意mount.fstab- 这就是我添加 nullfs 挂载的方式,但我现在不会进一步讨论这一点。然后我将我想要使用的内部IP添加到testjail。

然后我们通过添加以下内容来lo1克隆/etc/rc.conf

sysrc "cloned_interfaces"="lo1"

为此,您需要重新启动。如果您想立即尝试而不重新启动,则需要自己创建界面:

ifconfig lo1 create
ifconfig lo1 up

cloned_interfaces="lo1"这与中的设置效果相同/etc/rc.conf。您无需为 IP 地址创建别名,因为这会在监狱启动时自动处理。

然而,这很无聊,因为交通无处可去。

为了让流量继续运行,我们需要设置防火墙并启动一些 NAT。我选择的毒药是普夫。你的规则集在/etc/pf.conf

要从外部 NAT 到 http 的监狱 IP,请执行以下操作:

rdr pass inet proto tcp from any to (em0) port http -> 172.17.2.1 port http

如果许多服务无法连接到自身,则它们不会启动。因此我也为此添加了一条规则。

pass on lo1 proto tcp from 172.17.2.1 to 172.17.2.1 port http

仅当您希望外部人员进行连接时才需要 NAT 规则。当您开始拥有多个监狱时,您通常只希望一个监狱能够连接到另一个监狱。

pass on lo1 proto tcp from 172.17.2.1 to 172.17.2.2 port 8180

通过这些简单的防火墙设置,您可以在监狱和外部之间实现非常好的网络隔离。

概括

根据我自己的经验,我建议按照以下路径来了解如何使用监狱:

  1. 了解基本的内核功能 - 什么是进程
  2. 了解 chroot
  3. 了解启动过程
  4. 建造一个肥胖的监狱
  5. 与监狱一起工作。使用/更新
  6. 了解他
  7. 建造一个薄弱的监狱
  8. 与多个监狱合作
  9. 了解网络
  10. 设置网络
  11. 了解 ZFS

这是我的食谱。可能不完全是你所希望的:-)

如果您不想使用提供的工具处理棘手的问题,请查看一些第 3 方工具:

巴士底狱

埃兹贾尔

控制

从这一切您可以看出您的问题相当广泛,并且在很大程度上取决于您的首选设置。我相信我已经展示了一个表现良好的包裹的收据,例如nginx但也许您需要问一些其他问题:

  • 如何守护tinydns?
  • 如何以非特权用户身份运行tinydns?
  • 如何编写 rc 脚本

但我会把监狱部分排除在外。

相关内容