Jails on FreeBSD

We have had Jails on FreeBSD since 4.0 came out 19 years ago in March 2000. This describes how to setup jails on FreeBSD 12 without any helpers.

Steps

  1. I usually place all jails under /var/jail and give each jail its own directory, which reflects its short hostname. For this, I use bsdinstall:
    # bsdinstall jail /var/jail/mysql
    Deselect all optional system components during the installation as well as any services.
  2. On the host system, you can either create one big /etc/jail.conf or one /etc/jail.HOSTNAME.conf per jail, e.g. /etc/jail.mysql.conf:
    exec.start = "/bin/sh /etc/rc";
    exec.stop = "/bin/sh /etc/rc.shutdown";
    exec.clean;
    mount.devfs;
    
    path = "/var/jail/mysql";
    
    mysql {
      host.hostname = "mysql.intra.ogris.net";
      ip4.addr = "lo1|10.0.0.2";
    }
    
  3. In order to have the jails started and stopped during system boot and shutdown, respectively, add this to /etc/rc.conf:
    jail_enable="YES"
    jail_list="mysql"
    
  4. Unless you want to assign each jail an IP address from your network, you have to setup a host-only network. In /etc/rc.conf:
    cloned_interfaces="lo1"
    ifconfig_lo1="inet 10.0.0.1 netmask 255.255.255.0"
    
  5. Usually, you want to give your jails Internet access. Thus, we need NAT on the host. First, create /etc/ipfw.rules:
    nat 1 config if vtnet0 same_ports
    add nat 1 ip from any to any via vtnet0
    add allow ip from any to any
    
    Replace vtnet0 by your actual network interface.
  6. Now add this to /etc/rc.conf in order to activate IP forwarding and to have the firewall rules loaded during system boot:
    kld_list="ipfw_nat"
    gateway_enable="YES"
    firewall_enable="YES"
    firewall_type="/etc/ipfw.rules"
    
  7. Reboot the host. Afterwards, you can log into your jails, e.g. by typing
    # jexec mysql /bin/csh
    
  8. Optionally, set up a local unbound as caching DNS resolver:
    # cat >/etc/unbound/conf.d/local.conf <<EOF
    server:
    	interface: 0.0.0.0
    	access-control: 127.0.0.0/8 allow
    	access-control: 10.0.0.0/24 allow
    EOF
    # service local_unbound enable
    # service local_unbound start
    
  9. Inside the jail, setup /etc/resolv.conf (you can do this from the host as well!). Either add your local nameserver, or the jail's IP address if you started a local unbound resolver on the host as described above
  10. Now, you can install the ports collections as usual:
    # portsnap fetch extract
    
  11. Not strictly needed, but I like my jails to send emails via the host system. First, fix /etc/make.conf for both the host and the jails:
    SENDMAIL_MC=/etc/mail/freebsd.mc
    SENDMAIL_SUBMIT_MC=/etc/mail/freebsd.submit.mc
    
  12. On the host, add this to /etc/rc.conf:
    sendmail_enable="YES"
    sendmail_rebuild_aliases="YES"
    
  13. Make sure that /etc/mail/freebsd.mc contains these lines:
    FEATURE(`accept_unresolvable_domains')dnl
    FEATURE(`accept_unqualified_senders')dnl
    DAEMON_OPTIONS(`Name=IPv4, Family=inet, Addr=10.0.0.1')dnl
    
  14. Additionally, if your host has to use a relay server as well:
    define(`SMART_HOST', `[192.168.23.42]')dnl
    
  15. Rebuild sendmail.cf and restart sendmail:
    # make -C /etc/mail clean all install
    # service sendmail restart
    
  16. Inside each jail, add this to /etc/rc.conf:
    sendmail_rebuild_aliases="YES"
    
  17. In /etc/mail/freebsd.mc, set the host's ip address as relay server:
    define(`SMART_HOST', `[10.0.0.1]')dnl
    FEATURE(nocanonify)dnl
    
  18. Rebuild sendmail.cf and restart sendmail inside the jails as well:
    # make -C /etc/mail clean all install
    # service sendmail restart