11.4 Linux Jail

引言:跨生态系统容器化的技术融合

Linux 兼容层(Linuxulator)与 Jail 技术的结合,为 FreeBSD 提供了运行 Linux 二进制程序的能力,实现了不同操作系统生态系统间的技术互操作性。Linuxulator 是 FreeBSD 内核中的一个兼容层模块,通过系统调用转换机制,使 Linux 二进制程序能够在 FreeBSD 内核上直接运行。本章详细探讨如何在 FreeBSD Jail 中部署完整的 Linux 用户空间环境。

准备工作:环境配置

本文将所有 Jail 绑定到虚拟网络接口 lo1,使其相当于位于 FreeBSD 系统下的一个局域网,其中 FreeBSD 主机充当该局域网的网关。

所有 Jail 的网络流量必须通过网络接口 lo1,因此需要启用网络转发,本节使用 pf 防火墙实现此功能。

注意

配置 pf 防火墙是实现网络访问的关键步骤。

准备网络接口

添加克隆网络接口 lo1 并启用:

# sysrc cloned_interfaces+="lo1"
# service netif cloneup

准备 pf 防火墙

这里提供两种方式进行配置,请按需选用。

方案一

编辑 /etc/pf.conf,加入以下配置。pf 防火墙的"表"是一种命名结构,可保存地址和网络的集合,是 pf 实现高效地址匹配和管理的核心机制。persist 标志使防火墙始终保留该表,即使没有相关规则引用它,确保表在防火墙规则重新加载时不会被自动清空:

可以使用 pfctljails 表进行添加或删除操作,以控制网络访问。例如:

  • pfctl -t jails -T add 192.168.5.1192.168.5.1 加入 jails 表使其可以访问网络

  • pfctl -t jails -T delete 192.168.5.1192.168.5.1 移出 jails 表使其无法访问网络。

这种方法的特点是手动管理较为繁琐,但具有较高灵活性。

方案二

直接在 /etc/pf.conf 中写下规则:

此方法允许 192.168.5.1 访问网络,其特点是规则固化在配置文件中,对于没有特殊需求的场景比较方便。

启用 pf 防火墙

即使不使用防火墙规则,也需要启用 pf 服务来实现 NAT 功能:

加载 Linux 二进制兼容层(Linuxulator)内核模块

启用并启动 Linux 兼容层服务,该方式可以自动加载 Linux 兼容层所需的各个内核模块:

准备目录

创建用于保存建立 jail 的相关文件的目录:

创建 Debian Jail:Debian 12 实例配置

下载基本系统

这里以 Debian 12(bookworm)为例:

构建 Ubuntu/Debian 基本系统。debootstrap 是用于构建 Debian/Ubuntu 基本系统的工具,设置权限为 0700 可确保只有 root 用户可以执行该程序:

示例输出如下:

输出末尾可能出现与配置相关的提示信息,这是 debootstrap 在 chroot 环境中运行服务配置脚本时的正常现象,不影响基本系统的使用。

创建 Debian Jail 实例

现在用 Debian 12 基本系统创建 Jail 实例,命名为 debian。

实例的基本配置

创建 /etc/fstab.debian。各文件系统作用如下:devfs 提供设备节点访问,tmpfs 为共享内存提供临时文件系统,fdescfs 提供文件描述符访问,linprocfs 和 linsysfs 为 Linux 应用提供兼容的 proc 和 sys 文件系统,nullfs 用于挂载宿主机的 tmp 目录:

/etc/jail.conf 中,加入以下内容(若无则新建)。关键配置项说明:devfs_ruleset 定义 devfs 的规则集,enforce_statfs 控制 Jail 中挂载点的可见性(0:无限制,1:仅根目录下可见,2:默认,仅根目录所在挂载点可操作):

exec.start 指定启动 Jail 时运行的命令。

在 FreeBSD 中创建 Jail 时,一般使用 exec.start = 'sh /etc/rc' 来调用 FreeBSD 的 rc 系统启动服务。

Debian 使用 systemd 作为初始化系统,而 Jail 中由于缺少必要的 cgroup 挂载和系统权限,无法使用 systemd,因此无法直接运行相应命令(但 service 命令仍可使用)。此处使用 /bin/true 安全返回 true(成功状态),不执行任何操作,这是一种在受限环境中避免服务启动失败的常用技术手段。

例如,在 debian Jail 中启用 sshd 服务后(执行 service ssh start),重启 Jail 时 sshd 服务不会随 Jail 自动启动。此时可以设置 exec.start = 'service ssh start',以确保启动 Jail 时自动启动 sshd 服务。

如果要启用更多服务,则可以按如下方式编写:

在 Jail 启动时顺序启动 SSH 和 D-Bus 服务:

exec.stop 停止 Jail 时运行的命令。

如果是 FreeBSD Jail,通常使用 sh /etc/rc.shutdown jail

同样由于 systemd 的限制,此处使用 /bin/true 安全返回 true 即可。

启用实例,允许网络访问

启动 Jail:

停止 Jail:

在 pf 防火墙中的 jails 表中加入 Jail 的地址,以允许 Jail 访问网络:

更新系统

执行以下命令进入 Jail 并更新系统:

或者使用

至此,一个基于 Debian 12 的 Linux Jail 已成功建立,同样的方法可用于创建基于不同版本的 Debian 或 Ubuntu 的多个 Jail。

Jail 开机自启

开机时启动 jail 服务:

默认情况下会启动 /etc/jail.conf 中配置的所有 Jail。

另外可以在 /etc/rc.conf 中用 jail_list 变量指定在开机时启动的 Jail 的名字,编辑 /etc/rc.conf 写入:

或执行

如果 jail_list 变量为空,则会启动所有在 /etc/jail.conf 中配置的 Jail。

创建 Ubuntu Jail

Ubuntu Jail 的建立方法与 Debian 相同,这里以 Ubuntu 22.04(Jammy)为例。

构建基本系统并配置

创建 Ubuntu Jail 根目录并使用 debootstrap 安装系统:

创建 /etc/fstab.ubuntu,内容如下:

/etc/jail.conf 中写入 Ubuntu 的配置:

启用实例并允许网络访问

启用实例:

如果已经在 /etc/rc.conf 中设置过 jail_enable=YES,也可以用:

开机启动可以参考 debian Jail,执行以下命令启动名为 ubuntu 的 Jail:

在 pf 防火墙中的 jails 表中加入 Jail 的地址,以允许 Jail 访问网络:

更新系统

或者使用

过程和 debian Jail 相同。

创建 antiX Linux Jail

antiX Linux 是基于 Debian 的轻量级发行版,采用 SysVinit 初始化系统而非 systemd,为非 systemd 生态提供了重要替代方案。SysVinit 是传统的 UNIX 初始化系统,依赖运行级别(runlevels)和 init 脚本进行服务管理,在 Jail 环境中具有更好的兼容性。

下载镜像文件并解压

先安装 squashfs-tools,用以解压 antiX Linux 文件系统镜像。

使用 pkg 安装:

antiX Linux 提供了四款镜像版本:full、base、core 和 net,本示例下载 core 版本:

配置 antiX Jail

创建 /etc/fstab.antix,内容如下:

/etc/jail.conf 中写入(这里只展示 antiX 相关部分):

此处将 exec.start 设置为 /etc/init.d/rc 3

如前文 debian Jail 所述,Debian 使用 systemd 作为初始化系统,而在 Jail 中无法使用。因此在 debian Jail 中使用 /bin/true 启动,以安全返回 true 并不执行任何操作。

antiX 不使用 systemd 作为初始化系统,而是使用 rc 进行初始化。此处 /etc/init.d/rc 3 指定 antiX Jail 启动时使用 rc 在运行级别 3 初始化 Jail,因此 debian Jail 中遇到的服务启动问题在这里不存在,可直接在 Jail 启动时运行服务(如 sshd)。

更新系统

设置开机自启,然后启动:

在 pf 中允许网络访问,方法同上:

现在进入 Jail:

apt upgrade 过程中可能出现 mandb 权限相关提示,多次执行 mandb 命令可完成索引更新。

创建 Alpine Jail

构建基本系统:

创建 /etc/fstab.alpine。/tmp 挂载被注释掉是为了避免将整个宿主机 /tmp 目录暴露给 Jail,提高安全性:

/etc/jail.conf 中写入:

设置开机启动,然后启动:

在 pf 中允许网络访问,方法同上:

现在进入 Jail。minirootfs 仅提供基础环境,安装 OpenRC 可获得完整的服务管理能力:

修改 /etc/jail.conf 中 Alpine 的配置:

重启 alpine Jail:

Jail 中的 GUI

本文示例环境为 Windows 10 物理机,运行 VirtualBox 安装的 FreeBSD 13.1 虚拟机。

在 FreeBSD 虚拟机中已部署了 4 个 Jail,如下。其中有一个 FreeBSD Jail,为区分 VirtualBox 中的 FreeBSD 虚拟机,称其为 freebsd-jail

Windows 10 物理机 ⇨ FreeBSD 13.1 虚拟机 ⇨ FreeBSD Jail(freebsd-jail) + debian Jail + Ubuntu Jail + Alpine Jail。

在这 4 个 Jail 中分别安装 xclock、firefox、chrome、jwm 这几款软件。

在 freebsd-jail、Debian、Ubuntu 和 Alpine(Alpine 使用 VNC 方法不成功,其它方法如 xclock、xterm 可运行,Firefox 和 Chrome 未成功)中均已成功安装,其中 jwm 用于美化界面。

技巧

不用安装 xorg。

其中,在 Ubuntu 22.04,Firefox、Chrome 要求用 snap 安装,snap 需要 systemd,不能使用,所以使用 deb 包安装。方法如下:

带 X Server 的终端

这里使用 MobaXterm。MobaXterm 默认配置,无多余配置。

确保 X server 已经启用,记下 DISPLAY 值,格式是 hostname:displaynumber.screennumber 这里是 192.168.56.1:0.0

登录 Jail(不限方式,可以是 SSH,可以是 jexec;不限用户,可以是普通用户,不一定是 root、wheel 组成员)

4 个 xclock 可独立显示在 Windows 10 桌面上,效果比较理想。

内嵌 X Server

Xnest/Xephyr 是嵌套式 X Server 技术,对 X11 应用来说是 X Server,对宿主 X Server 来说是 X Client,这种嵌套结构可在一个 X 窗口中运行另一个完整的 X 会话。

在 FreeBSD 虚拟机里安装 Xnest 或 Xephyr。两者择其一即可:

或者

在 FreeBSD 中启用:

参数说明:

  • :1DISPLAY 值中的 displaynumber。FreeBSD 系统 IP 为 10.0.2.15,所以完整的 DISPLAY 值为 10.0.2.15:1.0。因为 FreeBSD 系统 X Server 的 displaynumber 值为 0,所以从 1 开始

  • -listen tcp 侦听 TCP 端口

在 Jail 中,采取上面相同的方法:

四个 Jail 可以同时在 FreeBSD 开启的一个 Xnest/Xephyr 窗口中打开 xclock。但是此时的 xclock 因为没有窗口管理器,缺少窗口装饰和基本交互功能。可以在执行 xclock 前先执行 jwm,如下:

jwm 执行一次即可,不需要每个 Jail 都去执行。此处使用 jwm 是因为其轻量级,Xfce 等桌面环境也可使用,可根据需要尝试。

共享主机 socket 方式

先在 FreeBSD 系统上执行:

注意:xhost + 会关闭访问控制,存在安全风险。更安全的方式是指定允许访问的 IP 地址。

然后在 Jail 的 fstab 中加入下面内容,这里以 ubuntu Jail 的 fstab 为例,其它 Jail 参照修改即可:

必要时先 mkdir -p /usr/jails/ubuntu/tmp/.X11-unix,确保有挂载点。

重启 jail 后,在 jail 中执行:

上文提到 fstab 文件中有下面这样一行:

该写法将 FreeBSD 的 /tmp 目录暴露在 Jail 中并且可读写,破坏隔离性,因此注释掉。而仅挂载 /tmp/.X11-unix 可显著提高安全性。

X Server TCP 侦听加 xhost 连接管理

此处使用 SDDM 桌面管理器,如使用其它桌面管理器,请参考相应文档,原理相同。

在 FreeBSD 内新建 /usr/local/etc/sddm.conf(如果没有的话),修改 ServerArguments 内容如下:

重启后,FreeBSD 上用下面方式加入 Jail IP 以允许访问 (无需 root 权限):

这种方式比 xhost + 更安全,仅允许指定 IP 访问。

然后在 Jail 中,执行:

DISPLAY 设为 :0.0127.0.0.1:0.010.0.2.15:0.0 均可成功,上面几种方法也可以一一尝试。

VNC

在 Jail 中安装 vnc server,可以用 tightvnc-server,也可以用 tigervnc-server,这里以 debian Jail 为例,在 Jail 中执行:

vncserver 的端口号为 5900 加上冒号后的数字,现在为 5900,:1 端口号为 5901,以此类推。

使用 vnc 客户端登录:

备注

宿主机 X Server TCP 侦听加 xhost 连接管理的方式是安全性较差的一种,是 TCP 侦听默认关闭原因之一。

共享宿主机 Socket 的方式注意上面提到的两点,安全性还是有保障的。

带 X Server 的终端、共享主机 Socket、VNC 这三种方式比较推荐。无论采用哪种方式,Linux Jail 中不应要求每个 X 应用都能运行,这存在一定限制,因为兼容层无法做到完全兼容,而 FreeBSD Jail 的兼容性更好。

除共享宿主机 Socket 的方法外,其他方法同样可在非 Jail 环境中使用。

参考资料

课后习题

  1. 部署 Debian 12 Linux Jail 并在其中运行 X11 应用,验证三种不同 X Server 连接方法的安全性与功能性差异。

  2. 对比 systemd(Debian)、SysVinit(antiX)和 OpenRC(Alpine)三种初始化系统在 Jail 中的兼容性,分析技术选型如何影响系统能力。

  3. 修改 Linux Jail 的 enforce_statfs 配置,观察挂载点可见性变化,分析该参数的意义。

最后更新于