====== Scripts and configs of my jails on freebsd ======
===== Content of directory /root/bin =====
List all jails. Example:
jlist
+--------------+-----------+--------------------+---------------------------------------------------------------+
| runs/stopped | version | autostart ip/ip | note |
+--------------+-----------+--------------------+---------------------------------------------------------------+
| astZf1 | 14.3-R | 81.xx.xx.52/27 | Asterisk worker |
| dbTest | 14.2-R | 192.168.144.157/24 | |
| dbZFS | 14.2-R | 192.168.144.154/24 | Starší mysql s innodb, po 5 vteřinách přerušuje dlouhé dotazy |
| dbZFSino | 14.2-R-p3 | 192.168.144.156/24 | Nejnovější mariadb s innoDB |
| dbZFSsata | 14.2-R | 192.168.144.153/24 | |
| pkg | 14.3-R | 192.168.144.152/24 | Created 2025-06-17 11:14:12 |
| ucet | 14.3-R | 192.168.144.155/24 | |
+--------------+-----------+--------------------+---------------------------------------------------------------+
Starting order: dbZFSino,dbZFSsata,dbZFS,ucet
Alternative user scripts located in ~/bin :jedit,jdestroy,jcreate
#!/usr/bin/env ruby
require 'json'
require '~/bin/ArrayToTable'
def mark_running(t); return "#{C_LIGHT_CYAN}#{t}#{C_NC}"; end
def mark_autostart(t); return "#{C_YELLOW}#{t}#{C_NC}";end
# it expect that each jail config is located in /jails/JAILNAME/c.conf
existing_jails= `grep -h '^[^#]' /jails/*/c.conf 2> /dev/null| grep -h -E '[[:space:]]*[[:alnum:]][[:space:]]*\\{' | tr -d '\\ \\t{'`.split
running_jails=JSON.parse(`jls --libxo json`)['jail-information']['jail'].map{ |hsh| hsh['hostname'] }
autostart_jails=`sysrc -n jail_list`.strip.split
array_table=[[ mark_running("runs")+"/stopped","version", mark_autostart("autostart ip")+"/ip", "note" ]]
existing_jails.each{|j|
config=File.read("/jails/#{j}/c.conf")
ip=/\s*\$ip\s*=\s*"([^"]*)"/.match(config)
ip= ip ? ip[1] : "-"
note=/\s*\$note\s*=\s*"([^"]*)"/.match(config)
note = note ? note[1] : "-"
if File.exist?("/jails/#{j}/rootfs/bin/freebsd-version")
freeBSDversion=`/jails/#{j}/rootfs/bin/freebsd-version -u`.sub("RELEASE","R").sub("CURRENT","C").sub("STABLE","S").sub("BETA","B").strip
else
freeBSDversion="-"
end
running=running_jails.include?(j)
is_autostart=autostart_jails.include?(j)
array_table << [ running ? mark_running(j) : j,freeBSDversion,is_autostart ? mark_autostart(ip) : ip, note ]
}
print array_table.to_table
puts "Starting order: #{autostart_jails.join(",")}"
print "Alternative user scripts located in ~/bin :"
bin_directory_content =Dir.entries("/root/bin").delete_if {|x| x.start_with?(".")}
blacklisted_bin_content=%w(ArrayToTable.rb jlist odkladiste jmore jailCreateEpair.sh)
puts (bin_directory_content-blacklisted_bin_content).join(",")
# It prints centered table in console, does not count color changing sequences as characters.
class Array
def to_table(header: true)
column_sizes = self.reduce([]) do |lengths, row|
row.each_with_index.map{|iterand, index| [lengths[index] || 0, iterand.to_s.gsub(/\e[^m]*m/,'').length].max}
end
head = '+' + column_sizes.map{|column_size| '-' * (column_size + 2) }.join('+') + '+'
puts head
to_print = self.clone
if (header == true)
header = to_print.shift
print_table_data_row(column_sizes, header)
puts head
end
to_print.each{ |row| print_table_data_row(column_sizes, row) }
puts head
end
private
def print_table_data_row(column_sizes, row)
row = row.fill(nil, row.size..(column_sizes.size - 1))
row = row.each_with_index.map{|v, i| v = v.to_s + ' ' * (column_sizes[i] - v.to_s.gsub(/\e[^m]*m/,'').length)}
puts '| ' + row.join(' | ') + ' |'
end
end
C_NC="\e[0m" # No Color
C_BLACK="\e[0;30m";C_GRAY="\e[1;30m";C_RED="\e[0;31m";C_LIGHT_RED="\e[1;31m";C_GREEN="\e[0;32m";C_LIGHT_GREEN="\e[1;32m";C_BROWN="\e[0;33m";C_YELLOW="\e[1;33m";C_BLUE="\e[0;34m";C_LIGHT_BLUE="\e[1;34m";C_PURPLE="\e[0;35m";C_LIGHT_PURPLE="\e[1;35m";C_CYAN="\e[0;36m";C_LIGHT_CYAN="\e[1;36m";C_LIGHT_GRAY="\e[0;37m";C_WHITE="\e[1;37m"
I prefer nice predictable names of interface pairs so had to write script for it.
Generated names of epairs will be renamed.
#! /bin/sh
jailName="$1"
# ifconfig returns new epair name as outpout
epairA="$(ifconfig epair create)"
# the second intervace has got b at the end
epairB="${epairA%?}b"
# We rename the interface from the generated name to be te same like jail's name
# The maxium lenght of network interface is about 15 character which souhld be enough
ifconfig $epairA name A$jailName
ifconfig A$jailName up
ifconfig bridge0 addm A$jailName up
ifconfig $epairB name B$jailName
# The line below would only work if called from exec.created section
#ifconfig eth0 vnet $jailName up
# insipration from https://forums.freebsd.org/threads/jails-vnet-freebsd-mastery-multiple-interfaces.70356/
# to list only epair interfaces you can you can use
# ifconfig -g epair -l
opens config of one ore more jails in vim
#! /bin/sh
vim_arguments=""
while test $# -gt 0
do
vim_arguments="$vim_arguments /jails/$1/c.conf"
shift
done
vim -p $vim_arguments
Creates new jail.
#! /bin/sh
jailName="$1"
mkdir -p /jails/$jailName
if zfs list zroot/jails/$jailName
then
echo " Dataset $jailName already exists !"
exit
else
ls -al /usr/freebsd-dist/MANIFEST
echo "I will install freeBSD version which was issued by the day the file above was last changed."
read -p "If you want to instlal newer version, remove the content of dirctory /usr/freebsd-dist/ before continuing. " REPLY
zfs create -o mountpoint=/jails/$jailName zroot/jails/$jailName
# zfs set recordsize=8K zroot/jails/$jailName
cd /jails/$jailName
# adresář rootfs to samo vytvoří
bsdinstall jail rootfs
datum=`date "+%Y-%m-%d %H:%M:%S"`
printf "$jailName{\n\$note = \"Created $datum\";\n\$ip = \"192.168.144.XXX/24\";\n}">/jails/$jailName/c.conf
echo "Doing update patches"
freebsd-update -b /jails/$jailName/rootfs fetch
freebsd-update -b /jails/$jailName/rootfs install
echo "Do not forget to change ip adress in /jails/$jailName/c.conf"
echo "Remember to setup firewall."
echo "Install apps for example by \"pkg install screen vim mc\" inside."
fi
destroys a jail
#! /bin/sh
jailName="$1"
if ! zfs list zroot/jails/$jailName
then
echo " dataset zroot/jails/$jailName does not exists "
exit
fi
service jail stop $jailName
read -p "Are you sure you want to delete $jailName ? " REPLY
echo # (optional) move to a new line
if [ "$REPLY" == "Y" ] || [ "$REPLY" == "y" ]
then
zfs destroy zroot/jails/$jailName
sleep 2
rmdir /jails/$jailName
fi
====== Jail config files ======
$bridge = "bridge0";
$gateway = "XX.XX.XX.XX";
# PATH/HOSTNAME
path = "/jails/${name}/rootfs";
host.hostname = "${name}";
# VNET/VIMAGE
vnet;
vnet.interface = "B$name";
exec.prestart = "/root/bin/jailCreateEpair.sh $name";
# have to remove the mask part of ip in order to by able to use the ip in firewall rules
exec.prestart += "(echo -n '# $note\next_if = \"B$name\"\next_ip = \"' && echo -n '${ip}'|cut -d \"/\" -f 1|tr -d '\n' && echo -n '\"\n')> ${path}/etc/pf_local_definitions.conf";
# I mount a common config file direcotry for shared configs and settings
exec.prestart += "mkdir -p ${path}/etc/ovps_shared";
exec.prestart += "mount_nullfs -o ro /etc/ovps_shared ${path}/etc/ovps_shared";
exec.start = "ifconfig B$name ${ip} up";
exec.start += "route add -net XX.XX.XX.XX/29 -interface B$name || true";
exec.start += "route add -net XX.XX.XX.XX/28 -interface B$name || true";
exec.start += "route add -net 192.168.145.0/24 81.31.45.55";
exec.start += "route add default ${gateway}";
exec.start += "/bin/sh /etc/rc";
exec.poststop = "ifconfig bridge0 deletem A$name";
exec.poststop += "ifconfig A$name destroy";
exec.release = "umount -t nullfs ${path}/etc/ovps_shared";
# STARTUP/LOGGING
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;
.include "/jails/*/c.conf";
Each jail has got very smal own config.
For example
pkg{
$note = "Created 2025-06-17 11:14:12";
$ip = "192.168.144.152/24";
}