+++ title = "poudriere in jails with zfs" date = "2016-06-26T16:30:00+00:00" author = "Gibheer" draft = false +++ There are tons of tutorials out there on how to get poudriere running in a jail. But most of them have in common, that they either miss options or have too many of them. So what I try with this, is to get the most condensed version of the whole process to get poudriere up and running, serving the generated packages. In the following for 4 sections, we create a jail with the name *poudriere1*, using partitions `tank/jails/poudriere1` and `tank/jails/ppoudriere1/data`. For connectivity the jail will get the IP *192.168.1.10*. ## setting up zfs The first step is to create both ZFS partitions on the host system with the following commands: ``` $ zfs create -o mountpoint=/jails/poudriere1 tank/jails/poudriere1 $ zfs create -o jailed=on tank/jails/poudriere1 ``` The option *jailed=on* makes the partition completely available for the jail to manipulate, so that poudriere can create new partitions. This also makes it unavailable for the host system to mount as the jail can change the mountpoint. ## system preparations In addition to the ZFS partitions, we also need a separate network address to listen on, so that we can make the packages available using nginx. For this, we define a new network interface lo1. This is then used by the jail to add its IP. To make this work, place the following line in rc.conf: ``` cloned_interfaces="lo1" ``` and start the new interface with the command ``` service netif cloneup ``` Next the jail must be able to reach the internet. As I had pf already in place, I added the following NAT rule ``` nat on em0 inet from 192.168.1.0/24 to any -> (em0) ``` which redirects all traffic from 192.168.1.0/24 to the external interface. Reload pf to make this change working ``` $ service pf reload ``` But to make this work, the host must also be told to do packet forwarding in the `rc.conf` ``` gateway_enable="YES" ``` After that restart the routing service using ``` $ service routing restart ``` ## configuring the jail The next step is to configure the jail, so that it can start and run poudriere. This is done in `/etc/jail.conf`. The following section shows the settings needed and a short explanation. More can be looked up in `main 8 jail`. ``` poudriere1 { # first we set the permissions for the jail # enforce_statfs allows the jail to get information about the mountpoint. With # 1 it is able to see its root mountpoint and below. This is needed to be able # to mount any file system. enforce_statfs=1; # This option allows the jail to mount file systems. allow.mount; # The following options enable mounting the specific file systems, needed to # get poudriere running. allow.mount.devfs; # nullfs is used for remounting the ports tree into the child jails. allow.mount.nullfs; # tmpfs can be disabled when poudriere is told to not use it. allow.mount.tmpfs; allow.mount.procfs; # This is needed to mount ZFS file systems. allow.mount.zfs; # As poudriere is using chflags, the jails needs to be allowed its usage. allow.chflags; # This option needs to be set, as poudriere grants that permission its jails. allow.raw_sockets; allow.socket_af; allow.sysvipc; # Allow this jail to run its own child childs up to the number. children.max=20; # Set a hostname visiable through jls. host.hostname = "$name"; # Set the path to the jails root directory. path = "/jails/$name"; # Automatically mount and unmount the dev file system, needed for ZFS and also # used in poudriere jails. mount.devfs; # Set the IPs to use. 192.168.1.10 is handled automatically, localhost # is reused from the host system. ip4.addr=lo1|192.168.1.10, 127.0.0.1; ip6.addr=::1; # Boot up the jail at start using the RC system. This enables the use of rc.conf. exec.start += "/bin/sh /etc/rc"; # After the jail is started, grant the ZFS partition to the jail, so that it # can manage the work partition itself. exec.poststart += "zfs jail $name rpool/jails/$name"; # On stopping the jail, go through the RC system. exec.stop += "/bin/sh /etc/rc.shutdown"; # This option makes sure, that the jail is running without any environment # variables set. exec.clean; } ``` ## installing and starting the jail To install the jail we need to fetch a release, extract it into the jail root, make some last adjustments and then start it up. To fetch a release the following command can be used (adjust the version to your need): ``` $ fetch ftp://ftp.freebsd.org/pub/FreeBSD/releases/amd64/amd64/10.2-RELEASE/base.txz -o /tmp/base.txz ``` Then extract the base package into the root using the following command ``` $ tar -xf /tmp/base.txz -C /jails/poudriere1 ``` Next the timezone has to be set, the resolv.conf copied and the hostname set. ``` $ echo 'hostname="poudriere1"' > /jails/poudriere1/etc/rc.conf $ cp /etc/localtime /jails/poudriere1/etc/localtime $ cp /etc/resolv.conf /jails/poudriere1/etc/resolv.conf ``` Instead of using the resolv.conf of the host system it would also be possible to use unbound on the host and use that as the DNS server in the jail. With that done, we can now start the jail using the command: ``` $ jail -c poudriere1 ``` If the command fails with the message that it can't find `/jails/poudriere1`, check that the ZFS partition hasn't *jailed* set to *on* and that it is mounted in the correct place. Accessing the jail using *jexec* it is possible to check if the basic setup works. ``` $ jexec poudriere1 root@poudriere1:/ # echo 'GET index.html' | nc zero-knowledge.org 80 root@poudriere1:/ # zfs list ``` If you do not get html output, check if the interface has the IPs defined and that you set up the routing correctly. If the ZFS partitions are missing, check if the permissions are set in */etc/jail.conf*. ## configuring poudriere To install poudriere, build it from ports or install it through pkg. ``` root@poudriere1/ # portsnap fetch extract root@poudriere1/ # cd /usr/ports/ports-mgmt/poudriere root@poudriere1/ # make install clean ``` Before starting with setting up poudriere, we have to mount the work directory somewhere where we can actually use it: ``` root@poudriere1/ # zfs set mountpoint=/poudriere tank/jails/poudriere1/work root@poudriere1/ # zfs mount tank/jails/poudriere1/work ``` Now we can set up poudriere the environment. Change the following settings in `/usr/local/etc/poudriere.conf`: ``` ZPOOL=tank # relative to the zpool ZROOTFS=/jails/poudriere1/work BASEFS=/poudriere # enable when you have set mount.allow.tmpfs in the jail.conf USE_TMPFS=yes # size in GB to allow for the ram drive TMPFS_LIMIT=2 # set to no when you have the linux driver enabled NOLINUX=yes # set to no when you do not want to keep old versions of packages around KEEP_OLD_PACKAGES=yes KEEP_OLD_PACKAGES_COUNT=10 PRESERVE_TIMESTAMP=yes BUILD_AS_NON_ROOT=yes ``` With that done, we can build the first jail for poudriere to work with. I mostly follow the [FreeBSD handbook](https://www.freebsd.org/doc/handbook/ports-poudriere.html) ``` root@poudriere1/ # # create a jail with the 10.2-RELEASE root@poudriere1/ # poudriere jail -c -j 102amd64 -v 10.2-RELEASE root@poudriere1/ # # list available jails root@poudriere1/ # poudriere jail -l ``` If there is a problem with the jail creation, you can run the command using *-x* to get the debug output. ``` poudriere -x jail -c -j 102amd64 -v 10.2-RELEASE ``` If it happens that you get the error `Unable to execute id(1) in jail.` a permission is missing in `/etc/jail.conf`. To find out which is missing, check the debug output for the jail command. All permissions are added on the command line, so it is easier to compare the list of permissions with what poudriere wants to grant its jails. Next we create the ports tree for poudriere to use: ``` root@poudriere1/ # # create a new ports tree root@poudriere1/ # poudriere ports -c -p local root@poudriere1/ # # list the installed port trees root@poudriere1/ # poudriere ports -l ``` The next step is to create the list of packages poudriere should build into `/usr/local/etc/poudriere.d/base-pkglist`: ``` ports-mgmt/pkg www/nginx ``` It is also possible to use sets, for example for build options. The next code would be the make.conf for the base set, when placed in `/usr/local/etc/base-make.conf`: ``` OPTIONS_UNSET=DOCS EXAMPLES X11 DOCBOOK NLS CUPS DEFAULT_VERSIONS+=ssl=openssl DEFAULT_VERSIONS+=pgsql=9.5 ``` Using these files, the ports can be configured using the command: ``` root@poudriere1/ # poudriere options -j 102amd64 -p local -z base -f /usr/local/etc/poudriere.d/base-pkglist ``` To start a bulk run, which build all packages in the list, use the bulk command ``` root@poudriere1/ # poudriere bulk -j 102amd64 -p local -z base -f /usr/local/etc/poudriere.d/base-pkglist ``` You can find the created packages in the directory `/poudriere/data/packages`. ## configuration of nginx in the jail The configuration of nginx in the jail is done in a moment. For that nginx has to be installed. Using the freshly built packages us the following command (adjust the path according to your setup): ``` pkg install /poudriere/data/packages/102amd64-local-base/All/nginx-1.10.1.2.txz ``` After that you can configure nginx in the file `/usr/local/etc/nginx/nginx.conf`. The server configuration needs adjustment and nginx must be told where the data resides: ``` server { listen 192.168.1.10:80; server_name 192.168.1.10; location / { root /poudriere/data/packages; autoindex on; } } ``` This will create an automatic index of the directory content and make it available for download. With this, it can be consumed by pkg on other systems. If you also want to serve the logs, you can enable them with the following code ``` location /logs { root /poudriere/data/logs/bulk; autoindex on; } ``` ## configuration of nginx outside of the jail To forward incoming requests to the jail nginx instance, the following location option can be used: ``` location / { proxy_pass http://192.168.1.10:80; include proxy_params; } ``` ## more information This should help to get things up and running. If you need further information, please see the following man pages * [man jail](https://www.freebsd.org/cgi/man.cgi?jail) * [man jail.conf](https://www.freebsd.org/cgi/man.cgi?jail.conf) * [man poudriere](https://www.freebsd.org/cgi/man.cgi?poudriere) * [man zfs](https://www.freebsd.org/cgi/man.cgi?zfs) There is also good documentation found on * [FreeBSD handbook ports section](https://www.freebsd.org/doc/handbook/ports.html) * [FreeBSD handbook jails section](https://www.freebsd.org/doc/handbook/jails.html) * [nginx](http://nginx.org/en/docs/) There are also some tools to run jails, instead of making it raw like I did in this entry. * [cbsb](https://www.bsdstore.ru/en/about.html) * [ezjail](https://erdgeist.org/arts/software/ezjail/) * [iocage (unsupported from 10.3 onwards)](https://github.com/iocage/iocage)