Making FreeBSD jails with jail.conf

So far making jails in FreeBSD with jail.conf is the easiest method I’ve tried (out of the several jail mangers I’ve tried). I’m not sure if any jail manager can be easier than this.

vnet, yep.
mounts, yep.
pkgs, yep.
resource limits, yep.

I am building myself a script to handle “templates” which will consist of a few config files that list the name, IP, mountings and whatnot but below is a quick script for you to play with after you do the initial “classic jail setup” items listed in the handbook. NOTE: Absolutely zero error handling on the below script (this is not a “production ready” version). Use with caution!

#!/bin/sh

# Script Arguments
jailname=$1
epairid=$2

# Global variables.
media=14.1-RELEASE-base.txz
containers=/usr/local/jails/containers/$jailname

# Create the directory.
mkdir -p $containers

# Extract the base.
tar -xf /usr/local/jails/media/$media -C $containers --unlink

# Copy DNS server 
cp /etc/resolv.conf $containers/etc/resolv.conf

# Copy timezone
cp /etc/localtime $containers/etc/localtime

# update to latest patch
# freebsd-update -b $containers/ fetch install

# Create the jail.conf file.
echo "$jailname {" > /etc/jail.conf.d/${jailname}.conf
echo '
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop  = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;
  devfs_ruleset = 5;

  # PATH/HOSTNAME
  path = "/usr/local/jails/containers/${name}";
  host.hostname = "${name}";

  # VNET/VIMAGE
  vnet;
  vnet.interface = "${epair}b";' >> /etc/jail.conf.d/${jailname}.conf
echo "
  # NETWORKS/INTERFACES
  \$id = \"${epairid}\"; 
  \$ip = \"192.168.0.\${id}/24\";
  \$gateway = \"192.168.0.1\";
  \$bridge = \"bridge0\"; 
  \$epair = \"epair\${id}\";" >> /etc/jail.conf.d/${jailname}.conf
echo '  
  # ADD TO bridge INTERFACE
  exec.prestart  = "/sbin/ifconfig ${epair} create up";
  exec.prestart += "/sbin/ifconfig ${epair}a up descr jail:${name}";
  exec.prestart += "/sbin/ifconfig ${bridge} addm ${epair}a up";
  exec.start    += "/sbin/ifconfig ${epair}b ${ip} up";
  exec.start    += "/sbin/route add default ${gateway}";
  exec.poststop = "/sbin/ifconfig ${bridge} deletem ${epair}a";
  exec.poststop += "/sbin/ifconfig ${epair}a destroy";
}' >> /etc/jail.conf.d/${jailname}.conf

And another to cleanup the jail. NOTE: same as above, no error
checking! Use with caution.

#!/bin/sh

jailname=$1

# stop the jail
service jail stop $jailname

# Remove flags.
chflags -R 0 /usr/local/jails/containers/$jailname

# Delete directory
rm -rf /usr/local/jails/containers/$jailname

# Delete the .conf file.
rm /etc/jail.conf.d/${jailname}.conf

Last Saturday morning before everyone got up I hacked up a more robust version that reads a few config files (one for the userland and jail location) and one for the jail specific variables (name, IP, mounts, etc)–for a template system–and creates a jail.conf file in /etc/jail.conf.d/. My script became a bit “too monolithic” for my taste but it worked for the most part. Last night I ran a simple test to see if I could do the setup portion too–which worked(ish)–but the script needs to be rewritten to include a more robust error trapping system (to account for conditions like when jail start up fails).

This was fun but it turns out these jail managers written in shell script are hard to write. If I do take this further, I will rewrite in C (or get a bit more organized instead of hacking the skeleton code up in the early morning).

I forgot I made this thread. BIG UPDATE: This works now.

The link below contains everything needed to get started creating jails with simple config files (see the example folder in the link for two easy examples).

make install and make uninstall work.
man jcreate will show documentation.

I have made two updates to this jail setup tool:

  1. Added the optional ability to install packages from the host system with a jail.packages variable. This allows the jail to be slimmed (extra unnecessary stuff removed) thus moving the jail maintenance steps to host system. This also should allow for easier copying of existing jail templates (like the plugins from iocage which may keep a separate package list). See the “emby example” in the example directory.

  2. Added a check if container already exists -i.e. jcreate will not extract the userland if the container already exists. This should allow for updates to be run as well; skipping the userland extraction step will still allow for the setup script and etc to be (re)copied in and run for extra/changed setup conditions. Because the script will now skip creating the container but still copy in the files, setup script, and create the jail.conf file in /etc/jail.conf.d/ means jcreate now supports thin jails as well. -i.e. if a thin jail container is already created, this script will still allow for custom setups to be preformed on thin jails as it does for thick jails.

Just implemented (haven’t pushed to GitHub just yet) a change that allows for multiple scripts to be loaded and executed inside jail. A file is passed to this script and if that file is a script, it is loaded and executed. If that file is a list of scripts, each script is loaded and executed.

This change will help in a few different scenarios:

  1. Any user setup or sensitive scripts (with passwords, etc) can be kept in a separate script, loaded, launched and deleted -e.g. do_some_stuff ... rm "$(readlink -f "$0")".
  2. Package configurations can be kept in separate scripts–for easier maintenance (or whatever reason)–and loaded separately and run in a specific order -e.g. “database.sh”, “serversetup.sh”, “appsetup.sh”.
1 Like

I have created and tested an instancing based rc.d script for the jail setup portion of this tool to use the rc.d system instead of the current “execution-based method”. By using an rc.d script to launch the setup this should allow for more streamlined automation possibilit(y/ies); since the host system can copy-in an updated setup script and enable the script to be run on launch/reboot with either manual scripting or an updated jcreate configuration.

The rc.conf looks like:

run_setup_enable (bool):        Set to YES to enable run on startup.
                                Default: NO
run_setup_path (string):        Path of script to run

because the rc.d script I built is instancing, it will allow a symlink to be created:

ln -s /usr/local/etc/rc.d/run_setup /usr/local/etc/rc.d/run_setup_sshd
sysrc run_setup_sshd_enable=YES
sysrc run_setup_sshd_path=/location/of/sshd/setup/script.sh

The jail can then be restarted, or the service(s) can be manually started from within with a service start like:

service run_setup_sshd start

Thinking about the rc.d step more last night, I think this may pan out to be more of an unnecessary feature than I think; in that, during creation if the packages are to be installed from the host, the jail is started anyways so the rc.d setup script wouldn’t save a jail execution step (i.e., the rc.d setup script may stay in my back pocket for now).

However, I did find a bug in the last commit. Fixed and pushed.

Implemented a host.config variable to execute commands or launch scripts that do setup on the host.

-e.g. host.config = “host_setup_script.sh”
which can create directories or copy setup scripts for use in the exec.poststart / exec/prestop jail.conf variables or do anything else you desire.

-e.g. host.config = jail -m … meta=“key=value” env=“configuration”
or attach metadata to the created jail.