This page looks best with JavaScript enabled

Managing Jails in FreeBSD with Iocage

 ·  🎃 kr0m

Iocage is a container (jails) manager that makes use of the best functionalities and technologies that FreeBSD offers us. This will facilitate management throughout the jail’s life cycle, creation, destruction, and updating.

The article is composed of several parts:


Installation

The first step will be to install the software:

pkg install py37-iocage

We can tell it to show us colors:

export IOCAGE_COLOR=TRUE

To make it permanent, we will have to export the variable in the shell we use, in my case it is bash:

vi .bashrc

export IOCAGE_COLOR=TRUE

If it is a server that will contain many jails, we must mount the file descriptor file system , in this way access will be faster:

mount -t fdescfs null /dev/fd

To make it permanent:

vi /etc/fstab

fdescfs /dev/fd fdescfs rw 0 0

If we have more than one zpool, we must indicate to Iocage which one to use:

zpool list

NAME      SIZE  ALLOC   FREE  CKPOINT  EXPANDSZ   FRAG    CAP  DEDUP  HEALTH  ALTROOT
storage   928G  35.8G   892G        -         -     1%     3%  1.00x  ONLINE  -
zroot     109G  16.0G  93.0G        -         -     3%    14%  1.00x  ONLINE  -

In my case, zroot:

iocage activate zroot

We download the version of FreeBSD that will act as the base for the jails:

iocage fetch

Press [Enter] to fetch the default selection: (12.1)
ENTER

To provide network to the Jails, aliases are used on the parent interface. Since these are aliases, they are IPs of the parent and the Jail simultaneously, which implies certain problems if we bind the parent’s services in all available directions. For example, if we do it with the Ssh service while the Jail has the service started, the Ssh of the Jail will be served on that IP. If the Ssh of the Jail is turned off, the Ssh of the parent will be served, which can lead to confusion.

Any service of the parent must be bound exclusively to its IP address. In the jail, there is no problem since it only sees the assigned IP, and with a wildcard, it is bound to this unique existing IP.

In the parent, we must avoid generic bindings like these:

ListenAddress 0.0.0.0  
listen *

Creating a Jail

We create our jail:

iocage create -r LATEST -n test boot=on

We check that it is up:

iocage list

+-----+---------+-------+--------------+--------------+
| JID |  NAME   | STATE |   RELEASE    |     IP4      |
+=====+=========+=======+==============+==============+
+-----+---------+-------+--------------+--------------+
| 5   | test    | up    | 12.1-RELEASE | -            |
+-----+---------+-------+--------------+--------------+

We can access the console with the console command:

iocage console test
root@test:~ # hostname
test
exit

Iocage comes with several jails ready to be used, they are called plugins. With the command list –plugins –remote, we can see which jails are available:

iocage list –plugins –remote

Some of the most popular ones are:

+-------------------+-------------------+-------------------+------------------+
|       NAME        |    DESCRIPTION    |        PKG        |       ICON       |
+===================+===================+===================+==================+
| Bacula-server     | Manage, backup,   | bacula-server     | https://www.true |
+-------------------+-------------------+-------------------+------------------+
| GitLab            | DevOps lifecycle  | gitlab            | https://www.true |
+-------------------+-------------------+-------------------+------------------+
| Jenkins           | Open source build | jenkins           | https://www.true |
+-------------------+-------------------+-------------------+------------------+
| Nextcloud         | Suite of client-  | nextcloud         | https://www.true |
+-------------------+-------------------+-------------------+------------------+
| Zoneminder        | Closed-circuit    | zoneminder        | https://www.true |
+-------------------+-------------------+-------------------+------------------+

Assigning an IP address

Regarding the assignment of IP addresses, Iocage supports two modes: sharedIP/Vnet, but the latter is considered unstable, so we will focus on sharedIP.

The jail IPs will be automatically configured on the parent host through aliases when starting the corresponding jail.

First, we make sure that Vnet is not enabled in the jail:

iocage get vnet test

0

We assign an IP address:

iocage set ip4_addr="nfe0|IP_JAIL/24" test
iocage list

+-----+---------+-------+--------------+--------------+
| JID |  NAME   | STATE |   RELEASE    |     IP4      |
+=====+=========+=======+==============+==============+
+-----+---------+-------+--------------+--------------+
| 5   | test    | up    | 12.1-RELEASE | IP_JAIL      |
+-----+---------+-------+--------------+--------------+

We check that the jail knows its IP:

iocage exec test ifconfig

nfe0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
    options=8210b<RXCSUM,TXCSUM,VLAN_MTU,TSO4,WOL_MAGIC,LINKSTATE>
    ether 00:00:ca:fe:00:00
    inet IP_JAIL netmask 0xffffff00 broadcast 192.168.1.255
    media: Ethernet autoselect (1000baseT <full-duplex,master>)
    status: active
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> metric 0 mtu 16384
    options=680003<RXCSUM,TXCSUM,LINKSTATE,RXCSUM_IPV6,TXCSUM_IPV6>
    groups: lo
ipfw0: flags=8800<SIMPLEX,MULTICAST> metric 0 mtu 65536
    groups: ipfw

Defining boot dependencies

Iocage allows defining dependencies between jails, so that the jail in question will not start until its dependencies have started:

iocage set depends="dep1 dep2" test


Managing snapshots

One of the most interesting features is snapshots. To create them, we execute:

iocage snapshot -n testJail00 test
iocage snapshot -n testJail01 test

To view the snapshots of a jail:

iocage snaplist test

+-----------------+-----------------------+-------+------+
|      NAME       |        CREATED        | RSIZE | USED |
+=================+=======================+=======+======+
| testJail00      | Sun Mar 29 14:06 2020 | 92K   | 60K  |
+-----------------+-----------------------+-------+------+
| testJail00/root | Sun Mar 29 14:06 2020 | 1.33G | 264K |
+-----------------+-----------------------+-------+------+
| testJail01      | Sun Mar 29 14:14 2020 | 92K   | 0    |
+-----------------+-----------------------+-------+------+
| testJail01/root | Sun Mar 29 14:14 2020 | 1.33G | 220K |
+-----------------+-----------------------+-------+------+

NOTE: We can see that there are two snapshots, one of the directory where the Jail configuration is located (config.json/fstab) and another of the root of that Jail.

If we want to revert, we first stop the jail:

iocage stop test

Then we perform the rollback:

iocage rollback -n testJail00 test

NOTE: If we do not revert to the last snapshot, the intermediate snapshots will be deleted. In my case, I had the snapshots testJail00 and testJail01, and by reverting to testjail00, testjail01 has been deleted:

iocage snaplist test
+-----------------+-----------------------+-------+------+
|      NAME       |        CREATED        | RSIZE | USED |
+=================+=======================+=======+======+
| testJail00      | Sun Mar 29 14:06 2020 | 92K   | 0    |
+-----------------+-----------------------+-------+------+
| testJail00/root | Sun Mar 29 14:06 2020 | 1.33G | 0    |
+-----------------+-----------------------+-------+------+

To remove snapshots, we execute:

iocage snapremove -n testJail00 test


Automatic software installation

It is possible to automatically install certain packages in a jail when it is created. We just need to prepare the list in json format:

vi pkgs.json

{
    "pkgs": [
    "ngrep",
    "tcpdump"
    ]
}

We start the jail with the following command:

iocage create -r LATEST -p pkgs.json -n test ip4_addr="nfe0|JAIL_IP/24"


Import/Export of Jails

The import functionality in Iocage is buggy and consumes all the RAM+SWAP of the system. As a workaround, we can export normally, but we will have to import manually using ZFS commands:

iocage export test
scp /zroot/iocage/images/test_2020-03-28.zip REMOTE_SERVER:/zroot/iocage/images/test_2020-03-28.zip

On the receiving server:

unzip /zroot/iocage/images/test_2020-03-28.zip
zfs recv -F zroot/iocage/jails/test < zroot/iocage/images/test_2020-03-28
zfs recv -F zroot/iocage/jails/test/root < zroot/iocage/images/test_2020-03-28_root


Mount Points in Jails

If we want to mount external directories inside the jail, we will use the Iocage fstab command. In this case, we mount the /storage directory inside the jail (/mnt/storage):

iocage console test
mkdir /mnt/storage

iocage fstab -a test "/storage /mnt/storage nullfs rw 0 0"

We can check the mount points with:

iocage fstab -l test

+-------+-------------------------------------------------------------------------------------------+
| INDEX |                                        FSTAB ENTRY                                        |
+=======+===========================================================================================+
| 0     | /storage        /zroot/iocage/jails/test/root/mnt/storage       nullfs  rw      0       0 |
+-------+-------------------------------------------------------------------------------------------+

Quering jail resources

Another very useful option is to be able to see which resources each jail is occupying:

iocage df

+---------+-------+------+------+-------+-------+
|  NAME   |  CRT  | RES  | QTA  |  USE  |  AVA  |
+=========+=======+======+======+=======+=======+
| test    | 1.04x | none | none | 396K  | 84.1G |
+---------+-------+------+------+-------+-------+

Types of Jails

Iocage supports several types of jails.

  • Clone: iocage create -r [RELEASE]
    This is the default jail type. When a jail of this type is created, a clone of the RELEASE snapshot is made. It consumes little space since the base is shared between jails and only the data that is changed inside is saved. Each jail of this type must be updated independently. Since jails of this type have the RELEASE as their base, we cannot delete the RELEASE until we have deleted all jails that depend on it.

  • Base: iocage create -r [RELEASE] -b
    This is a jail generated from a complete copy of the RELEASE, but certain directories of the RELEASE are mounted inside the jail through nullfs. This type of jail takes up less space than thick jails but more than clone jails. It is the ideal type for patching massively since by running iocage update on one of the jails, we will be updating all those that share the RELEASE with it. All jails of that RELEASE can also be updated by running iocage fetch RELEASE again.

  • Thick: iocage create -r [RELEASE] -T
    A complete copy of the RELEASE is made, taking up more space than the other types. It is a jail completely independent of the others, and each jail of this type must be updated independently.

  • Template
    Templates are jails tweaked to serve as a template for quick deployment. To generate a template from an existing jail, follow these steps:

iocage stop test
iocage set template=yes test

We can check the local templates:

iocage list -t

+-----+------+-------+--------------+--------------+
| JID | NAME | STATE |   RELEASE    |     IP4      |
+=====+======+=======+==============+==============+
| -   | test | down  | 12.1-RELEASE | JAIL_IP      |
+-----+------+-------+--------------+--------------+

To create a jail from a template:

iocage create -t test -n newtest

The conversion process to a template can be reversed using the template parameter:

iocage set template=no test

  • Empty: iocage create -e
    These jails are designed for testing or unsupported functionalities, ideal for experimenting with unsupported RELEASES or Linux jails.

We will normally use clone jails unless we use templates where we must bear in mind that these cannot be updated without restarting the jails that descend from them. To be able to perform updates on the templates without having to restart the jails, they will have to be created as Thick type.

iocage create -T -t test -n newtest

Update

Like any other FreeBSD system the operating system update is divided in two parts, the base system and the installed binary packages/ports.


Update BASE

If we stay within the same version, it will be enough to run:

iocage update JAILNAME

To move from one version to another, consult the latest RELEASE available.
If it is a minor update, e.g. 12.0 -> 12.1:

iocage upgrade JAILNAME -r 12.1-RELEASE
iocage update JAILNAME

If it is a major update, e.g. 12.1 -> 13.0, in addition to the steps of a minor update, we must reinstall binary/ports packages and finish the update.
If we are working with binary packages:

iocage console JAILNAME
pkg-static upgrade -f
exit
iocage update JAILNAME

If we are working with ports:

iocage console JAILNAME
git -C /usr/ports pull
cd /usr/ports
make fetchindex
for PORT in $(pkg info|awk '{print$1}'); do PORT_PATH=$(pkg info $PORT|grep Origin|awk '{print$3}') && echo PORT: $PORT - $PORT_PATH && cd /usr/ports/$PORT_PATH && export BATCH="yes" && make clean reinstall clean; done
exit
iocage update JAILNAME

NOTA: Updates using Iocage automatically take a snapshot with each update, so if something goes wrong, it can be easily reverted.


Update PACKAGES/PORTS

Update the binary packages inside a jail:

iocage exec JAILNAME 'ASSUME_ALWAYS_YES=yes pkg upgrade'

iocage exec JAILNAME 'ASSUME_ALWAYS_YES=yes pkg autoremove'

Update the ports inside a jail:

iocage console JAILNAME
git -C /usr/ports pull
for PORT in $(pkg info|awk '{print$1}'); do PORT_PATH=$(pkg info $PORT|grep Origin|awk '{print$3}') && echo PORT: $PORT - $PORT_PATH && cd /usr/ports/$PORT_PATH && export BATCH="yes" && make clean reinstall clean; done


Update script

To make jail updates more convenient, we can write a script like the following, which always leaves the last snapshot in case it needs to be reverted and snapshots called PRESERVER*:

#!/usr/local/bin/bash
source /root/.scripts/cecho.sh

function sendTelegram {
	message=${@:1}
	curl -s -X POST https://api.telegram.org/bot535179217:AAGXRe1df_1WNgqxOCfC8VrCNKGqouhslLw/sendMessage -d chat_id=30418601 -d text="$message"
}

clear
cecho "Cyan" "<== Iocage Jail updater by kr0m ==>"

echo ""
echo ""
cecho "Cyan" ">> Converting basic_template to regular jail"
iocage set template=no basic_template
iocage start basic_template

for JAIL in $(iocage list|grep up|awk '{print$4}'); do
	echo ""
	echo ""
	cecho "Cyan" ">> Updating Base: $JAIL"
	iocage update $JAIL
done

echo ""
cecho "Green" "----------------------------------------------------"

for JAIL in $(iocage list|grep up|awk '{print$4}'); do
	echo ""
	echo ""
	cecho "Cyan" ">> Clearing $JAIL snapshots"
	SNAPSNUMBER=$(iocage snaplist $JAIL|grep -v root|grep -v '+'|grep -v 'NAME'|wc -l|awk '{print$1}')

	# Check testJail
	echo $JAIL |grep test_ 1>/dev/null
	if [ $? -eq 0 ]; then
		TESTJAIL=1
	else
		TESTJAIL=0
	fi
		
	if [ $SNAPSNUMBER -gt 0 ]; then
		# TestJail: Delete all snapshots
		if [ $TESTJAIL -eq 1 ]; then
			for SNAPTOREMOVE in $(iocage snaplist $JAIL|grep -v root|grep -v '+'|grep -v 'NAME'|awk '{print$2}'); do
				cecho "Cyan" "-- Removing: $SNAPTOREMOVE"
				iocage snapremove -n $SNAPTOREMOVE $JAIL
			done
		else
			# If you want to preserve some snapshot from being deleted in update process, name it PRESERVE
			# Additionally we will preserve las IOC update snapshot, it always will be present because script always execute iocage update $JAIL in previous step
			LAST_IOC_UPDATE=$(iocage snaplist $JAIL|grep ioc_update|grep -v 'root'|awk '{print$2}'|tail -n 1)
			for SNAPTOREMOVE in $(iocage snaplist $JAIL|grep -v root|grep -v '+'|grep -v 'NAME'|grep -v 'PRESERVE'|grep -v "$LAST_IOC_UPDATE"|awk '{print$2}'); do
				cecho "Cyan" "-- Removing: $SNAPTOREMOVE"
				iocage snapremove -n $SNAPTOREMOVE $JAIL
			done
		fi
	else
		cecho "Cyan" "-- No snapshots to remove"
	fi
done

echo ""
cecho "Green" "----------------------------------------------------"

for JAIL in $(iocage list|grep up|awk '{print$4}'); do
        echo ""
        echo ""
        cecho "Cyan" ">> Updating PKGs: $JAIL"
	iocage exec $JAIL 'ASSUME_ALWAYS_YES=yes pkg upgrade'
	echo ""
        cecho "Cyan" ">> Executing AUTO-REMOVE: $JAIL"
	iocage exec $JAIL 'ASSUME_ALWAYS_YES=yes pkg autoremove'
done

echo ""
echo ""
cecho "Cyan" ">> Converting basic_template jail to template"
iocage stop basic_template
iocage set template=yes basic_template

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating SpamAssasin: DrWho"
iocage exec DrWho 'sa-update -v'
iocage exec DrWho 'service sa-spamd restart'

echo ""
echo ""
cecho "Cyan" ">> Restarting Mail services: DrWho"
iocage exec DrWho 'service sendmail restart'
iocage exec DrWho 'service dovecot restart'

echo ""
cecho "Cyan" ">> Visit: https://mail.alfaexploit.com/?admin#/about"

echo ""
cecho "Cyan" ">> Checking if Mail system still works: DrWho"
DATE=$(date "+%d/%m/%Y %H:%M:%S")
#echo "DrWho updated: $DATE" | mail -s "DrWho updated: $DATE" kr0m@alfaexploit.com
echo -e "Subject: DrWho updated: $DATE" | sendmail -f root@alfaexploit.com kr0m@alfaexploit.com
sleep 10
grep -r "DrWho updated: $DATE" /zroot/iocage/jails/DrWho/root/var/mail/kr0m 1>/dev/null
if [ $? -ne 0 ]; then
	cecho "Red" "++ ERROR: Mail system is not working"
	MESSAGE="ERROR: Mail system is not working"
	sendTelegram $MESSAGE
else
	cecho "Cyan" ">> Mail system is working"
fi

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating Python PIP: rxWod"
iocage exec rxWod 'su -l kr0m -c "/usr/home/kr0m/rxWod/bin/python3.7 -m pip install --upgrade pip"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating git: rxWod"
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject && git status"'|grep 'nothing to commit, working tree clean' 1>/dev/null
if [ $? -eq 1 ]; then
	iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject && git stash"'
	iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject && git stash drop"'
fi
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject && git pull"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating Python libraries: rxWod"
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod && source bin/activate && cd rxWodProject/ && pip-upgrade -p all"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Making Django migrations: rxWod"
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod && source bin/activate && cd rxWodProject && python manage.py makemigrations"'
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod && source bin/activate && cd rxWodProject && python manage.py migrate"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating Yarn libraries: rxWod"
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject/ && yarn install --force"'
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject/ && yarn upgrade"'
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod/rxWodProject/ && yarn prod-build"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Collecting Django static files: rxWod"
iocage exec rxWod 'su -l kr0m -c "cd /home/kr0m/rxWod && source bin/activate && cd rxWodProject && python manage.py collectstatic --noinput"'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Restarting Daphne service: rxWod"
iocage exec rxWod '/usr/sbin/service daphne restart'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating owasp-modsecurity: Infinity"
iocage exec Infinity 'cd /usr/local/owasp-modsecurity-crs/ && git pull'

echo ""
cecho "Cyan" ">> Updating Hugo theme: Infinity"
iocage exec Infinity 'su -l kr0m -c "cd /home/kr0m/AlfaExploit/alfaexploit_zzo/ && git submodule update --remote --merge"'

echo ""
cecho "Cyan" ">> Redeploying Alfaexploit: Infinity"
iocage exec Infinity 'su -l kr0m -c "cd /home/kr0m/AlfaExploit/alfaexploit_zzo/ && hugo && rm -rf /usr/local/www/alfaexploit/* && cp -r public/* /usr/local/www/alfaexploit/"'

NOTE: I leave the script cecho a little further down, if we don’t want to use it, we just have to change the cechos to echos.


New jail script

To automate the creation of new jails, we must first create a jail that will serve as a base:

iocage create -r 12.2-RELEASE -n basic_template
iocage set ip4_addr=“nfe0|192.168.69.9/24” basic_template

We install the base software and perform basic configuration:

iocage console basic_template

We convert the jail into a template:

iocage stop basic_template
iocage set template=yes basic_template

Now we can automate the creation of jails with the following script:

#!/usr/local/bin/bash
source /root/.scripts/cecho.sh

clear
cecho "Cyan" "<== Iocage Jail creator by kr0m ==>"

echo ""
echo ""
cecho "Green" "-- Jail name: $1"
cecho "Green" "-- IP address: $2"
if [ -z $3 ]; then
	cecho "Green" "-- VNET: NO"
	VNET=0
else
	if [ $3 == "vnet" ]; then
		cecho "Green" "-- VNET: YES"
		cecho "Yellow" "-- VNET: Remember to check configuration for bridge interface"
		VNET=1
	else
		cecho "Green" "-- VNET: NO"
		VNET=0
	fi
fi

echo ""
echo ""
cecho "Cyan" ">> Making basic checks"
if [ $# -ne 2 ] && [ $# -ne 3 ]; then
	cecho "Red" "++ ERROR: Script needs JAIL_NAME IP_ADDRESS VNET(optional) arguments"
	exit
fi

JAIL_NAME=$1
IP_ADDRESS=$2

cecho "Cyan" ">> Checking ip in correct net/range"
first_ip_address=$(echo $IP_ADDRESS|awk -F "." '{print$1}')
second_ip_address=$(echo $IP_ADDRESS|awk -F "." '{print$2}')
third_ip_address=$(echo $IP_ADDRESS|awk -F "." '{print$3}')
fourth_ip_address=$(echo $IP_ADDRESS|awk -F "." '{print$4}')

if [ $first_ip_address -eq 192 ] && [ $second_ip_address -eq 168 ] && [ $third_ip_address -eq 69 ] && [ $fourth_ip_address -lt 200 ]; then
    cecho "Green" "-- Correct ip net/range"
else
    if [ $first_ip_address -ne 192 ] || [ $second_ip_address -ne 168 ] || [ $third_ip_address -ne 69 ]; then
        cecho "Red" "++ ERROR: Incorrect ip address NET: $IP_ADDRESS"
	exit
    fi
    if [ $fourth_ip_address -ge 200 ]; then
        cecho "Red" "++ ERROR: Ip address in DHCP range: $IP_ADDRESS"
	exit
    fi
fi

cecho "Cyan" ">> Checking jail names"
for JAIL in $(iocage list|grep up|awk '{print$4}'); do
    if [ "$JAIL_NAME" == "$JAIL" ]; then
        cecho "Red" "++ ERROR: Already running jail with name: $JAIL_NAME"
        exit
    fi
done
cecho "Green" "-- Correct jail name"

cecho "Cyan" ">> Checking duplicated jail ips"
for IP in $(iocage list|grep up|awk '{print$10}'); do
    if [ "$IP_ADDRESS" == "$IP" ]; then
        cecho "Red" "++ ERROR: Already running jail with ip: $IP_ADDRESS"
        exit
    fi
done
cecho "Green" "-- Correct jail ip"

cecho "Cyan" ">> Checking if base template exists"
BASE_TEMPLATE_EXISTS=$(iocage list -t|grep 'basic_template'|awk '{print$4}'|wc -l|awk '{print$1}')
if [ $BASE_TEMPLATE_EXISTS -eq 1 ]; then
    cecho "Green" "-- Base template found"
else
    cecho "Red" "++ ERROR: Base template not found"
    exit
fi

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Deploying: $JAIL_NAME"
iocage create -T -t basic_template -n $JAIL_NAME
if [ $VNET -eq 1 ]; then
	iocage set vnet=on $JAIL_NAME
	#iocage set allow_raw_sockets="1" $JAIL_NAME
	iocage set defaultrouter=192.168.69.200 $JAIL_NAME
	iocage set ip4_addr="vnet0|$IP_ADDRESS/24" $JAIL_NAME
else
	#iocage set ip4_addr="bridge0|$IP_ADDRESS/24" $JAIL_NAME
	iocage set ip4_addr="nfe0|$IP_ADDRESS/24" $JAIL_NAME
fi
iocage start $JAIL_NAME

echo ""
cecho "Green" "----------------------------------------------------"

if [ $VNET -eq 1 ]; then
	echo ""
	echo ""
	cecho "Cyan" ">> Disabling FW: $JAIL_NAME"
	iocage exec $JAIL_NAME "sysrc firewall_enable=YES"
	iocage exec $JAIL_NAME "sysrc firewall_type=open"
fi

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Setting FQDN: $JAIL_NAME"
iocage exec $JAIL_NAME "sysrc hostname=$JAIL_NAME.alfaexploit.com"

cecho "Cyan" ">> Patching /etc/hosts: $JAIL_NAME"
iocage exec $JAIL_NAME "sed -i '' 's/$JAIL_NAME//g' /etc/hosts"
JAIL_NAME2=$(echo $JAIL_NAME | tr '[:upper:]' '[:lower:]')
if [ "$JAIL_NAME" == "$JAIL_NAME2" ]; then
	iocage exec $JAIL_NAME "sed -i '' 's/CUSTOM_ENTRY/$IP_ADDRESS\t\t$JAIL_NAME.alfaexploit.com $JAIL_NAME/g' /etc/hosts"
else
	iocage exec $JAIL_NAME "sed -i '' 's/CUSTOM_ENTRY/$IP_ADDRESS\t\t$JAIL_NAME.alfaexploit.com $JAIL_NAME $JAIL_NAME2.alfaexploit.com $JAIL_NAME2/g' /etc/hosts"
fi

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Restarting: $JAIL_NAME"
iocage stop $JAIL_NAME
iocage start $JAIL_NAME

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating Base: $JAIL_NAME"
iocage update $JAIL_NAME

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Clearing $JAIL_NAME snapshots"

N=$(iocage snaplist $JAIL_NAME|grep 'ioc_update'|grep -v root|grep -v '+'|grep -v 'NAME'|wc -l|awk '{print$1}')

if [ $N -gt 0 ]; then
    for SNAPTOREMOVE in $(iocage snaplist $JAIL_NAME|grep 'ioc_update'|grep -v root|grep -v '+'|grep -v 'NAME'|awk '{print$2}'); do
        cecho "Cyan" "-- Removing: $SNAPTOREMOVE"
	    iocage snapremove -n $SNAPTOREMOVE $JAIL_NAME
    done
else
    cecho "Cyan" "-- No snapshots to remove"
fi

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating PKGs: $JAIL_NAME"
iocage exec $JAIL_NAME 'ASSUME_ALWAYS_YES=yes pkg upgrade'
iocage exec $JAIL_NAME 'ASSUME_ALWAYS_YES=yes pkg autoremove'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Updating pip: $JAIL_NAME"

iocage exec $JAIL_NAME 'pip install --upgrade pip'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Configuring SendMail Stats: $JAIL_NAME"

iocage exec $JAIL_NAME 'cd /etc/mail && make'
echo "define(\`STATUS_FILE',\`/var/log/sendmail.stats')dnl" >> /zroot/iocage/jails/$JAIL_NAME/root/etc/mail/$JAIL_NAME.alfaexploit.com.mc
iocage exec $JAIL_NAME 'cd /etc/mail && make'
iocage exec $JAIL_NAME "cp /etc/mail/$JAIL_NAME.alfaexploit.com.cf /etc/mail/sendmail.cf"
iocage exec $JAIL_NAME 'service sendmail restart'

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Making last restart: $JAIL_NAME"
iocage stop $JAIL_NAME
iocage start $JAIL_NAME

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Red" ">> Remember to add $JAIL_NAME to Prometheus: /etc/hosts and /usr/local/etc/prometheus.yml: sendmail_exporter"
cecho "Red" ">> Remember to add fstab entry via iocage and CRON job for backups"

echo ""
cecho "Green" "----------------------------------------------------"

echo ""
echo ""
cecho "Cyan" ">> Jail $JAIL_NAME deployed and updated"

An initial version of the script created clone-type jails, but this corrupted the jails already created from the template. As it is a clone, the shared base cannot be modified while it is running. Restarting the jail made it work again, but to avoid this downtime, it was decided to create them as Thick type .

Here’s the translation:

Below is the cecho script:

cecho(){
    # Reset
    Color_Off='\033[0m'       # Text Reset
    
    # Regular Colors
    Black='\033[0;30m'        # Black
    Red='\033[0;31m'          # Red
    Green='\033[0;32m'        # Green
    Yellow='\033[0;33m'       # Yellow
    Blue='\033[0;34m'         # Blue
    Purple='\033[0;35m'       # Purple
    Cyan='\033[0;36m'         # Cyan
    White='\033[0;37m'        # White
    
    # Bold
    BBlack='\033[1;30m'       # Black
    BRed='\033[1;31m'         # Red
    BGreen='\033[1;32m'       # Green
    BYellow='\033[1;33m'      # Yellow
    BBlue='\033[1;34m'        # Blue
    BPurple='\033[1;35m'      # Purple
    BCyan='\033[1;36m'        # Cyan
    BWhite='\033[1;37m'       # White
    
    # Underline
    UBlack='\033[4;30m'       # Black
    URed='\033[4;31m'         # Red
    UGreen='\033[4;32m'       # Green
    UYellow='\033[4;33m'      # Yellow
    UBlue='\033[4;34m'        # Blue
    UPurple='\033[4;35m'      # Purple
    UCyan='\033[4;36m'        # Cyan
    UWhite='\033[4;37m'       # White
    
    # Background
    On_Black='\033[40m'       # Black
    On_Red='\033[41m'         # Red
    On_Green='\033[42m'       # Green
    On_Yellow='\033[43m'      # Yellow
    On_Blue='\033[44m'        # Blue
    On_Purple='\033[45m'      # Purple
    On_Cyan='\033[46m'        # Cyan
    On_White='\033[47m'       # White
    
    # High Intensity
    IBlack='\033[0;90m'       # Black
    IRed='\033[0;91m'         # Red
    IGreen='\033[0;92m'       # Green
    IYellow='\033[0;93m'      # Yellow
    IBlue='\033[0;94m'        # Blue
    IPurple='\033[0;95m'      # Purple
    ICyan='\033[0;96m'        # Cyan
    IWhite='\033[0;97m'       # White
    
    # Bold High Intensity
    BIBlack='\033[1;90m'      # Black
    BIRed='\033[1;91m'        # Red
    BIGreen='\033[1;92m'      # Green
    BIYellow='\033[1;93m'     # Yellow
    BIBlue='\033[1;94m'       # Blue
    BIPurple='\033[1;95m'     # Purple
    BICyan='\033[1;96m'       # Cyan
    BIWhite='\033[1;97m'      # White
    
    # High Intensity backgrounds
    On_IBlack='\033[0;100m'   # Black
    On_IRed='\033[0;101m'     # Red
    On_IGreen='\033[0;102m'   # Green
    On_IYellow='\033[0;103m'  # Yellow
    On_IBlue='\033[0;104m'    # Blue
    On_IPurple='\033[0;105m'  # Purple
    On_ICyan='\033[0;106m'    # Cyan
    On_IWhite='\033[0;107m'   # White

    printf "${!1}${2} ${Color_Off}\n"
}

Firewall

As a final note, we must keep in mind that all firewall rules must be defined on the parent host. When referring to “me,” it includes all the IPs of the aliases. Therefore, generic rules will be configured using “me,” and the more specific ones for each service will indicate the source and destination IP of the jail.


Recommendations

Some recommendations are:

  • It is more efficient to restart jails using iocage restart -s than to do a stop/start.
  • Every so often, clean up old snapshots, especially for jails whose data changes frequently.
If you liked the article, you can treat me to a RedBull here