{"code":200,"data":{"id":"hhqqk96cbo","slug":"freebsd-wrapped-jails","appearance":"norm","language":"en","rtl":false,"created":"2026-08-22T21:31:51Z","updated":"2026-08-25T19:24:00Z","title":"FreeBSD: Wrapped jails","body":"As my first post here, I'll try to describe, and perhaps rationalise, how I prefer to run most of my jails.\n\n## Background\nIn case anyone reading this doesn't already know, [FreeBSD](https://en.wikipedia.org/wiki/FreeBSD) is a [Unix](https://en.wikipedia.org/wiki/Unix)-like operating system tracing its heritage back to the original [Berkeley Software Distribution](https://en.wikipedia.org/wiki/Berkeley_Software_Distribution) (BSD) from the late 70s. Fast forward to the early '00s and the release of FreeBSD 4.0, and we got a new toy to play with: [Jails](https://en.wikipedia.org/wiki/FreeBSD_jail).\n\nIn many ways, jails were the precursor and at least partially the inspiration for several other implementations of [OS-level virtualization](https://en.wikipedia.org/wiki/OS-level_virtualization), with [Solaris](https://en.wikipedia.org/wiki/Solaris_Containers) and [Docker](https://en.wikipedia.org/wiki/Docker_(software)) being fairly well-known examples. While the implementations differ significantly, the basic idea is the same: Virtualize OS services and resources, but not the hardware, thus achieveing much more frugal virtualization than full-blown VMs.\n\n## Basics\nHistorically, FreeBSD jails were quite simple in their implementation: Aside from separating process spaces (grouping processes and preventing a jail from seeing processes in other jails or on the host) and a rudimentary IP address delegation mechanism, it was essentially (though perhaps not technically) an extension of the [chroot](https://en.wikipedia.org/wiki/Chroot) mechanism that was first introduced in BSD as early as 1979 - admittedly in a much different shape than today.\n\nToday, this has changed quite a bit, with perhaps the most interesting change being the introduction of [VIMAGE](https://wiki.freebsd.org/VIMAGE), a framework for virtualizing kernel subsystems, and the [VNET](https://docs.freebsd.org/en/books/handbook/jails/#creating-vnet-jail) facility built using VIMAGE, which provides a separate, virtualized network stack to jails.\n\nAdd some other killer FreeBSD- and jail-specific features like [ZFS](https://docs.freebsd.org/en/books/handbook/zfs/) and [hierarchical, or nested, jails](https://docs.freebsd.org/en/books/handbook/jails/#hierarchical-jails), the [PF firewall](https://docs.freebsd.org/en/books/handbook/firewalls/#firewalls-pf), and you have the building blocks for some really interesting things!\n\n## The problem with classic jails\nClassic jails are nice because the lack of a network stack can be considered a security feature: The processes in the jail, even those running as root, are completely isolated from touching the network stack, which means they cannot do anything useful with `ifconfig(8)`, can't modify routing tables, and are generally strictly limited to the IP address(es) you assign to the jail.\n\nUnfortunately, this comes with some drawbacks: Running `ifconfig` in the jail will reveal much about the network configuration on the host: It will basically give you the full output for the interface(s) on which the jail has IPs, minus the other IPs that are defined there. Consider the following:\n\n```\n$  ifconfig                                                                                                             \nix0: flags=1008943\u003cUP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\noptions=a538b9\u003cRXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,WOL_UCAST,WOL_MCAST,WOL_MAGIC,VLAN_HWFILTER,VLAN_HWTSO,RXCSUM_IPV6,HWSTATS\u003e\n        ether ac:1f:6b:62:17:1c\n        media: Ethernet autoselect (10Gbase-T \u003cfull-duplex\u003e)\n        status: active\nix1: flags=8802\u003cBROADCAST,SIMPLEX,MULTICAST\u003e metric 0 mtu 1500\noptions=4e53fbb\u003cRXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,JUMBO_MTU,VLAN_HWCSUM,TSO4,TSO6,LRO,WOL_UCAST,WOL_MCAST,WOL_MAGIC,VLAN_HWFILTER,VLAN_HWTSO,RXCSUM_IPV6,TXCSUM_IPV6,HWSTATS,MEXTPG\u003e\n        ether ac:1f:6b:62:17:1d\n        media: Ethernet autoselect\n        status: no carrier\nlo0: flags=1008049\u003cUP,LOOPBACK,RUNNING,MULTICAST,LOWER_UP\u003e metric 0 mtu 16384\noptions=680003\u003cRXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6\u003e\n        groups: lo\npflog0: flags=0 metric 0 mtu 33152\n        options=0\n        groups: pflog\nngeth0: flags=1008943\u003cUP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\n        options=28\u003cVLAN_MTU,JUMBO_MTU\u003e\n        ether 58:9c:fc:10:64:f9\n        media: Ethernet autoselect (1000baseT \u003cfull-duplex\u003e)\n        status: active\nbridge0: flags=1008843\u003cUP,BROADCAST,RUNNING,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\n        options=10\u003cVLAN_HWTAGGING\u003e\n        ether ac:1f:6b:62:17:1c\n        inet 10.1.2.3 netmask 0xffffff00 broadcast 10.1.2.255\n        id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15\n        maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200\n        root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0\n        bridge flags=0\u003c\u003e\n        member: epair2a flags=143\u003cLEARNING,DISCOVER,AUTOEDGE,AUTOPTP\u003e\n                port 15 priority 128 path cost 2000 vlan protocol 802.1q\n        member: epair1a flags=143\u003cLEARNING,DISCOVER,AUTOEDGE,AUTOPTP\u003e\n                port 11 priority 128 path cost 2000 vlan protocol 802.1q\n        member: epair0a flags=143\u003cLEARNING,DISCOVER,AUTOEDGE,AUTOPTP\u003e\n                port 7 priority 128 path cost 2000 vlan protocol 802.1q\n        member: ngeth0 flags=143\u003cLEARNING,DISCOVER,AUTOEDGE,AUTOPTP\u003e\n                port 5 priority 128 path cost 20000 vlan protocol 802.1q\n        member: ix0 flags=143\u003cLEARNING,DISCOVER,AUTOEDGE,AUTOPTP\u003e\n                port 1 priority 128 path cost 2000 vlan protocol 802.1q\n        groups: bridge\nepair0a: flags=1008943\u003cUP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\n        description: jail1\n        options=200019\u003cRXCSUM,VLAN_MTU,VLAN_HWTAGGING,RXCSUM_IPV6\u003e\n        ether 58:9c:fc:10:e6:88\n        groups: epair\n        media: Ethernet 10Gbase-T (10Gbase-T \u003cfull-duplex\u003e)\n        status: active\nepair1a: flags=1008943\u003cUP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\n        description: jail2\n        options=200019\u003cRXCSUM,VLAN_MTU,VLAN_HWTAGGING,RXCSUM_IPV6\u003e\n        ether 58:9c:fc:10:98:28\n        groups: epair\n        media: Ethernet 10Gbase-T (10Gbase-T \u003cfull-duplex\u003e)\n        status: active\nepair2a: flags=1008943\u003cUP,BROADCAST,RUNNING,PROMISC,SIMPLEX,MULTICAST,LOWER_UP\u003e metric 0 mtu 1500\n        description: jail3\n        options=200019\u003cRXCSUM,VLAN_MTU,VLAN_HWTAGGING,RXCSUM_IPV6\u003e\n        ether 58:9c:fc:10:22:09\n        groups: epair\n        media: Ethernet 10Gbase-T (10Gbase-T \u003cfull-duplex\u003e)\n        status: active\n```\nThis `ifconfig` invocation is from within a *classic* jail running on the same host as a number of VNET jails. Despite not being able to modify anything, I can still see a lot of detail about the host that, one would think, the jail should not be privy to: All the physical and virtual interfaces on the machine, including `lo0` (localhost), the `bridge0` interface and all the bridge members, the primary interface `ix0` and its twin `ix1`, the `pflog0` interface revealing we've got `pf` running, `ngeth0` showing there's `netgraph(4)` in use, etc.\n\nAnd perhaps worst of all: The individual `epair` interfaces for the VNET jails, including their descriptions which - in a well-assembled system - could give away details about what and who else exists on the host.\n\nClearly, this is not the prison I was hoping for.\n\n## The problem with VNET jails\nQuite the opposite of classic jails, VNET jails provide a virtualised network stack. This can be connected to the outside world with a virtual cable, by way of the `epair` interface type, connecting it to a bridge interface on the host. Or you can assign separate physical interfaces, VLAN interfaces, or practically any other interface type. Either way, the jail controls that interface in practically every way, in addition to the routing table (or tables), packet filtering, and most other aspects of the network stack.\n\nThis is obviously very powerful, and provides some features that classic jails are (sorely?) missing:\n\n* An actual localhost interface! Classic jails don't have this, there are just some code shortcuts in the kernel that allow applications that bind to localhost to .. not fail, but there are several situations where this is far from optimal or even desired.\n* The ability to run stuff like `nmap(1)` from the jail, since it fiddles with routing tables and creates raw network packets.\n* Firewalling with `pf(4)` (or possibly others; I have not played with those). This includes basic filtering, NAT, rate limiting, and all the other goodness offered.\n* Several network-related `sysctl(8)` settings can be tuned on a per-jail basis, like datagram sizes, routing settings, TCP congestion control algorithm, etc.\n\nHowever: Do you really want all the processes in your jail to be this close to all those foot cannons? A simple misconfiguration, local privilege escalation bug, or simply fat-fingering something yourself can wreck havoc on the jail's network configuration - and there is a definite possibility of inflicting damage on other jails, the host, and even other devices on your network.\n\nThe limitations of a classic jail suddenly start looking an awful lot like features, don't they?\n\n## A possible solution\nWithout more ado (there's enough of that above), here's how we've solved this at work: We \"wrap\" a classic jail inside a VNET jail.\n\nHere's how:\n\n### 1. Create the jail template\nThe easiest way is to fetch `base.txz` from the FreeBSD download servers, and estract it in a suitable directory:\n\n```sh\n# Create template directory. If you use ZFS, a dataset might be preferable:\nmkdir -p /data/jails/template.151\n\n# Fetch and extract\nfetch -o - https://download.freebsd.org/ftp/releases/amd64/15.1-RELEASE/base.txz \\\n    | tar --unlink -xpzf - -C /data/jails/template.151\n\n# Create a symlink for use in `fstab(5)`\nln -s /data/jails/template.151 /data/jails/current\n```\n\n### 2. Create `fstab` files\nFor the parent and child jails, create `fstab(5)` files to `nullfs(4)`-mount the parts of the base you need into the jails. An added benefit is that this makes the base OS read-only inside the jails, which is probably a good thing:\n\n```\n# /etc/fstab.testjail.parent\n/data/jails/current/etc /data/jails/testjail/etc nullfs ro\n/data/jails/current/bin /data/jails/testjail/bin nullfs ro\n/data/jails/current/lib /data/jails/testjail/lib nullfs ro\n/data/jails/current/sbin /data/jails/testjail/sbin nullfs ro\n/data/jails/current/libexec /data/jails/testjail/libexec nullfs ro\n/data/jails/current/usr/sbin /data/jails/testjail/usr/sbin nullfs ro\n```\n\n```\n# /etc/fstab.testjail\n/data/jails/current/bin /data/jails/testjail/root/bin nullfs ro\n/data/jails/current/lib /data/jails/testjail/root/lib nullfs ro\n/data/jails/current/usr/share /data/jails/testjail/root/usr/share nullfs ro\n/data/jails/current/libexec /data/jails/testjail/root/libexec nullfs ro\n/data/jails/current/sbin /data/jails/testjail/root/sbin nullfs ro\n/data/jails/current/usr/bin /data/jails/testjail/root/usr/bin nullfs ro\n/data/jails/current/usr/include /data/jails/testjail/root/usr/include nullfs ro\n/data/jails/current/usr/lib /data/jails/testjail/root/usr/lib nullfs ro\n/data/jails/current/usr/libdata /data/jails/testjail/root/usr/libdata nullfs ro\n/data/jails/current/usr/libexec /data/jails/testjail/root/usr/libexec nullfs ro\n/data/jails/current/usr/sbin /data/jails/testjail/root/usr/sbin nullfs ro\n```\n\n### 3. Create the directory structures for the jails\nThis is all greatly simplified, but it should give you a starting point. Note how the child jail lives in a subdirectory (`/root`) inside the parent jail:\n\n```sh\n# Create the parent jail directory structure\nfor dir in \\\n    root dev etc bin lib sbin libexec \\\n    usr/sbin /var/log /var/run /var/empty\ndo\n    mkdir -p /data/jails/testjail/$dir\ndone\n\n# Copy some files into the parent jail; necessary for pf to work\ncp /data/jails/template.151/etc/pf.os /data/jails/testjail/etc/\ncp /data/jails/template.151/etc/protocols /data/jails/testjail/etc/\n\n# Create the child jail directory structure\nfor dir in \\\n    root dev bin lib sbin libexec tmp \\\n    usr/sbin usr/bin usr/include usr/lib usr/libdata \\\n    usr/libexec usr/share /usr/local/etc\ndo\n    mkdir -p /data/jails/testjail/root/$dir\ndone\n\n# Copy basic /etc and /var to the child jail\ncp -Rp /data/jails/template.151/etc /data/jails/testjail/root/\ncp -Rp /data/jails/template.151/var /data/jails/testjail/root/\n```\n\n#### Using ZFS instead\nThe `mkdir` parts for the child jail could be modified to use ZFS (which is what I'd do). This is a skeleton suggestion, but you'd do well in forming your own strategy for how to approach this with ZFS. Personally I see no point using ZFS datasets for the parent jail, as it is largely ephemeral anyway:\n\n```sh\n# Create the child jail datasets (zfs 'create -p' acts like 'mkdir -p')\nfor dir in \\\n    root usr/local usr/home tmp var etc\ndo\n    zfs create -p data/jails/testjail/root/$dir\ndone\n\n# Create directories in the datasets\nfor dir in \\\n    dev bin lib sbin libexec \\\n    usr/sbin usr/bin usr/include usr/lib usr/libdata \\\n    usr/libexec usr/share /usr/local/etc\ndo\n    mkdir -p /data/jails/testjail/root/$dir\ndone\n```\n\nOne useful trick I've learned is to keep `/var/log` inside the child jail in a ZFS dataset homed outside the child jail's path, and simply set its mountpoint to be inside the jail. In case you need to roll back the dataset(s) in the child jail, you can safely do so without erasing your logs. Security people and auditors may even come to appreciate this:\n\n```sh\n# Create the dataset; make sure it always has space to write logs even if the\n# rest of the datasets have run out of space:\nzfs create -p \\\n    -o reservation=64M \\\n    -o mountpoint=/data/jails/testjail/root/var/log \\\n    data/jails/testjail/logs\n```\n\n### 4. Start the jails\nWe start by creating the `epair` interfaces. Assuming there is already a `bridge0` to attach the dangling cable^Winterface to:\n```sh\nif_a=\"$(ifconfig epair create)\"\nif_b=\"${if_a%a}b\"\nifconfig bridge0 addm $if_a\nifconfig $if_a up\n```\n\nNext, we start the parent jail. Note this is crude; a complete `jail.conf` entry should take care to unmount filesystems on jail shutdown, avoiding mounting things twice, and various other housekeeping:\n```sh\n# Mount the parent filesystems\nmount -a -F /etc/fstab.testjail.parent\n\n# Start the jail - set children.max=1 to allow the child jail\njail -c path=/data/jails/testjail name=testjail children.max=1 vnet persist\n\n# $if_a and $if_b is from the interface create script\nifconfig $if_b vnet testjail\njexec testjail ifconfig lo0 127.0.0.1/8\njexec testjail ifconfig $if_b 10.2.3.4/24\njexec testjail route add default 10.2.3.1\n\n# Mount devfs in the jails\nmount -t devfs -oruleset=4 . /data/jails/testjail/dev\nmount -t devfs -oruleset=4 . /data/jails/testjail/root/dev\n```\n\nNow that we have the parent jail running, starting the child jail is a matter of\n```sh\n# Mount the child filesystems\nmount -a -F /etc/fstab.testjail.parent\n\n# Start the child jail\njexec testjail jail -c ip4=inherit path=/root name=child exec.start=\"/bin/sh /etc/rc autoboot\"\n```\n\nAt this point you should be able to `jexec testjail.child su -` and find yourself in a bare, but functional, jail. Run `ifconfig` to see that only network interfaces pertaining to `testjail` are visible, and confirm that you cannot make changes. List the jails with `jls`; it should show two jails - the parent and the child.\n\n## Stopping the jails\nSince we're cheating and doing everything from the command line here, stopping the jails takes a few steps:\n\n```sh\n# Stop the child jail by killing all the processes\njexec testjail.child kill -9 -1\n\n# In case there are lingering TCP sockets preventing the child jail from dying,\n# use tcpdrop(8) to clean up:\njexec testjail tcpdrop -a\n\n# Destroy the parent jail\njail -r testjail\n\n# Destroy the epair interfaces (find the name using ifconfig if needed; both\n# the 'a' and 'b' of the pair should now be visible on the host:\nifconfig $if_a destroy\n\n# Finally, unmount the filesystems\numount -f /data/jails/testjail/root/dev\numount -f /data/jails/testjail/dev\numount -F /etc/fstab.testjail -a\numount -F /etc/fstab.testjail.parent -a\n```\n\n## What now?\nAll of the above, while lengthy, is hardly elegant. Some additional steps may be desirable, e.g. to enable `pf`, creating a \"black hole\" around the child jail, and other niceties. A `jail.conf` taking care of these steps should also be created, which will require a helper script or two.\n\n### Enabling the packet filter\nIn order to use `pf`, we need a `pf.conf` file, and some additional device nodes and optionally the pflog interface in the parent jail.\n\nThe `pf.conf` file really only needs a single line for the purpose of this demonstration. We're adding `log` here for `pflog` to have any work to do. Place it in the root of the parent jail, like so:\n\n`echo \"pass log all\" \u003e /data/jails/testjail/pf.conf`\n\nThen we need to unhide a couple of device nodes in the parent jail, load the rules, and start `pflogd`. This should be done after starting the parent jail, but before starting the child jail:\n```sh\ndevfs -m /data/jails/testjail/dev rule apply path pf unhide\ndevfs -m /data/jails/testjail/dev rule apply path bpf unhide\njexec testjail pfctl -F all -vf /pf.conf \u0026\u0026 pfctl -e\n\n# Starting pflogd, excluding some particularly noisy traffic\njexec testjail pflogd 'not host 224.0.0.18'\n```\n\nAt this point, if you plan to keep a configuration like this, you may want to make sure you rotate the `pflogd` files automatically, by adding this to `/usr/local/etc/newsyslog.d/jails.conf` (or anywhere else you prefer):\n`/data/jails/testjail/var/log/pflog root:wheel 640 7 * $D0 B /data/jails/nextcloud/var/run/pflogd.pid`\nAsk me how I know. :-P\n\n### The Black Hole\nOne of the more \"interesting\" possibilities with this approach is that after the child jail has been started, there really is no reason to keep the parent jail around. Of course it needs to exist, but most of the filesystems (i.e binaries and libraries) are really not necessary. While nowhere near an actual black hole (you can still access the child jail's files), it has a non-zero value as an obfuscation.\n\nFollowing startup of the child jail and all the parent jail services (`pflogd`, etc.), you can simply unmount all the parent filesystems:\n`umount -F /etc/fstab.testjail.parent`\n\nHowever, a side effect of this is that you'll no longer be able to modify the running configuration (update `pf` rules, etc.). You can no longer simply `jexec` into the jail - no libraries or binaries are available in their expected locations.\n\nTo get around this, you can use some variation of the following:\n`LD_LIBRARY_PATH=/root/lib jexec testjail /root/libexec/ld-elf.so.1 /root/sbin/pfctl -F all -f /pf.conf`\nThis will set `LD_LIBRARY_PATH` to the child jail's path, and invoke `ld-elf.so.1` directly (with the binary as argument), since it doesn't exist in `/libexec` any longer.\n\nBe aware, however, that doing this will expose you to potentially tainted files in the child jail. An alternate approach would be to mount and unmount the parent filesystems, e.g.\n```sh\nmount -F /etc/fstab.testjail.parent -a\njexec testjail \u003ccommand\u003e\numount -F /etc/fstab.testjail.parent -a\n```\nThen again, since these base OS directories are read-only mounted from the template, this should be a minimal risk. If you're attempting to run programs/libraries that live in read/write-mounted directories in the child jail, however - beware.\n\n## Putting it all together (sort of)\nWhat's missing now is of course the `jail.conf` entry. With this in place, you can use the regular\n`service jail start [\u003cname\u003e]` and\n`service jail stop [\u003cname\u003e]`\ncommands to manage your jails, and you can have them automatically start on system boot.\n\nCorrectly assembling such a configuration is a fairly involved task, but I'll leave you with an example that should help you get started. Good luck!\n\n```sh\n# /etc/jail.conf\n## Some defaults\n# The default devfs ruleset - this is a default /etc/defaults/devfs.rules\ndevfs_ruleset = 4;\n# We want the venerable rc(8) to manage the child jails\nexec.start = \"/bin/sh /etc/rc autoboot\";\n# Set a high securelevel for the child jails (see security(7))\n$child_securelevel = 4;\n# Disallow modifying hostnames after creating jails\nallow.set_hostname = 0;\n# Clean the environment\nexec.clean;\n\n## Our test jail starts here\ntestjail {\n    # Set a variable for use later - a run directory for some housekeeping\n    $rundir = \"/var/run/jails/testjail\";\n\n    # Basic settings for the parent jail\n    host.hostname = \"testjail.PARENT\";\n    path = \"/data/jails/testjail\";\n    vnet;\n    # Setting a high value here, so we can use service jails (see rc.conf(5))\n    children.max = 21;\n\n    # The fstab for the parent, will be automatically mounted on startup. Also\n    # make sure we can mount filesystems inside the parent\n    mount.fstab = \"/etc/fstab.bbs\";\n    allow.mount;\n    allow.mount.devfs;\n    # Allow inspecting ZFS snapshots from within the child jail (optional)\n    zfs.mount_snapshot = 1;\n    # In case you want PostgreSQL or something\n    allow.sysvipc = 1;\n    # Keep a console log for the jail\n    exec.consolelog = \"/var/log/jail_${name}_console.log\";\n\n    # Cleanup before starting the jail (In case a previous attempt failed)\n    exec.prepare = \"umount -f -F /etc/fstab.testjail -a || :\";\n    exec.prepare += \"umount -f -F /etc/fstab.testjail.parent -a || :\";\n    exec.prepare += \"umount -f ${path}/root/dev || :\";\n    exec.prepare += \"umount -f ${path}/dev || :\";\n\n    ### Starting the jail\n    ## Pre-start commands\n    # Create rundir on host\n    exec.prestart = \"mkdir -p ${rundir}\";\n    # Create the epair interface; put the names of each end in files in $rundir\n    exec.prestart += \"ifconfig epair create \u003e ${rundir}/if_a\"\n    exec.prestart += \"sed 's/a$/b/' \u003c ${rundir}/if_a \u003e ${rundir}/if_b\"\n    # Mount parent jail filesystems\n    exec.prestart += \"mount -F /etc/fstab.${name}.parent -a || :\";\n    # Mount devfs in parent jail\n    exec.prestart += \"mount -t devfs -oruleset=${devfs_ruleset} . ${path}/dev\";\n    # Unhide pf and bpf device in parent jail\n    exec.prestart += \"devfs -m ${path}/dev rule apply path pf unhide\";\n    exec.prestart += \"devfs -m ${path}/dev rule apply path bpf unhide\";\n\n    ## Post-create commands (run after jail created, but before exec.start)\n    # Mount devfs in the child jail with appropriate ruleset\n    exec.created = \"mount -t devfs -oruleset=${devfs_ruleset} . ${path}/root/dev\";\n    # Assign jail-side interface to jail\n    exec.created += \"ifconfig `cat ${rundir}/if_b` vnet ${name}\";\n    # Assign IP address to jail interface\n    exec.created += \"jexec ${name} ifconfig $(cat ${rundir}/if_b) inet 10.2.3.4/24\";\n    # Add default route\n    exec.created += \"jexec ${name} route add default 10.2.3.1\";\n\n    ## Startup commands\n    # Assign IP to lo0-interface\n    exec.start = \"ifconfig lo0 127.0.0.1/8\";\n    # Load PF rules for parent jail and enable\n    exec.start += \"pfctl -F all -vf /pf.conf \u0026\u0026 pfctl -e\";\n    # Start pflogd\n    exec.start += \"/sbin/pflogd 'not host 224.0.0.18'\";\n    # Wait for ping to jail (interface plumbing); assuming this file is\n    # autogenerated and the IP includes the netmask\n    exec.start += \"ip='10.1.2.3/24' ; ping -n -c 15 -t 20 -o \\${ip%/*}\";\n    # Set sysctls (if any are configured)\n    exec.start += \"(test -s /sysctl.conf \u0026\u0026 sysctl -f /sysctl.conf) || :\";\n\n    ## Create child jail\n    exec.start += \"jail -c ip4=inherit path=/root host.hostname=testjail \\\n                   name=child zfs.mount_snapshot=1 children.max=20 \\\n                   securelevel=${child_securelevel} \\\n                   sysvmsg=\\\"new\\\" sysvsem=\\\"new\\\" sysvshm=\\\"new\\\" \\\n                   exec.start=\\\"/bin/sh /etc/rc autoboot\\\"\";\n\n    ## Post-start commands\n    # Unount parent jail filesystems\n    exec.poststart += \"umount -F /etc/fstab.${name}.parent -a -f || :\";\n\n    ### Stopping the jail\n    ## Pre-stop commands\n    # Mount parent jail filesystems\n    exec.prestop = \"mount -F /etc/fstab.${name}.parent -a\";\n\n    ## Shutdown commands\n    # Shut down child jail cleanly, if possible\n    exec.stop = \"jexec child /bin/sh /etc/rc.shutdown ; sleep 1\";\n    # Forcibly remove child jail, just in case\n    exec.stop += \"jls -d name|chroot /root grep -q '^${name}.child$' \u0026\u0026 jail -Rd ${name}.child || :\";\n    # Remove any lingering TCP sockets\n    exec.stop += \"tcpdrop -a || :\";\n\n    ## Post-stop commands\n    # Unount parent jail filesystems\n    exec.poststop = \"umount -F /etc/fstab.${name}.parent -a -f || :\";\n    # Unmount devfs\n    exec.poststop += \"umount -f ${path}/root/dev || :\";\n    # Unount parent jail dev filesystem\n    exec.poststop += \"umount -f ${path}/dev || :\";\n    # Finally, destroy jail interfaces\n    exec.poststop += \"ifconfig `cat ${rundir}/if_b` destroy\";\n\n    # Clean up rundir\n    exec.poststop += \"rm -Rf ${rundir}\";\n```","tags":[],"paid":false,"views":1021,"likes":0}}