> 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/ask/flat/chapter-32-jail-container-management/di-32.2-jie-hou-jail-thick-jail.md).

# 32.2 Thick Jail

## Configuring Services

Edit the **/etc/rc.conf** file with a text editor and add the following:

```ini
jail_enable="YES"	# Start the Jail service
jail_parallel_start="YES" # Start Jail services in parallel
```

`jail_parallel_start="YES"` enables parallel Jail startup; by default Jails start sequentially, and setting this option causes all containers to start in parallel.

Network configuration (`cloned_interfaces`, `bridge0`, `epair0`, etc.) will be set up in the "Configuring Host Network" section below.

Run the following command to install the base system for the Jail. It is recommended to use `base.txz` extraction for installation to save compilation time. Note that the Jail's **user space version must not be newer than the host** (the FreeBSD kernel can be backward compatible with older user space versions), and it is recommended to keep it consistent with the host. FreeBSD's kernel and user space form a whole (the base system, also known as world), and inconsistent user space may cause compatibility issues.

## Obtaining the Base System

It is not recommended to extract the base system directly into **/usr/jail**. Instead, create subdirectories within it and place each container in its own directory for easier management. Note that the extraction and compilation methods are **alternatives** — choose only one, and do not install twice.

### Extraction Installation Method

```sh
# Create Jail directory
# mkdir -p /usr/jail/myjail
# Fetch the base system (FreeBSD 15 Release)
# fetch https://download.freebsd.org/releases/amd64/amd64/15.0-RELEASE/base.txz
# Extract files to the container directory
# tar -xf base.txz -C /usr/jail/myjail
```

The compilation method can also be used for installation.

### Compilation Installation Method

```sh
# Enter the source directory
# cd /usr/src
# Build world (compile the base system)
# make buildworld
# Install the compiled system to the specified Jail directory
# make installworld DESTDIR=/usr/jail/myjail
# Generate the accompanying configuration files
# make distribution DESTDIR=/usr/jail/myjail
```

## Configuring Time

Copy the host's timezone and DNS configuration to ensure proper networking and time inside the Jail:

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

## Configuring Host Network

> **Warning**
>
> The following network configuration operations (modifying bridges, physical NIC IP, restarting network services, etc.) **must be performed on the local console**. Do not perform them remotely via SSH. If executed via SSH, network interruption during the operation will cause the host to become unreachable, making it impossible to complete the configuration. Ensure proper data backup and contingency measures.

First, **configure the host network**. Create a bridge `bridge0` and a virtual Ethernet pair `epair` for use by VNET (Virtualized Network Stack), then attach the virtual network interface `epair0a` to the bridge. Edit the **/etc/rc.conf** file:

```ini
# Create bridge and a pair of epair virtual NICs
cloned_interfaces="bridge0 epair0"
# Add physical NIC re0 and virtual NIC host-side epair0a to the bridge and bring them up
# Migrate the IP configuration from the original ifconfig_re0 to bridge0, and comment out or remove the original ifconfig_re0 IP configuration
# Otherwise, after adding the physical NIC to the bridge, the host will lose network connectivity
ifconfig_bridge0="inet 192.168.1.100/24 addm re0 addm epair0a up"
# Ensure epair0a is in the up state
ifconfig_epair0a="up"
```

`bridge0` is a virtual switch (Network Bridge), and `epair0a` is a port on the virtual switch.

It can be thought of as a virtual switch with an invisible network cable plugged into it. The RJ45 connector is plugged into the switch. The bridge-side port of the cable is called `epair0a`, and the container-side port is called `epair0b`.

`epair` is a fixed prefix assigned by the kernel, followed by a number representing the virtual connection pair index.

To rename a network interface, run the command `ifconfig epair0a name newname`. This is not recommended to avoid name confusion.

The Jail VNET network topology is as follows:

```sh
Physical Network (example home router 192.168.1.1)
       │
┌──────▼────────────────────────────────────────┐
│ Host                                          │
│                                               │
│  [ Physical NIC re0 ]                         │
│         │                                     │
│  [ Virtual Switch bridge0 ] <───(bridged)     │
│         │                                     │
│  [ Virtual NIC end epair0a ]                  │
└─────────┼─────────────────────────────────────┘
          │ (virtual cable)
┌─────────┼─────────────────────────────────────┐
│  [ Virtual NIC end epair0b ]                  │
│                                               │
│ Jail (192.168.1.120)                        │
└───────────────────────────────────────────────┘
```

After configuring the network, restart the network service:

```sh
# service netif restart
```

It is not recommended to execute the above command on a remote host. Ensure proper data backup and contingency measures.

## Configuring Container Network

Next, **configure the container network**. Create the Jail configuration file **/etc/jail.conf** (create it if it does not exist), which applies to all containers. A reference configuration is as follows:

```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";
}
```

The first three lines of the configuration allow the use of System V IPC (Inter-Process Communication) mechanisms inside the Jail, including shared memory, semaphores, and message queues;

`exec.clean` ensures that commands like `exec.start` run in a clean environment when the container starts, rather than inheriting the environment variables from when the Jail was started; environment variables are discarded, retaining only the following:

| Variable | Setting Method                              |
| -------- | ------------------------------------------- |
| `HOME`   | Set to the default of the target login user |
| `SHELL`  | Set to the default of the target login user |
| `TERM`   | Imported from the current environment       |
| `USER`   | Set to the target login username            |
| `PATH`   | Set to `/bin:/usr/bin`                      |

Additionally, environment variables from the capability database of the login class to which the target login user belongs will also be set; `JID`, `JNAME`, and `JPATH` will not be set.

`mount.devfs` is used to mount the devfs file system in the container's `/dev` directory and apply the default ruleset to restrict the device nodes visible inside the container.

The final `myjail {...}` block creates the Jail example `myjail`:

The first line sets the container hostname;

The second line sets the container path;

`vnet;` enables the VNET virtual network stack;

`vnet.interface` sets the container network interface;

`allow.raw_sockets = 1;` allows the container to create raw sockets, enabling commands like `ping` to work properly; FreeBSD Jail's default security policy prohibits containers from directly creating raw sockets, and the `ping` command requires Raw Sockets to send ICMP packets. Without this configuration, running `ping` will result in `Operation not permitted`.

The last three lines define the container's behavior on startup and shutdown;

`exec.created` sets the `epair0b` interface to the up state in the host environment. This command is executed before `vnet.interface` moves the interface into the Jail, so the interface is still in the host network stack. After the interface is moved into the Jail, it retains the up state, ensuring that the network configuration inside the Jail works properly.

Set a static IP address in the container's **/etc/rc.conf**, with the protocol family set to `inet` (IPv4), the IP address set to **192.168.1.120**, the subnet mask set to **255.255.255.0**, and the default gateway set to **192.168.1.1**. Edit the configuration file inside the container: **/usr/jail/myjail/etc/rc.conf**, and add the following:

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

## Starting the Container

After configuration is complete, start it using the `service` command:

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

After starting, use the `jls` command to check. If the container was created successfully and is running, it will appear in the list.

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

In VNET mode, the IP address is managed internally by the container, so it is normal for the Address field to not show an IP.

Common management commands are as follows:

| Command                       | Description                      |
| ----------------------------- | -------------------------------- |
| `service jail start myjail`   | Start container `myjail`         |
| `service jail restart myjail` | Restart the container            |
| `service jail stop myjail`    | Stop the container               |
| `jls`                         | List running containers          |
| `jexec myjail sh`             | Enter the container command line |

Run `jexec myjail sh` to enter the container command line (this operation requires root privileges). Upon success, the command prompt `#` will appear, and commands can be entered to operate the container.

If raw socket creation has been enabled as described earlier, use `ping example.com` to confirm internet connectivity:

```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
```

## Testing the Container

First, install the package manager by running the `pkg` command. When prompted with `Do you want to fetch and install it now? [y/N]:`, select `y`.

If the installation is successful, output similar to the following will be displayed:

```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'.
```

If the network is restricted and installation fails, refer to earlier chapters of this book to change the software mirror, or install using Ports.

After successful installation, run `pkg update -f` to force-refresh the package repository catalog, then run `pkg ins nginx`, and run `service nginx onestart` to start `nginx` once. Upon success, the following output will be displayed:

```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.
```

Use the `ifconfig` command to confirm the IP address. If all steps in this section were followed exactly, the address should be **192.168.1.120**.

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

Press **Ctrl**+**D** or type `exit` to exit the Jail container's command line.

Return to the host environment and run `fetch -o - http://192.168.1.120 | grep Welcome` to check whether NGINX is working properly.

If the container network is functioning and Nginx is working properly, the following output will be displayed:

```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:

```
GET https://book.bsdcn.org/ask/flat/chapter-32-jail-container-management/di-32.2-jie-hou-jail-thick-jail.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
