Skip to content

19.3 Qjail

19.3.1 Jail 管理工具对比

Qjail 是用于部署 Jail 环境的封装工具,提供了安全性和性能增强功能。

常见的 Jail 管理工具包括 ezjail、Qjail 和 iocage,各有侧重。

特性ezjailQjailiocage
开发语言shshPython
当前维护状态基本停止维护持续可用持续维护
文件系统要求UFS / ZFS 均可UFS / ZFS 均可必须 ZFS
是否依赖 ZFS
是否支持 UFS支持支持不支持
是否支持 VNET不支持支持支持
网络隔离能力较弱较强较强
配置方式Shell 脚本Shell 脚本Python CLI
学习成本较高
快照/克隆能力基本无有限强(依赖 ZFS)
资源占用很低很低较高
适合新部署不推荐推荐推荐(仅限 ZFS)
典型适用场景老旧系统维护通用 Jail 管理大规模 ZFS Jail 环境
主要限制无 VNET、停止维护功能相对朴素强依赖 ZFS

本节部署的 Jail 的概念结构如下图所示:

Jail 结构

19.3.2 预留 Jail 的 IP 地址:网络接口配置

/etc/rc.conf 文件中添加如下配置。克隆接口 lo1 可将 Jail 网络和宿主机网络配置分开,增强隔离性。

ini
cloned_interfaces="lo1"  # 克隆出 lo1,应和宿主机网络配置分离 ①
ifconfig_lo1_alias0="inet 192.168.1.1/28" # 创建独立网段,共 14 个 IP 可供分配。需要根据实际网络环境调整地址范围

警告

① 如要生成多个接口,应在同一行中以空格分隔描述,而不是另外创建多行,应如 cloned_interfaces="lo1 lo2"。分行书写时,只有第一行会生效。

重启所有网络接口以应用配置:

sh
# service netif restart

lo1 将获得 14 个 IP 地址,均可用于分配给各个 Jail。

19.3.3 安装 Qjail 工具

使用 pkg 安装 Qjail:

sh
# pkg install qjail

或者使用 Ports 安装:

sh
# cd /usr/ports/sysutils/qjail/
# make install clean

设置 qjail 服务开机自启动:

sh
# sysrc qjail_enable=YES

19.3.4 部署 Qjail 使用的目录结构

使用 Qjail 前,需部署 Qjail 使用的目录结构,有以下两种方式:

19.3.4.1 方法一:从官方镜像站自动下载

sh
# qjail install

此时 Qjail 会从 FreeBSD 官网下载 base.txz 文件,示例输出如下:

sh
# qjail install
resolving server address: ftp.freebsd.org:80
requesting http://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/15.0-RELEASE/base.txz
remote size / mtime: 195363380 / 1652346155
...

19.3.4.2 方法二:从境内镜像站下载

境内网络访问受限时,可使用镜像站手动下载,以中国科学技术大学开源镜像站为例。使用 freebsd-version 命令可确认宿主机的 FreeBSD 版本,Qjail 要求文件版本与宿主机一致。以下示例为 FreeBSD amd64 15.0:

从镜像服务器下载 FreeBSD 基本系统文件:

sh
# fetch https://mirrors.ustc.edu.cn/freebsd/releases/amd64/15.0-RELEASE/base.txz

使用 qjail 安装基本系统到指定 Jail:

sh
# qjail install base.txz

部署好 Qjail 的目录结构后,/usr/jails 目录下会自动生成 sharedfstemplatearchiveflavors 四个目录,这四个目录构成了 Qjail 的核心文件系统架构:

  • sharedfs 包含一份只读的操作系统可执行文件库,通过 nullfs 在各 Jail 之间共享以节省存储空间。nullfs 是 FreeBSD 提供的特殊文件系统,可在同一主机的不同位置重复挂载同一文件系统。
  • template 包含操作系统的配置文件模板,将被复制到每个 Jail 的基本文件系统中,作为新 Jail 的初始配置。
  • archive 保存 Jail archive 命令产生的存档文件,用于 Jail 的备份与恢复。
  • flavors 包含系统风格(flavors)和用户创建的自定义风格,本质上是预定义的配置文件集合,用于快速定制新 Jail 的配置。

文件结构:

sh
/usr/jails/
├── sharedfs/         # 只读操作系统库(各 Jail 共享)
├── template/         # 配置文件模板
├── archive/          # Jail 存档文件
├── flavors/          # 系统风格配置
   └── default/
       └── usr/
           └── local/
               └── etc/
                   └── pkg/
                       └── repos/
                           └── FreeBSD.conf  # 自定义 pkg 镜像配置
├── jail1/            # jail1 的根目录
└── jail2/            # jail2 的根目录
    └── usr/
        └── local/
            └── etc/
                └── pkg/
                    └── repos/
                        └── FreeBSD.conf  # 自动复制的配置

19.3.5 部署 Jail

创建 Jail jail1,指定网络接口和 IPv4 地址:

sh
# qjail create -n lo1 -4 192.168.1.1 jail1
  • -n 指定使用 lo1 作为网络接口
  • -4 指定 IPv4 地址

生成 jail1 后,/usr/jails/ 目录下会创建 jail1 目录(/usr/jails/jail1/)用于保存对应文件。

可在前述的 flavors 目录中创建自定义配置文件,以便在部署新的 Jail 时自动复制。例如,新建 /usr/jails/flavors/default/usr/local/etc/pkg/repos/FreeBSD.conf 文件,则之后创建的 Jail 会自动复制该文件:

sh
# qjail create -n lo1 -4 192.168.1.2 jail2

建立 jail2 后,自动生成 /usr/jails/jail2/usr/local/etc/pkg/repos/FreeBSD.conf 文件,即修改了此后所有 Jail 的默认 pkg 镜像。但由于生成 jail1 时尚未在 flavors 目录中写入相应文件,jail1 未生成该文件。

19.3.6 Qjail 基本用法

Qjail 管理的 Jail 本质上仍是标准的 FreeBSD Jail,因此 jlsjexec 等系统命令同样可用于查看和操作这些 Jail。以下为 Qjail 专属管理命令。

列出 Qjail 管理的 Jail:

sh
# qjail list

启用 Jail:

sh
# qjail start # 启动所有 Jail
# qjail start jail1 # 启动 jail1

停止 Jail:

sh
# qjail stop # 停止所有 Jail
# qjail stop jail1 # 停止 jail1

重启 Jail:

sh
# qjail restart # 重启所有 Jail
# qjail restart jail1 # 重启 jail1

进入 Jail 控制台:

sh
# qjail console jail1

进入 Jail 控制台后,将以 Jail 中的 root 账户身份操作(无需输入密码)。由于 Jail 可能开启对外服务,为安全起见,建议设置 root 账户密码。

备份 Jail:

sh
# qjail archive -A  # 备份所有 Jail
# qjail archive jail1  # 备份 jail1

从备份中恢复 Jail:

sh
# qjail restore jail1

删除 Jail:

sh
# qjail delete jail1  # 删除 jail1
# qjail delete -A     # 删除所有 Jail

19.3.7 更新 Jail

Jail 的部分更新并非针对单个 Jail,而是针对所有 Jail,这些文件通过 nullfs 共享一份。

19.3.7.1 更新 Jail 中的基本系统

更新 sharedfs 中的文件:

sh
# qjail update -b

19.3.7.2 更新 ports

选项 -P(大写)使用宿主机的 Ports 更新 Jail 的 Ports 树。

sh
# qjail update -P

19.3.7.3 更新系统源代码

sh
# qjail update -S # S 大写

19.3.7.4 更新过程

完整的更新流程如下:

获取并安装 FreeBSD 系统更新:

sh
# freebsd-update fetch
# freebsd-update install

停止所有 qjail Jail:

sh
# qjail stop

更新 qjail 基本系统:

sh
# qjail update -b

更新 qjail 源代码:

sh
# qjail update -S

更新 qjail Ports:

sh
# qjail update -P

启动所有 qjail Jail:

sh
# qjail start

19.3.8 Jail 设置

Qjail 提供 qjail config 命令,用于定制每个 Jail 的配置。Jail 运行时核心配置参数无法安全修改,运行该命令前须先停止目标 Jail。

qjail config 命令提供丰富的配置选项,以下列出几个常用参数。

19.3.8.1 -h

为 jail1 快速配置 SSH 服务:

sh
# qjail config -h jail1

该命令会快速启用 jail1 的 SSH 服务,新建一个 wheel 组用户,用户名和密码均与 jail1 名称相同,首次使用该用户登录时,系统会要求修改密码。也可在登录 jail1 控制台后,自行配置 sshd 服务。

19.3.8.2 -m-M

设置 jail1 为手动启动状态:

sh
# qjail config -m jail1

将 jail1 设为手动启动状态(manual 状态)。qjail_enable="YES" 写入 /etc/rc.conf 文件后,系统启动时将自动启动各个 Jail;设为手动启动后,系统启动时不会自动启动相应 Jail,须以 qjail start jailname 手动启动。

对应小写的 -m 选项,有大写的 -M 选项,作用为关闭手动启动状态,即清除 manual 状态,可在系统启动时自动启用 Jail。Qjail 中有大量类似的选项,小写字母的选项启用某个功能,大写字母的选项关闭对应功能。下文中同时出现小写和大写的选项时不再说明。

19.3.8.3 -r-R

将 jail1 设为不允许启动(norun 状态):

sh
# qjail config -r jail1

该配置相当于禁用 jail1。

19.3.8.4 -y-Y

启用 jail1 的 System V IPC(进程间通信)机制:

sh
# qjail config -y jail1

System V IPC 是 UNIX 的经典进程间通信机制,包括共享内存、信号量和消息队列,在 jail1 中部署 PostgreSQL 等依赖共享内存和信号量的应用时,必须启用该选项。

19.3.9 网络设定

部分教程中提到使用 qjail config -k jailname 启用 raw_sockets 功能以实现外网访问,这纯属误解。raw_sockets 仅为 ping 等工具所需,并非网络访问的必要条件。在 Jail 中启用 raw_sockets 存在安全风险,这是 Jail 环境默认的安全设计。除非确实需要在 Jail 中使用 ping 等工具,不建议启用 raw_sockets 功能。

Jail 尚无法连接网络,它绑定在 lo1 网络接口上,lo1 并不能直接访问外网,需借助 PF 配置网络,其中 em0 为外网接口。使用 ifconfig 命令可查找系统中的外网接口名称。

/etc/pf.conf 文件中写入配置。网络地址转换(NAT)可使 Jail 访问外部网络,端口重定向可使外部网络访问指定 Jail 的服务:

ini
rdr pass on em0 inet proto tcp from any to em0 port 22 -> 192.168.1.1 port 22 # 端口重定向:将 em0 上 22 端口的 TCP 连接转发到 192.168.1.1(jail1)的 22 端口
nat pass on em0 inet from lo1 to any -> em0

启动防火墙服务:

sh
# service pf enable
# service pf start

此时,绑定在 lo1 上的 jail 可以访问外部网络,外部网络可以通过宿主机的 22 号端口连接 jail1 的 22 号端口。

19.3.10 示例:部署 PostgreSQL Jail

已按前述步骤预留 Jail IP 并成功运行 qjail install 命令后,以 PostgreSQL 15 为例进行部署,其他版本也适用。

19.3.10.1 宿主机中操作

创建并配置 PostgreSQL Jail:

创建 jail postgres,绑定到 lo1 接口,IPv4 地址为 192.168.1.3

sh
# qjail create -n lo1 -4 192.168.1.3 postgres

配置 postgres jail,启用 SysV IPC:

sh
# qjail config -y postgres

启动 postgres jail:

sh
# qjail start postgres

编辑 /etc/pf.conf 文件:

ini
nat pass on em0 inet from lo1 to any -> em0
rdr pass on em0 inet proto tcp from any to em0 port 5432 -> 192.168.1.3 port 5432

注意

直接向外提供 PostgreSQL 连接存在安全风险,应根据实际需要谨慎开启端口转发。

启动防火墙服务 PF:

sh
# service pf start

进入 jail postgres 的控制台:

sh
# qjail console postgres

19.3.10.2 Jail 控制台中的操作

以下命令均在 Jail 控制台下运行,pkg 安装时可根据需要使用镜像。如果使用镜像,可在 Jail 控制台中以和宿主机相同的方式进行设置,详见相关章节。

19.3.10.2.1 配置 PostgreSQL 数据集

安装 PostgreSQL 的过程略去,详见本书其他章节。

设置 PostgreSQL 服务开机自启动:

sh
# service postgresql enable

创建 PostgreSQL 数据目录(注意版本号):

sh
# mkdir -p -m 0700 /var/db/postgres/data15

设置数据目录属主为 postgres 用户:

sh
# chown postgres:postgres /var/db/postgres/data15

切换到 postgres 用户:

sh
# su postgres

初始化 PostgreSQL 数据库:

sh
$ initdb -A scram-sha-256 -E UTF8 -W -D /var/db/postgres/data15

此处使用 initdb 而非安装时提示的 /usr/local/etc/rc.d/postgresql initdb,目的是避免在设置数据库密码时反复修改 pg_hba.conf 文件。对各选项简要说明如下:

  • -A 为本地用户指定在 pg_hba.conf 中使用的默认认证方法
  • -E 选择模板数据库的编码
  • -W 使 initdb 提示为数据库超级用户设置口令
  • -D 指定数据库集簇应该存放的目录

回到 jail root 用户:

sh
$ exit

立即启动 PostgreSQL 服务:

sh
# service postgresql start

如在上述过程中未使用 qjail config -y postgres 命令开启 SysV IPC,可能会出现如下错误:

19.3.10.2.2 初始化数据库集簇时出错

初始化错误

启动 PostgreSQL 时出现的错误。

启动错误

此时在宿主机控制台下执行 qjail config -y postgres 即可修正错误,具体如下:

停止 postgres jail:

sh
# qjail stop postgres

配置 postgres jail,启用 SysV IPC:

sh
# qjail config -y postgres

启动 postgres jail:

sh
# qjail start postgres

再次进入 Jail 的控制台后,即可正常初始化数据库集簇并运行 PostgreSQL 服务。

19.3.11 参考文献