Ton phacochère [1] familier, bien que très sympathique, continue de faire quelques bêtises dans ta maison, bouffer les meubles, vider sa gamelle un peu partout, voire embêter tes autres colocataires.
Ne voulant pas te résoudre à l’enfermer dans une niche (chroot) trop étroite, tu décides de lui construire un enclos qui lui sera dédié et dans lequel il pourra faire ce que bon lui semble sans nuire à son entourage (continue to be root).
Pré-requis
- un terrain propice (avoir un FreeBSD sous la main, être root dessus) ;
- un peu d’espace disque, car on va dupliquer tout le userland dans l’enclos ;
- avoir les sources dans
/usr/src.
Avant d'envisager la construction
Avant de pouvoir configurer son enclos (jail), il faut déjà préparer le terrain tout autour (configurer le serveur qui va accueillir les jails pour que les daemons n’écoutent plus sur toutes les IP).
Donc, tout autour de l’emplacement de son futur enclos, on fixe les branches qui dépassent, on ramasse les feuilles mortes, on passe un coup de râteau et on laisse un emplacement le plus nickel possible.
sshd
Utilisezsshd_flags=”-oListenAddress=Public_IP”dans votre fichier/etc/rc.confou modifiez/etc/ssh/sshd_config(et redémarrez le daemon sshd par/etc/rc.d/sshd restart)
sendmail
Ajoutez
DAEMON_OPTIONS(`Addr=Public_IP’)à votre fichier de macro sendmail (fichiermc)
inetd
Si vous utilisez
inetd, il faut ajouterinetd_flags=”-a Public_IP”à votre/etc/rc.conf(attention à bien reprendre les flags existants).
named
Utilisez la directive
listen-on { Public_IP; }; dans votre fichier de configurationnamed.conf.
apache
Utilisez la directive
Listen Public_IP:80dans votre/usr/local/etc/httpd.conf
- etc.
Idem pour les autres daemons écoutant sur toutes les IP.
Un enclos en kit
Tu as acheté ton enclos au même endroit que ton abri de jardin et, bien aimablement, le fabricant a fourni une aide minimale au montage des différents éléments ; pourtant, tout n’est pas aussi simple que chez Ikea. Tu n’auras pas de notice imprimée recto-verso t’expliquant ce qu’est une vis, mais cela ne signifie pas qu’il n’y a pas de documentation !
Extrait de la page de man de jail [2]
EXAMPLES Setting up a Jail Directory Tree To set up a jail directory tree containing an entire FreeBSD distribution, the following sh(1) command script can be used: D=/here/is/the/jail cd /usr/src mkdir -p $D make world DESTDIR=$D make distribution DESTDIR=$D mount_devfs devfs $D/dev
Si tu as compilé ton système récemment, tu peux remplacer make world DESTDIR=$D par make installworld DESTDIR=$D ce qui te fera économiser tout le temps de la compilation.
Une méthode plus Gruik consiste à monter une image ISO de FreeBSD et à extraire l’environnement de base comme s’il s’agissait d’une installation d’un système :
# ggatel create 6.2-RELEASE-i386-disc1.iso ggate0 # mount -t cd9660 /dev/ggate0 /mnt # cd /mnt/6.2-RELEASE/base # mkdir -p /home/jails/gcujail # cat base.?? | tar --unlink -vxpzf - -C /home/jails/gcujail
On va créer manuellement le /dev/* du jail et y "entrer" :
# mount_devfs devfs /home/jails/gcujail/dev # jail -l -U root /home/jails/gcujail gcujail 127.0.0.1 /bin/csh
On regarde un peu dans quel environnement on se retrouve :
gcujail# uname -a FreeBSD gcujail 7.0-CURRENT FreeBSD 7.0-CURRENT #33: Sat Mar 17 19:35:45 CET 2007 root@stealth.domain.tld:/usr/obj/usr/src/sys/STEALTH i386 gcujail# ls -al -rw-r--r-- 2 root wheel 801 Jan 12 07:42 .cshrc -rw-r--r-- 2 root wheel 251 Jan 12 07:42 .profile -r--r--r-- 1 root wheel 6196 Jan 12 07:42 COPYRIGHT drwxr-xr-x 2 root wheel 1024 Jan 12 07:41 bin drwxr-xr-x 5 root wheel 512 Jan 12 07:42 boot [snip] gcujail# mount /dev/ad0s1f on / (ufs, local, soft-updates)
On s’aperçoit que le système dans le jail hérite de la version du kernel du serveur hôte malgré le userland "décalé" et que la sortie de la commande mount ne montre que la partition accueillant le jail.
De même, depuis le jail, la commande uptime montre le résultat du serveur hôte et pas du jail en lui-même.
Pour vous en convaincre, créez le fichier /etc/rc.local contenant
echo `date` >> /root/boot.log
dans le jail ; amusez-vous à faire plusieurs reboot et regarder le fichier de log et l’uptime.
Pour le moment, le user root du jail voit tout /dev à l’identique de ce qu’il se trouve sur le serveur hôte, ce qui peut être très dangereux si vous n’avez pas confiance dans le root du jail, car il a accès à tous les disques et partitions, à la configuration de vos règles de firewall...
Et la barrière électrique ?
Dans un jail correctement configuré comme plus loin dans l’article, le root du jail ne peut pas configurer son propre firewall et, par conséquent, il ne peut pas choisir et setter ses rules.
# pfctl -s rules pfctl: /dev/pf: No such file or directory
Toute la configuration firewall se fait "au-dessus" du jail, sur le serveur hôte.
Configuration par défaut
Le système FreeBSD comporte une configuration par défaut et des exemples pour configurer votre/(vos) jail(s).
Ces informations se trouvent dans /etc/defaults/rc.conf qui est lu par les scripts de démarrage ; toutes les modifications doivent se faire dans /etc/rc.conf.
Voici les paramètres communs à tous les jails :
############################################################## ### Jail Configuration ####################################### ############################################################## jail_enable="NO" # Set to NO to disable starting of any jails jail_list="" # Space separated list of names of jails jail_set_hostname_allow="YES" # Allow root user in a jail to change its hostname jail_socket_unixiproute_only="YES" # Route only TCP/IP within a jail jail_sysvipc_allow="NO" # Allow SystemV IPC use from within a jail
Et voici la liste des paramètres spécifiques à un jail qui peuvent être repris pour la configuration de vos jails :
Voir Figure 1,
Configurons notre enclos
Voici ce que j’ai mis dans le /etc/rc.conf du serveur hôte ; l’utilisation de chaque variable sera détaillée plus loin.
# jail jail_enable="YES" jail_list="gcujail" # Emplacement du jail et son hostname jail_gcujail_rootdir="/home/jails/gcujail" jail_gcujail_hostname="gcujail.gcu.info" # ip du jail et interface qui portera l'alias jail_gcujail_ip="192.168.0.10" jail_gcujail_interface="bge0" # on veut un /dev restreint dans le jail jail_gcujail_devfs_enable="YES" jail_gcujail_devfs_ruleset="devfsrules_jail" # l'option -J permettra d'avoir des informations sur le jail jail_gcujail_flags="-l -U root -s 1 -J /var/run/jail_gcujail.jid"
Dans le rc.conf du jail, on lui indique de démarrer sshd comme d’habitude :
sshd_enable="YES" sendmail_enable="NONE"
Apparemment, il n’est plus indispensable de commenter adjkerntz -a dans /etc/crontab du système jailé.
Démarrons ce jail et regardons ce qui est vu du serveur hôte.
# /etc/rc.d/jail start gcujail Configuring jails:. Starting jails: gcujail.gcu.info. # ifconfig bge0 bge0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500 options=98<VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM> [snip] inet 192.168.0.10 netmask 0xffffffff broadcast 192.168.0.10 # mount [snip] devfs on /home/jails/gcujail/dev (devfs, local)
Ce qu’il s’est produit :
- l’alias IP
192.168.0.10est automatiquement ajouté sur l’interface précisée parjail_gcujail_ip; devfss’occupe de monter le/devdu jail au démarrage de celui-ci ;- les devices visibles dans le
/devdu jail ont réduit comme peau de chagrin et ne permettent plus d’outrepasser les devices sur le serveur hôte (faites unlspour vérifier).
Voir Figure 2
Vous aurez remarqué que le jail est démarré en securelevel [3] à 1 grâce à -s 1 de la variable jail_gcujail_flags.
Les logs
Activons les logs de la console dans le jail (fichier /home/jails/gcujail/etc/syslog.conf) :
console.info;*.err;kern.warning;auth.notice /var/log/console.log
Créons le fichier en question et redémarrons le jail :
# touch /home/jails/gcujail/var/log/console.log # /etc/rc.d/jail restart
Ce qui aura pour effet de créer un fichier /var/log/jail_gcujail_console.log sur le serveur hôte reprenant les logs de la console.
Remplir l'enclos
Afin d’économiser un peu d’espace disque, il vous semble évident que vous allez limiter au strict minimum les informations dupliquées sur le serveur hôte ainsi que dans votre (vos) jail(s).
Typiquement, on partage /usr/src et /usr/ports :
# mount -t nullfs -o ro /usr/src /home/jails/gcujail/usr/src # mount -t nullfs -o ro /usr/ports /home/jails/gcujail/usr/ports # mount | grep nullfs /usr/src on /home/jails/gcujail/usr/src (nullfs, local, read-only) /usr/ports on /home/jails/gcujail/usr/ports (nullfs, local, read-only)
Il est possible d’ajouter ces points de montage dans le /etc/fstab du serveur hôte afin que le jail y ait accès de manière permanente, mais attention, ces répertoires peuvent être supprimés depuis le jail !
Pour plus de sécurité, il est recommandé de monter /usr/src et /usr/ports/ en read-only dans le jail et de modifier la manière dont sont compilés les ports à partir du jail (pour pouvoir utiliser un /usr/ports en RO).
Si vous devez gérer plusieurs enclos, il est préférable de mettre en place un export NFS en read-only de ces répertoires vers les jails, ce qui évitera les points de montage en nullfs :
# mkdir -p /usr/obj/portsbuild /usr/obj/distfiles
Et on édite le /etc/make.conf du jail pour y ajouter les lignes suivantes :
WRKDIR=/usr/obj/portsbuild DISTDIR=/usr/obj/distfiles
Il suffit d’ajouter les ports dans le jail comme d’habitude. Ceci aura pour inconvénient d’ignorer le contenu de /usr/ports/distfiles ; n’hésitez pas à en recopier les fichiers s’ils existent, ce qui évitera de perdre de la bande passante et du temps.
Pensez à faire régulièrement le ménage dans /usr/obj/distfiles du jail sans quoi de l’espace disque sera perdu pour rien.
Sécuri-clos
Au cours de l’installation du jail, tu as constaté qu’il tenait sur une seule partition, ce qui est assez gênant quand tu veux avoir des points de montage avec des droits différents.
Par exemple, j’ai pris pour habitude d’utiliser un /tmp de taille fixe, monté en RAM, avec des droits noexec et nosuid.
Habituellement, je déclare dans /etc/rc.conf les lignes suivantes :
tmpmfs="YES" tmpsize="512m" tmpmfs_flags="-S -o noexec,nosuid"
qui ont pour résultat :
# mount | grep tmp /dev/md0 on /tmp (ufs, local, noexec, nosuid)
Mais cela ne fonctionne pas dans gcujail tout simplement parce que les commandes mdconfig n’ont pas accès aux devices /dev/md* depuis le jail !
Les ajouter serait une fausse bonne idée, puisque cela donnerait accès aux devices md* appartenant au serveur hôte depuis l’intérieur du jail.
Oh, tu peux essayer de bricoler le /etc/fstab du jail, si tu arrives à quelque chose de concluant tu es prié d’écrire à Pinpin (ou à l’auteur).
La solution, peu gracieuse, trouvée pour l’instant, est la suivante :
- sur le serveur hôte, on crée un fichier vide d’une taille fixe :
# dd if=/dev/zero of=/home/jails/gcujail.tmp bs=1m count=512 512+0 records in 512+0 records out 536870912 bytes transferred
Edition du fichier /etc/fstab.gcujail (qui sera pris par défaut par le script jail) :
Voir Figure 3
et on ajoute la gestion du mount dans le fichier /etc/rc.conf du serveur hôte pour ce jail :
jail_gcujail_mount_enable="YES"
Par défaut, le fichier fstab du jail pris en compte par la variable jail_gcujail_fstab sera /etc/fstab.gcujail.
On redémarre le jail :
# /etc/rc.d/jail restart # mount | grep md /dev/md1 on /home/jails/gcujail/tmp (ufs, local, noexec, nosuid)
Le /tmp de notre jail est bien d’une taille limitée et monté avec des droits moins permissifs.
Note
Un inconvénient de cette méthode est que bien qu’un /etc/rc.d/jail stop démonte le /tmp du jail, cela ne libère pas le device md qui y était associé et il faut le faire à la mimine depuis le serveur hôte !
Quota-clos
Jusqu’à présent, ton phacochère est tout seul dans son enclos. Il peut le décorer comme il lui plaît et ajouter ce dont il a besoin, mais, surtout, il peut continuer à prendre tout l’espace disponible dans son enclos.
Il y a au moins deux solutions pour résoudre ce problème :
- mettre les répertoires accueillant les jails dans des partitions physiques et donc de taille fixe ;
- utiliser l’astuce d’un
filesystemsur un fichier comme ce qu’on a fait avec/home/jails/gcujail.tmp, ce système de fichiers étant monté depuis le système hôte avant le démarrage du jail.
Manager plusieurs enclos
Divers programmes sont disponibles dans les ports afin de manager plusieurs jails plus facilement, liste évidemment non exhaustive :
# make search name=jail | grep -E "^Port|^Path|^Info" Port: jailaudit-1.2 Path: /usr/ports/security/jailaudit Info: Script to generate portaudit reports for jails Port: ezjail-2.0.1 Path: /usr/ports/sysutils/ezjail Info: A framework to easily create, manipulate and run FreeBSD jails Port: jailadmin-1.8_2 Path: /usr/ports/sysutils/jailadmin Info: A system for managing a set of named jails Port: jailctl-0.71 Path: /usr/ports/sysutils/jailctl Info: Jail management tool Port: jailer-1.1.2 Path: /usr/ports/sysutils/jailer Info: Manage FreeBSD jail startup, shutdown and console Port: jailuser-1.9_1 Path: /usr/ports/sysutils/jailuser Info: Builds a chrooted environment Port: jailutils-1.0 Path: /usr/ports/sysutils/jailutils Info: Several utilities for managing jails Port: p5-BSD-Jail-Object-0.02 Path: /usr/ports/sysutils/p5-BSD-Jail-Object Info: An object oriented perl interface to jail(2)
Pour une gestion plus facile des jails depuis le serveur hôte, je conseille l’installation des ports jailadmin et jailutils ; l’upgrade des enclos se fait de la même manière qu’un serveur FreeBSD (y compris pour le mergemaster) sauf qu’on change la variable $DESTDIR pour pointer dans le répertoire du jail à mettre à jour.
Figure 1
#
# To use rc's built-in jail infrastructure create entries for
# each jail, specified in jail_list, with the following variables.
# NOTES:
# - replace 'example' with the jail's name.
# - except rootdir, hostname and ip, all of the following variables may be made
# global jail variables if you don't specify a jail name (ie. jail_interface).
#
#jail_example_rootdir="/usr/jail/default" # Jail's root directory
#jail_example_hostname="default.domain.com" # Jail's hostname
#jail_example_ip="192.168.0.10" # Jail's IP number
#jail_example_interface="" # Interface to create the IP alias on
#jail_example_exec_start="/bin/sh /etc/rc" # command to execute in jail for starting
#jail_example_exec_afterstart0="/bin/sh command" # command to execute after the one for
# starting the jail. More than one can be
# specified using a trailing number
#jail_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in jail for stopping
#jail_example_devfs_enable="NO" # mount devfs in the jail
#jail_example_fdescfs_enable="NO" # mount fdescfs in the jail
#jail_example_procfs_enable="NO" # mount procfs in jail
#jail_example_mount_enable="NO" # mount/umount jail's fs
#jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail
#jail_example_fstab="" # fstab(5) for mount/umount
#jail_example_flags="-l -U root" # flags for jail(8)
Figure 2
# jls
JID IP Address Hostname Path
6 192.168.0.10 gcujail.gcu.info /home/jails/gcujail
# ps auxwww | grep J
root 2392 0.0 0.1 1376 1052 ?? SsJ 8:06PM 0:00.00 /usr/sbin/syslogd -s
root 2448 0.0 0.3 3524 3084 ?? IsJ 8:06PM 0:00.00 /usr/sbin/sshd
root 2455 0.0 0.1 1388 1064 ?? IsJ 8:06PM 0:00.00 /usr/sbin/cron -s
Figure 3
# Device Mountpoint FStype Options Dump Pass# md /home/jails/gcujail/tmp mfs rw,-S,noexec,nosuid,-F/home/jails/gcujail.tmp 2 0
Conclusion
Et voici un petit tour d’horizon sur les jails avec, je l’espère, une présentation qui sort un peu des howtos classiques qu’on trouve sur différents sites web.
Références
[1] http://fr.wikipedia.org/wiki/Phacochère
[2] http://www.freebsd.org/cgi/man.cgi?query=jail&sektion=8
[3] http://www.freebsd.org/cgi/man.cgi?query=securelevel&sektion=8


