简单而轻松的 FreeBSD Jail
2025 年 8 月 25 日
作者:FreeBSD 基金会
在 FreeBSD 上运行常见服务很简单。但有时你想在同一个操作系统实例上运行多个服务,并且让每个服务都彼此安全地“隔离”。如果不提供互联网连接,web 服务就没多大意义,但这同时也可能带来了安全漏洞。很显然,我们不希望这样。如果某个服务有可能因为安全漏洞而被攻破,那么尽可能减少系统的暴露会更好,不是吗?
这就是 jail 的用武之地。Jail 是容器化的轻量化答案,早在 Docker 变得流行之前,很久以前 它就已存在。
设置 Jail
同科技行业中的所有事情一样,至少有一只手的方法能实现相同的结果。在 FreeBSD 上有许多工具可以用来创建和管理 jail,但我更倾向于 保持简单,所以选择使用默认安装系统中自带的工具。
手册中有 关于 jail 的优秀文档,我基本遵循它,仅做了一些细微的调整。
我从 我们最近 YouTube 视频 中创建的基础虚拟机开始,因为接下来的设置使用 ZFS。
这就是我设置 jail 的过程。如果你有五分钟,我们一起来看看。
配置宿主机
启用 jail:
sysrc jail_enable="YES" && sysrc jail_parallel_start="YES"
然后创建一些基本的 zfs 文件系统:
zfs create -o mountpoint=/jails zroot/jails
zfs create zroot/jails/media
zfs create zroot/jails/_base
手册中有其他挂载方式,你也可以选择使用。我更喜欢稍微简单一些的“base”和模板文件系统,然后通过快照来生成新的 jail。我们稍后会用到。
创建模板 jail
下载 FreeBSD 用户态文件用作我们的模板:
fetch https://download.freebsd.org/ftp/releases/$(uname -m)/$(freebsd-version)/base.txz -o /jails/media/$(freebsd-version)-base.txz
解压文件,修补模板,然后创建快照:
tar -xf /jails/media/$(freebsd-version)-base.txz -C /jails/_base/ --unlink
cp /etc/resolv.conf /jails/_base/etc/resolv.conf
cp /etc/localtime /jails/_base/etc/localtime
freebsd-update -b /jails/_base/ fetch install
pkg -c /jails/_base install -y pkg python zsh
zfs snapshot zroot/jails/_base@$(freebsd-version)-$(date +%d-%b-%y)
现在我们有了“模板” jail,可以用来创建新的 jail。我不会运行这个 jail,而且通常会在模板里添加常用包,这样每个后续克隆都会包含它们。把你喜欢的工具都放进去 🙂 值得注意的是:如果你打算用 Ansible 来管理 jail 中的包,你也需要在里面安装 pkg。之前为了保持简单,我没有这么做,而是直接在宿主机上用命令 pkg -j JAILNAME install BLAH
来安装。我想这算是个哲学问题——你是把 jail 看作是扩展的 chroot,还是微型的虚拟机?
差不多快完成了。我们需要 /etc/jail.conf
——我选择使用统一的“全局”文件,其中的选项适用于所有 jail,然后在 /etc/jail.conf.d/
中为每个 jail 单独创建配置文件:
/etc/jail.conf
模板:
# https://man.freebsd.org/jail.conf
# https://man.freebsd.org/jail
#
ip4 = inherit;
ip6 = inherit;
# 默认应用于所有 jail
exec.start = "/bin/sh /etc/rc";
exec.stop = "/bin/sh /etc/rc.shutdown jail";
exec.clean;
# 文件系统
mount.devfs;
devfs_ruleset=4;
enforce_statfs=1;
securelevel=2;
#allow.mount.zfs;
#allow.mount;
# 禁用所有花哨的东西
allow.mount.nodevfs;
allow.mount.nofdescfs;
allow.mount.nolinprocfs;
allow.mount.nonullfs;
allow.mount.noprocfs;
allow.mount.notmpfs;
allow.nochflags;
# 但这些总是有用的
allow.raw_sockets;
allow.reserved_ports;
allow.sysvipc=1;
# 杂项
allow.nomlock;
allow.noquotas;
allow.noread_msgbuf;
allow.noset_hostname;
allow.nosocket_af;
allow.nosysvipc;
sysvmsg=disable;
sysvsem=disable;
children.max=0;
.include "/etc/jail.conf.d/*.conf";
创建新的 jail
每当我需要新的 jail 时,只需 zfs 克隆模板,在 /etc/jail.conf.d/
中创建一个配置文件,并在 /etc/rc.conf
的 jail_list
中添加新 jail 名称。我写了个简单的 shell 脚本来实现这一点:
#!/bin/sh
#
set -eo pipefail
SNAP=${2:-$(zfs list -t snapshot -H -o name | grep "jails/_base" | cut -f3 -d/)}
if [ -z $1 ]; then
echo "pass new jail name"
exit 1
else
new=$1
fi
zfs clone zroot/jails/${SNAP} zroot/jails/${new}
test -d /etc/jail.conf.d || mkdir -p /etc/jail.conf.d
cat > /etc/jail.conf.d/${new}.conf <<EOF
${new} {
host.hostname = "${new}.jail";
path = "/jails/${new}";
}
EOF
sysrc jail_list+=${new}
扩展 Jail
这只是有关关于如何快速启动 jail 的简单介绍。手册文档里有更多细节,包括如何 管理资源限制(比如限制内存和 CPU)。如果你想进一步扩展,运行更多的 jail,你可能需要看看手册中列出的一些工具,或者参考 社区的 Ansible 剧本。
正如我们博客文章中常做的那样,我们把本文用到的一些资源放在了 我们的 GitHub 仓库,并制作了视频:
最后更新于