> For the complete documentation index, see [llms.txt](https://book.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://book.bsdcn.org/di-30-zhang-jail-rong-qi-guan-li/di-30.2-jie-hou-jail-thick-jail.md).

# 30.2 厚 Jail（Thick Jail）

## 编辑服务

使用文本编辑器编辑 **/etc/rc.conf** 文件，加入以下内容：

```ini
jail_enable="YES"	# 启动 Jail 服务
jail_parallel_start="YES" # 并行启动 Jail 服务
```

`jail_parallel_start="YES"` 启用 Jail 并行启动；默认情况下 Jail 串行启动，设置此选项后所有容器将并行启动。

网络配置（`cloned_interfaces`、`bridge0`、`epair0` 等）将在下文“配置宿主机网络”小节中统一设置。

运行以下命令为 Jail 安装基本系统，建议使用 `base.txz` 解压安装，省去编译的时间。注意 Jail 的 **用户空间版本不能比宿主机更新**（FreeBSD 内核可向后兼容旧版本的用户空间），建议与宿主机保持一致。FreeBSD 的内核和用户空间是一个整体（基本系统，又称世界），用户空间不一致可能导致兼容性问题。

## 获取基本系统

不建议将基本系统解压到 **/usr/jail** 中。应在其中创建子目录，将每个容器都单独放在其中的目录里面，如此方便管理。注意下载解压和编译两种方法 **任选一种方法** 进行安装即可，切勿重复安装。

### 解压安装方式

```sh
# 创建 Jail 目录
# mkdir -p /usr/jail/myjail
# 获取基本系统 (FreeBSD 15 Release)
# fetch https://download.freebsd.org/releases/amd64/amd64/15.0-RELEASE/base.txz
# 解压文件到容器目录
# tar -xf base.txz -C /usr/jail/myjail
```

也可通过编译方法安装。

### 编译安装方式

```sh
# 进入源码目录
# cd /usr/src
# 构建世界（编译基础系统）
# make buildworld
# 将编译好的系统安装到指定 Jail 目录
# make installworld DESTDIR=/usr/jail/myjail
# 生成配套的配置文件
# make distribution DESTDIR=/usr/jail/myjail
```

## 配置时间

沿用宿主机的时区和 DNS 配置，确保 Jail 内部网络和时间正常：

```sh
# cp /etc/resolv.conf /usr/jail/myjail/etc/
# cp /etc/localtime /usr/jail/myjail/etc/
```

## 配置宿主机网络

> **警告**
>
> **必须在本地控制台执行** 以下网络配置操作（修改网桥、物理网卡 IP、重启网络服务等），禁止通过 SSH 远程操作。若通过 SSH 执行，操作过程中网络中断将导致宿主机失联，无法继续完成配置。请做好数据备份和应急手段。

首先 **配置宿主机网络**。创建网桥 `bridge0` 和虚拟以太网对 `epair`，供 VNET（Virtualized Network Stack 虚拟网络栈）使用，然后将虚拟网卡 `epair0a` 挂载到网桥。编辑 **/etc/rc.conf** 文件：

```ini
# 创建网桥和一对 epair 虚拟网卡
cloned_interfaces="bridge0 epair0"
# 把物理网卡 re0 和虚拟网卡宿主机端 epair0a 都加入网桥并启动
# 将原 ifconfig_re0 中的 IP 配置迁移到 bridge0 上，同时注释掉或删除 ifconfig_re0 的原有 IP 配置
# 否则将物理网卡加入网桥后，宿主机会失去网络连通性
ifconfig_bridge0="inet 192.168.1.100/24 addm re0 addm epair0a up"
# 确保 epair0a 处于启动状态
ifconfig_epair0a="up"
```

`bridge0` 是虚拟交换机 (Network Bridge) ， `epair0a` 是虚拟交换机上的一个端口。

可将之视为一个虚拟交换机，其上插着一根无形的网线。水晶头插在交换机上。网线网桥侧端口称为 `epair0a`，容器侧称为 `epair0b`。

`epair` 是由内核指定的固定前缀，后面接着代表第几对虚拟连接的数字。

若要更改网卡名称，可执行命令 `ifconfig epair0a name newname` 更改。为了避免名称混淆和混乱，不建议如此。

Jail VNET 网络拓扑结构如下：

```sh
物理网络 (示例家用路由器 192.168.1.1)
       │
┌──────▼────────────────────────────────────────┐
│ 宿主机 (Host)                                 │
│                                               │
│  [ 物理网卡 re0 ]                             │
│         │                                     │
│  [ 虚拟交换机 bridge0 ] <───(桥接)            │
│         │                                     │
│  [ 虚拟网卡端 epair0a ]                       │
└─────────┼─────────────────────────────────────┘
          │ (虚拟网线)
┌─────────┼─────────────────────────────────────┐
│  [ 虚拟网卡端 epair0b ]                       │
│                                               │
│ Jail (192.168.1.120)                        │
└───────────────────────────────────────────────┘
```

配置好网络以后重启网络服务：

```sh
# service netif restart
```

不建议在远程主机执行上述命令。请做好数据备份和应急手段。

## 配置容器网络

接下来 **配置容器网络**，编写 Jail 配置文件 **/etc/jail.conf**（若不存在，请新建），对所有的容器生效，参考配置如下：

```ini
sysvmsg=new;
sysvsem=new;
sysvshm=new;

exec.clean;
mount.devfs;

myjail {
    host.hostname = "myjail.local";
    path = "/usr/jail/myjail";
    vnet;
    vnet.interface = "epair0b";
    allow.raw_sockets = 1;
    exec.created = "ifconfig epair0b up";
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
}
```

配置中前三行允许在 Jail 内部使用 System V IPC（进程间通信）机制，包括共享内存、信号量和消息队列；

`exec.clean` 用于在容器启动时，让 `exec.start` 等命令在干净的环境中运行，而非沿用启动 Jail 时的环境变量；环境变量会被丢弃，仅保留以下变量：

| 变量      | 设置方式               |
| ------- | ------------------ |
| `HOME`  | 设为目标登录用户的默认值       |
| `SHELL` | 设为目标登录用户的默认值       |
| `TERM`  | 从当前环境中导入           |
| `USER`  | 设为目标登录用户名          |
| `PATH`  | 设为 `/bin:/usr/bin` |

此外，目标登录用户所属登录分级的能力数据库中的环境变量也会被设置，`JID`、`JNAME`、`JPATH` 不会被设置。

`mount.devfs` 用于在容器的 **/dev** 目录挂载 devfs 文件系统，并应用默认规则集限制容器内可见的设备节点。

最后的 `myjail {...}` 用于创建 Jail 示例 `myjail`：

第一行设置容器主机名；

第二行设置容器路径；

`vnet;` 启用 VNET 虚拟网络栈；

`vnet.interface` 设置容器网卡；

`allow.raw_sockets = 1;` 允许容器创建原始套接字，使 `ping` 等命令能够正常工作；FreeBSD Jail 默认的安全策略禁止容器直接创建原始套接字，而 `ping` 命令需要利用 Raw Socket 来发送 ICMP 报文。若未添加此配置，执行 `ping` 将提示 `Operation not permitted`。

最后三行设定容器启动和停止时的行为；

`exec.created` 在宿主机环境中将 `epair0b` 接口设为 up 状态。此命令在 `vnet.interface` 将接口移入 Jail 之前执行，因此接口仍位于宿主机网络栈中。接口移入 Jail 后会保留 up 状态，确保 Jail 内的网络配置能够正常工作。

在容器内的 **/etc/rc.conf** 中设置静态 IP 地址，协议族设定为 `inet` (IPv4) ，将 IP 设定为 **192.168.1.120**，子网掩码设为 **255.255.255.0**，设置默认网关为 **192.168.1.1**，编辑容器内配置文件：**/usr/jail/myjail/etc/rc.conf**，添加如下内容：

```ini
ifconfig_epair0b="inet 192.168.1.120 netmask 255.255.255.0"
defaultrouter="192.168.1.1"
```

## 启动容器

配置完成以后使用 `service` 命令启动：

```sh
# service jail start myjail
```

启动以后使用 `jls` 命令查看，若容器创建成功，且正在运行，该容器将出现在列表中。

```sh
$ jls
   JID  IP Address      Hostname                      Path
     1                  myjail.local                  /usr/jail/myjail
```

VNET 模式中，IP 地址由容器内部管理，Address 字段看不到 IP 是正常的。

常用管理命令如下：

| 命令                            | 解释            |
| ----------------------------- | ------------- |
| `service jail start myjail`   | 启动容器 `myjail` |
| `service jail restart myjail` | 重启容器          |
| `service jail stop myjail`    | 停止容器          |
| `jls`                         | 查看运行中的容器列表    |
| `jexec myjail sh`             | 进入容器命令行       |

执行 `jexec myjail sh` 进入容器命令行（此操作需要 Root 权限），成功后将看到命令提示符 `#`，此时可输入命令操作容器。

若已按照前面的方法允许容器创建原始套接字，可使用 `ping example.com` 确认已经连接到互联网：

```sh
# ping example.com
PING example.com (104.20.23.154): 56 data bytes
64 bytes from 104.20.23.154: icmp_seq=1 ttl=47 time=95.063 ms
64 bytes from 104.20.23.154: icmp_seq=3 ttl=47 time=94.726 ms
^C
--- example.com ping statistics ---
4 packets transmitted, 2 packets received, 50.0% packet loss
round-trip min/avg/max/stddev = 94.726/94.895/95.063/0.169 ms
```

## 测试容器

首先安装包管理器，执行 `pkg` 命令，遇到提示 `Do you want to fetch and install it now? [y/N]:` 选择 `y`。

若安装成功，将得到类似输出：

```sh
Bootstrapping pkg from pkg+https://pkg.FreeBSD.org/FreeBSD:15:amd64/quarterly, please wait...
Verifying signature with trusted certificate pkg.freebsd.org.2013102301... done
[myjail.local] Installing pkg-2.6.2_1...
[myjail.local] Extracting pkg-2.6.2_1: 100%
pkg: not enough arguments
Usage: pkg [-v] [-d] [-l] [-N] [-j <jail name or id>|-c <chroot path>|-r <rootdir>] [-C <configuration file>] [-R <repo config dir>] [-o var=value] [-4|-6] <command> [<args>]

For more information on available commands and options see 'pkg help'.
```

若网络受限无法安装，可按照本书前面章节方法更换软件源，或者使用 Ports 安装。

安装成功后，可执行 `pkg update -f` 强制重新获取软件仓库目录，然后执行 `pkg ins nginx`，执行 `service nginx onestart` 单次启动 `nginx`，成功后将得到如下输出：

```sh
# service nginx onestart
Performing sanity check on nginx configuration:
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
Starting nginx.
```

使用 `ifconfig` 命令确认 IP 地址，若完全按本节步骤进行，地址应为 **192.168.1.120**。

```sh
# ifconfig | grep 192
	inet 192.168.1.120 netmask 0xffffff00 broadcast 192.168.1.255
```

按 **Ctrl**+**D** 或输入 `exit` 可退出 Jail 容器的命令行。

回到宿主机环境，执行 `fetch -o - http://192.168.1.120 | grep Welcome` 可查看 NGINX 是否正常工作。

若容器网络通畅，且 Nginx 正常工作，将得到如下输出：

```sh
$ fetch -o - http://192.168.1.120 | grep Welcome
-                                                      896  B 5646 kBps    00s
<title>Welcome to nginx!</title>
<h1>Welcome to nginx!</h1>
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://book.bsdcn.org/di-30-zhang-jail-rong-qi-guan-li/di-30.2-jie-hou-jail-thick-jail.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
