Jail FreeBSD

De Diablotins.org.


Confiner des processus, ou comment mettre un autre FreeBSD dans votre FreeBSD
Image:brush.png
Une «Jail» est, comme son nom l'indique, un endroit où l'on va confiner (emprisonner) des processus afin qu'ils n'empiètent pas sur le territoire de leur hôte.

Ce document se propose d'en expliquer le principes et la mise en œuvre dans de multiples cas, tel que des services chrootés dans des environnements clos et restreints, ou des machines virtuelles. La commande jail(8) et l'appel système associé jail(2) existent depuis FreeBSD 4.0.
Le système utilisé lors de la rédaction est un FreeBSD 6.2; la branche 7 ne semble pas présenter de différences.



Sommaire

Introduction

Le modèle Unix des droits est simple mais rapidement insuffisant pour faire face à des situations complexes où l'on doit déléguer certains droits d'administrations.
L'ajout d'un contrôle d'accès fin améliore les choses, mais au prix de la complexité de l'administration et de l'implémentation.
Les Jails permettent de partager l'environnement tout en gardant le modèle simple des droits Unix.
Dans une Jail, les utilisateurs sont confinés dans leur prison, le super-utilisateur (root) conserve son rôle mais ses actions y sont limitées.

La solution des jails

Cette solution partage l'environnement général, -les systèmes de fichiers, les ressources réseaux, etc.- entre les processus.

Processus.
Un processus emprisonné et ses descendant ne peuvent pas sortir de leur prison.
Un processus emprisonné ne peut pas agir sur un processus hors de sa prison.
Root reste un utilisateur privilégié mais ses droits sont très fortement limités 

interdiction de créer des nœuds de périphériques
pas de possibilité de monter / démonter un système de fichier
pas de possibilité de modifier les routes réseaux
etc...

En résumé, seules les actions impactant la jail sont autorisées.

Système de fichiers.
Seule l'arborescence de la prison est visible (comme chroot).
Réseau.
Chaque jail est attachée à une adresse IP(v4) et les processus emprisonnés ne peuvent que s'attacher à cette adresse.
En cas d'utilisation de raw socket (interdites par défaut), les adresses IP sont forcées.
Ces limitations ne sont implémentées que pour les sockets IPv4 (ou locales), tout autre domaine est interdit.
[source: <http://docs.freebsd.org/44doc/papers/jail/jail.html>]

Interlude

Exécution d'une jail

Cet exemple lance un shell (sh) dans une jail sur /rescue (les binaires dans /rescue sont liés statiquement), l'adresse IP est 192.168.2.1 et le nom d'hôte «roxette-1». Il nous faut une adresse IP pour la prison, nous utilisons un alias sur une interface, ici lo0 :

# ifconfig lo0 alias 192.168.2.1 netmask 0xffffffff

Depuis la branche 8, cet alias ne semble plus nécessaire.

Il nous faut aussi les nœuds de périphériques (devfs) dans le système de fichier de la jail, comme pour un chroot.

# mkdir /rescue/dev
# mount -t devfs dev /rescue/dev

On lance sh(1) dans une prison.

# jail /rescue roxette-1 192.168.2.1 sh

(à partir de ce point, nous sommes dans la prison)

# ./ls

on est à la racine (ie chrooté dans /rescue)

# ./ps
 PID  TT  STAT      TIME COMMAND
53606  p4  SJ     0:00.00 sh
53610  p4  R+J    0:00.00 ./ps

(on notera que les processus root tournant sur l'hôte principal n'apparaissent pas). Le “J“ dans la colonne STAT indique que le processus est emprisonné.
Comment s'appelle-t-on ?

# ./hostname
roxette-1

Testons le confinement de root :

# ./mount
/dev/ad0s3a on / (ufs, local, noatime)
# ./umount /dev/ad0s3a
umount: unmount of / failed: Operation not permitted

La prison vue de l'hôte

Dans ce chapitre, nous somme root sur l'hôte (i.e. pas dans la prison).
jls(8) permet de lister les jails actives et leur “JID“ :

# jls
   JID  IP Address      Hostname                      Path
     9  192.168.2.1     roxette-1                     /rescue

Les processus confinés sont également marqués “J” avec ps :

# ps
  PID  TT  STAT      TIME COMMAND
 1467  v0  Is+    0:00,00 /usr/libexec/getty Pc ttyv0
[...]
53629  p4  I+J    0:00,00 /sh

Notez que les processus root confinés sont visibles.
jexec(8) permet de lancer un processus dans une jail existante à partir de l'hôte :

# jexec 9 ./ps
  PID  TT  STAT      TIME COMMAND
53629  p4  I+J    0:00.00 /sh
53705  p7  R+J    0:00.00 ./ps

Maintenant retournons dans notre sh(1) emprisonné et quittons le shell:

# exit

Retour sur l'hôte.

# jls

La jail est détruite automatiquement. Une jail est détruite lorsque plus aucune ressource ne l'utilise (par refcounting(9)). Ça peut parfois être long, par exemple lorsque une socket TCP en confinement est toujours active.

Utilisations

Le petit exemple précédent montre les caractéristiques de jail(8). On peut heureusement faire des choses plus utiles. Jail permet de mettre en œuvre des machines virtuelles FreeBSD complètes (voire un linux), ou bien de confiner un service particulier à la manière de chroot, mais sur un environnement complet.

Machines virtuelles

Il faut bien noter que jail n'est pas une vraie virtualisation, il n'y a pas plusieurs machines virtuelles qui se partagent un même matériel. Jail n'effectue qu'un confinement des processus, il n'y a qu'un système (un seul noyau). La virtualisation est seulement -euh- virtuelle.
L'implémentation de jail est simple, lorsqu'un processus est confiné, une structure "prison" contenant les données de la jail est créée et liée au processus et à ses descendants. Il n'y a pas moyen de modifier cette structure après la création. Ensuite un chroot est effectué à la racine de la prison. Le confinement est réalisé par des tests dans le noyau. Le surcoût en terme de puissance est donc extrêmement faible.

Par exemple pour un processus confiné, la possibilité d'envoyer un signal est limité à un processus de la même jail, cela est fait par un simple test « prison_check() »

 
sys/kern/kern_proc.c
int
cr_cansignal(struct ucred *cred, struct proc *proc, int signum)
{
        int error;
 
        PROC_LOCK_ASSERT(proc, MA_OWNED);
        /*
         * Jail semantics limit the scope of signalling to proc in the
         * same jail as cred, if cred is in jail.
         */
        error = prison_check(cred, proc->p_ucred);
        if (error)
                return (error);
[...]

Isoler un service

Ici, seul FreeBSD peut proposer ce service. Là où sur la majorité des systèmes UNIX, on ne peut que chrooter le service sensible ; sur FreeBSD on va pouvoir le confiner, assurant ainsi un niveau de sécurité plus élevé. Le mécanisme peut être mis en œuvre sur beaucoup de services.
Nous en verrons divers exemples et réalisations. Les cas les plus intéressants sont bien sûr les accès SSH, FTP, les services comme un DNS, et tout services à ressources closes. Nous verrons les différences de mise en œuvre de chaque cas.

Mise en œuvre

Ce chapitre va exposer la commande Jail, et les contraintes de configuration qu'elle entraîne dans le cas le plus général. Nous allons voir que l'utilisation de Jail impose des règles de configuration de l'hôte strict au niveau réseau.
Nous commencerons par voir l'aspect topologique, puis la configuration du système et des services obligatoires. D'une manière générale et je le redirais, il doit y avoir un minimum de services qui tournent sur l'hôte.

Topologies

On considérera pour notre document une machine physique ayant deux interfaces réseaux : xl0 et xl1 (ces noms de périphériques sont ceux des cartes réseaux typiques de 3COM 3cXXX). Il est intéressant quelque soit le type de jail que l'on veut mettre en œuvre d'avoir deux interfaces Ethernet sur la machine hôte, nous verrons pourquoi dans le chapitre sur la sécurisation de la Jail depuis l'hôte. L'hôte aura pour IP : 192.168.1.1 sur le réseau 1. Les jails prendront IP sur le réseau 2. (Deux réseaux physiques peuvent êtres mis en place améliorant ainsi la sécurité) D'où pour une première Jail : L'IP sera 192.168.2.1.

L'utilisation d'une interface physique n'est pas obligatoire, on peut très bien utiliser des alias IP sur une interface logique, par exemple lo0.

L'hôte

Ici, encore, et comme dans la majeur partie du reste de cette documentation, il est important de bien maîtriser la configuration de FreeBSD, et donc je vous renvoie a la documentation sur la configuration de FreeBSD.

Pour rappel, le processus d'initialisation de FreeBSD s'appelle RC et est configuré par l'intermédiaire du fichier /etc/rc.conf.

Voici les lignes qu'il faut modifier au minimum (si vous avez suivi la procédure de configuration et d'installation prescrite).

  # vi /etc/rc.conf
  --
  [...]
  inetd_flags= "-wW -a 192.168.1.1"
  sendmail_enable= "NO"
  syslogd_flags="-s"
  [...]

Nous allons détailler chaque ligne modifiée ou ajoutée à rc.conf. La page de man de jail(8) décrit plus en détail la procédure dans le paragraphe «Setting up the Host Environment».

Inetd

Par défaut les démons de l'hôte écoutent sur toutes les IP, il faut donc les configurer pour qu'ils n'écoutent que sur leur IP qui n'est pas attribuée à une jail.

inetd_flags= "-wW -a 192.168.1.1"

Le flag passé à Inetd l'oblige à n'écouter que sur l'IP réel de la première interface et non sur les alias ou l'adresse de la deuxième interface que l'on réservera aux jails de notre système.

Et si on n'utilise pas Inetd, il vaut encore mieux l'arrêter.

#inetd_enable="YES"  commentez la ligne

Sendmail

Pour sendmail(8), soit on coupe le démon, soit on modifie sa configuration. (Dans cette documentation on ne verra pas comment faire cette configuration) On ne chargera pas le démon.

  sendmail_enable= "NO"

Syslogd

Syslogd reçoit le drapeau “-s” qui lui indique de travailler en mode sécurisé, c'est-à-dire qu'il ne journalise plus les machines distantes. Il est important de faire travailler syslogd(8) en mode sécurisé sur l'hôte, une machine spécifique par réseau doit réaliser la tâche de regrouper les logs venant des autres machines.

  syslogd_flags="-s"

Réseau

Il nous faut maintenant créer un alias IP pour notre Jail. (Exemple pour un serveur Intel ISC ou Dell POWER EDGE) Device : xlX La commande manuelle est :

  # ifconfig xl0 inet 192.168.1.1 netmask 255.255.255.0
  # ifconfig xl1 inet 192.168.2.1 netmask 255.255.255.0
  # ifconfig xl1 inet alias 192.168.2.2 netmask 255.255.255.255
  [...]

Pour que cela soit fait automatiquement au démarrage :

  # vi /etc/rc.conf
  --
  [...]
  # carte ethernet 1 hote
  ifconfig_xl0="inet 192.168.1.1 netmask 0xffffff00"
  #carte ethernet 2 (jails)
  ifconfig_xl1="inet 192.168.2.1 netmask 0xffffff00"
  ifconfig_xl1_alias0="inet 192.168.2.2 netmask 0xffffffff"
  ifconfig_xl1_alias1="inet 192.168.2.3 netmask 0xffffffff"

Pour plus d'informations voir le man de ifconfig et de rc.conf et la documentation sur l'installation de FreeBSD et sa configuration.

démons standard

Aller à l'essentiel

Il ne faut faire tourner qu'un minimum de démons sur la machine hôte, pour être précis seul SSH devrait tourner.

SSH

Pour sshd(8), le principe est le même que pour inetd, il ne doit écouter que sur l'IP de l'hôte. Dans le fichier de configuration de ManPage(5), ajoutez:

  # vi /etc/ssh/sshd_config
  --
  [...]
  ListenAddress 192.168.1.1
  [...]

Routes

Vous devez alors vérifier si il n'existe pas de route existante entre vos jails du réseau 2 et votre hôte du réseau 1, pour cela il y a deux choses à faire :

  • Vérifier qu'il n'a pas de forward entre les deux interfaces réseaux ethernet :

Tapez :

   # sysctl net.inet.ip.forwarding

Si la variable est à 1, alors passez la à 0 en tapant :

   # sysctl net.inet.ip.forwarding=0

Et vérifiez l'absence d'un

gateway_enable="YES" 

dans /etc/rc.conf ou la mise à un dans /etc/sysctl.conf.

  • Vérifier les routes via la commande :
   # netstat -rn

S'il existe des routes entre les deux réseaux, supprimez les avec la commande :

route delete

messages d'erreurs ARP

Dans le cas où les Jails sont dans le même réseau, les deux interfaces vont répondre l'une pour l'autre aux requêtes ARP sans que ce soit réellement un problème, mais FreeBSD va journaliser ces erreurs ARP, au risque de miner le fichier journal /var/log/messages Pour les supprimer il faut cette fois-ci forcer à 0 la variable:

# sysctl net.link.ether.inet.log_arp_wrong_iface=1

Puis:

# echo "net.link.ether.inet.log_arp_wrong_iface=1" >> /etc/sysctl.conf

Une machine virtuelle

Ici, l'objectif est de créer une machine virtuelle similaire à l'hôte. Un utilisateur d'une de ces machines virtuelles utilise donc un système FreeBSD complet mais confiné dans une prison. Dans le cas où l'utilisateur est root, ses droits sont en outre fortement limités.

Le principe est simple. Lorsqu'un système FreeBSD démarre, après le lancement de init(8), il exécute le script /etc/rc(8). Ce script poursuit l'initialisation de la machine et le lancement les démons en suivant la configuration données par /etc/rc.conf. Ici on fait la même chose sauf que ce script rc est lancé dans une jail.

À la racine de la prison, il faut recopier le système FreeBSD complet (la base). Cela se fait par un

make installworld

Si on ne dispose pas d'un monde déjà compilé il faudra le faire avant (voir le Handbook pour la compilation du monde : make buildworld).

Construction

La jail aura pour IP 192.168.2.1. Par convention, la racine d'une jail est souvent son adresse IP. La racine sera /jails/192.168.2.1:

# mkdir /jails/192.168.2.1
# cd /usr/src
# make installworld DESTDIR=/jails/192.168.2.1
# make distribution DESTDIR=/jails/192.168.2.1

L'arborescence créée :

# cd /jails/192.168.2.1
# ls
.cshrc     bin/       etc/       media/     rescue/    sys@       var/
.profile   boot/      lib/       mnt/       root/      tmp/
COPYRIGHT  dev/       libexec/   proc/      sbin/      usr/

On songe à garder une note dans la jail de la date de création et de la version du système utilisé pour la construire. C'est utile lors des mises à jours. Par convention perso :

   # echo `date` `uname -a` > /jails/192.168.2.1/etc/VERSION

On fait une configuration grossière de la jail comme l'indique man 8 jail(8):

   # vi /jails/192.168.2.1/etc/rc.conf
   sendmail_enable="NONE"
   rpcbind_enable="NO"
   network_interfaces=""

un fstab vide :

   # touch /jails/192.168.2.1/etc/fstab

fuseau horaire

   # echo "Europe/Paris" > /jails/192.168.2.1/etc/timezone

crontab système, adjkerntz(8) à enlever:

   # vi jails/192.168.2.1/etc/crontab

(optionnel) recopie des configs perso :

/etc/csh.cshrc
/root/.cshrc
/etc/make.conf
/etc/login.conf
...

ATTENTION : LA CONFIGURATION N'EST PAS TERMINÉE !

Il faut aussi configurer les mots de passes utilisateurs (en particulier celui de root), les aliases mails et toutes ces sortes de choses. La méthode est la même que pour l'hôte principal et doit être effectuée avec le même soin. S'il y a plusieurs jails, modifier les heures des crons "periodic" peut aussi être utile (ils démarrent tous à 3h00 par défaut).

Exécution

Les scripts de démarrage permettent d'automatiser le lancement des jails, y compris le montage des systèmes de fichiers spéciaux comme devfs. Il suffit de le paramétrer dans /etc/rc.conf :

/etc/rc.conf
# alias IP sur xl1 pour la jail "test1"
ifconfig_xl1_alias0="inet 192.168.2.1 netmask 255.255.255.255"

##############################################################
### Jail Configuration #######################################
##############################################################
jail_enable="YES"      	# Set to NO to disable starting of any jails
jail_list="test1"     	# Space separated list of names of jails
jail_set_hostname_allow="NO" # 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
jail_stop_jailer="NO"      # Only stop jailer. Requires jail_*_exec be 
                           # set to use sysutils/jailer port to start 
                           # the jail.

# To use rc's built-in jail infrastructure create entries for
# each jail, specified in jail_list, with the following variables.
# ----------------------------------------------------------------------
# test

jail_test1_rootdir="/jails/192.168.2.1"  # Jail's root directory
jail_test1_hostname="roxette-21"	 # Jail's hostname
jail_test1_ip="192.168.2.1"              # Jail's IP number
jail_test1_exec="/bin/sh /etc/rc"        # commands to execute in jail
jail_test1_devfs_enable="YES"            # mount devfs in the jail
jail_test1_fdescfs_enable="NO"           # mount fdescfs in the jail
jail_test1_procfs_enable="NO"            # mount procfs in jail
jail_test1_devfs_ruleset="devfsrules_jail" # devfs ruleset to apply to
                                           # jail

Nous avons configuré une prison «test1», d'adresse IP «192.168.2.1», sur la racine qui nous avons précédemment construite. Le paramètre

devfs_ruleset="devfsrules_jail" 

est important : le système de fichier devfs monté sur la prison ne contiendra que les fichiers spéciaux strictement indispensables. Autoriser des périphériques ici pourrait avoir de graves conséquences sur la sécurité de l'hôte.


La jail est lancée ou arrêtée par le script /etc/rc.d/jail:

# /etc/rc.d/jail start test1 

(si aucun nom de jail n'est spécifié, le script agit sur toutes les jails)

# jls
  JID  IP Address      Hostname                      Path
    3  192.168.2.1     roxette-21		     /jails/192.168.2.1
# /etc/rc.d/jail stop test1
# jls

Relançez là:

# /etc/rc.d/jail start test1 
# jls
   JID  IP Address    Hostname        Path
     4  192.168.2.1   roxette-21      /usr/jails/192.168.2.1

Entrez, (id = 4):

# jexec 4 csh

ici on est dans la prison. Jetez un œil :

# ls /dev
fd    ptyp0 ptyp3 ptyp6 ptyp9 ptypc stderr ttyp0  ttyp3 ttyp6 ttyp9 ttypc
zero  log   ptyp1 ptyp4 ptyp7 ptypa ptypd  stdin  ttyp1 ttyp4 ttyp7 ttypa
ttypd null  ptyp2 ptyp5 ptyp8 ptypb random stdout ttyp2 ttyp5 ttyp8 ttypb
urandom

Grâce au jeu de règles spécifié dans /etc/rc.conf (devfsrules_jail), il n'y a que le strict nécessaire. En particulier aucun périphérique matériel n'est accessible, kmem(4) est absent, etc.

# mount
/dev/ad0s3f on / (ufs, local, noatime, soft-updates)

Configurons rapidement un accès ssh pour se loguer à travers le réseau et non pas à partir de l'hôte principal.

   # echo sshd_enable=\"YES\" >> /etc/rc.conf
   # /etc/rc.d/sshd start
   Generating public/private rsa1 key pair.
   Your identification has been saved in /etc/ssh/ssh_host_key.
   Your public key has been saved in /etc/ssh/ssh_host_key.pub.
   [...]
   Starting sshd.

Et voilà ! Il ne reste plus qu'à créer des comptes utilisateurs, permettre le login par ssh si vous souhaitez que l'on puisse se connecter dans la jail directement (avec jexec on peut rentrer dans une jail à partir de l'hôte). Vous pouvez installer des ports, exactement comme sur l'hôte principal.

Il est possible d'utiliser un autre moyen pour se loguer mais ssh est le plus sécurisé.

Mise à jour

Le monde utilisé par une jail doit être synchronisé par rapport au monde de l'hôte principal et du noyau.
La mise à jour s'effectue exactement comme la mise à jour de l'hôte:

# cd /usr/src
# mergemaster -p -D /jails/192.168.2.1
# make installworld DESTDIR=/jails/192.168.2.1
# mergemaster -D /jails/192.168.2.1

Il est conseillé d'arrêter la jail durant cette procédure.
Il n'est pas possible d'effectuer la mise à jour depuis la jail,

make installworld

doit changer des ACL et cela n'est pas autorisé par défaut.

Ports

Là encore, il faut voir la jail comme une machine virtuelle et la méthode ne diffère pas de la méthode habituelle.


Certains logiciels peuvent ne pas fonctionner dans une jail, parce qu'ils nécessitent l'accès à un périphérique (ex: cdrecord) ou à une autre ressource inaccessible (raw socket par exemple). Dans ce cas il faut voir au cas par cas l'ajout des nœuds de périphériques dans le /dev de la jail ou s'il est possible de modifier les limitations imposées aux jails : il y a une option pour autoriser les raw sockets par exemple.


Dans tous les cas c'est à l'administrateur de l'hôte principal de décider.

Isoler un service

Dans de nombreux cas on voudra seulement isoler un service, ou créer un mini système pour des utilisations très spécifiques.
(Nous verrons le cas d'un accès SSH sécurisé et limité). L'usage de tels procédés à deux avantages :

  • Le premier est d'isoler complètement le service de la machine hôte.
  • Le second est de permettre une disponibilité réduite de commandes dans la Jail, et la suppression des commandes les plus sensibles sans limiter la maintenabilite de la structure de service en elle-même. On choisit les commandes que l'on veut.

Précautions

On aperçoit déjà la complexité de mise en oeuvre de telles structures.
D'une manière générale, plus le système à isoler a de dépendances quelconques vers différentes ressources, plus sa mise en jail devient difficile.
Il n'est pas possible de lancer des services qui accèdent à des fichiers sans quelques précautions. Il faut évidement que les fichiers soient dans l'arborescence chrooté. Sinon il ne sera pas possible d'y accéder, ils seront même entièrement invisibles dès le lancement de la Jail.

Méthode

D'une manière générale, on peut tracer un modèle structurel pour mettre en œuvre un service sous Jail. La partie suivante va décrire non pas les éléments à mettre en œuvre, car il est impossible d'en dresser une liste, et elle est même dépendante du service à enfermer. Mais va décrire les méthodes et outils pour la réalisation de l'édifice, car il s'agit bien ici d'une construction, on va installer le service morceau par morceau.
La méthode est basée sur une progression de proche en proche, de lien en lien, d'assemblage et de réassemblage empirique, régit par les règles de structures du système. On voit se profiler la démarche à suivre, nous allons reconstruire l'arborescence de FreeBSD dans un espace disque que l'on va chrooter.
Dans la mesure du possible, il va falloir essayer de créer le service statiquement, ou rapatrier les bibliothèques et les poser dans le répertoire où le binaire les attend relativement au point de chroot.

Les outils

Les outils les plus utiles pour notre réalisation sont :

  • which/whereis : qui sert à localiser un binaire dans le $PATH.
  • locate : qui permet la recherche de fichier. (cette commandes implique une base locate a jour : voir Handbook sur locate.updatedb)
  • ldd(1) : qui liste les dépendances d'un binaire exécutable.
  • cp/mv/? : toutes les commandes de manipulation des fichiers.
  • chown/chmod : toutes les commandes de manipulation des droits et de possession de fichier.
  • DESTDIR, que soit pour un port ou le monde
make install DESTDIR=/usr/home/jails/MonService

va tout installer à partir de /usr/home/jails/MonService/

Compilation

Pour la création du service, si le port FreeBSD, n'existe pas, ou si pour une raison quelconque vous devez ou voulez compiler le programme vous-même, les méthodes à suivre seront souvent similaire.

Vous devrez configurer le Makefile, en changeant le préfixe, (emplacement de dépôts de répertoires), préciser le linkage statique du binaire, et s'assurer du répertoire des fichiers de configuration. Toutes ses opérations se feront le plus souvent depuis le script Shell :

  # ./configure

Grâce à des paramètres tel que :

--prefix-cmd= 
pour préciser le chemin d'une commande utile
--prefix-bin= 
pour préciser le chemin des binaires.
--config-dir= 
pour le chemin des fichiers de configuration
--static 
pour linker le binaire en statique.


Il faut bien prendre en compte que, depuis la jail, la racine est le point de chroot.Donc si la jail est montée depuis /jails/192.168.2.1/ , le répertoire /jails/192.168.2.1/etc de l'hôte, correspond au répertoire /etc dans la jail.

arborescence

Dans ce domaine, mes conseils n'ont pas valeurs de références mais de mises en garde.
Je ne peux que vous pousser à conserver la structure des répertoires d'une machine FreeBSD (voir man hier), ne cherchez pas la complication.
Je ne suis pas sûr que changer les emplacements des fichiers de configurations ou des binaires pour des raisons de sécurité soit nécessaire. L'aspect atypique d'une jail en terme de structure est déjà suffisant. Ne vous compliquez pas trop la tâche, à vous et aux personnes qui devront maintenir et faire évoluer le service, mais jouez plutôt avec les fichiers pour vous assurez qu'il n'y a pas de trou.
Le but de la Jail n'est pas de cacher les fichiers de configuration du service, mais de restreindre l'univers des possibilités d'attaques d'un système en le laissant seul visible, et d'autre part voir ne veux pas dire modifier.
N'oubliez pas du fait du mécanisme de Jail, les acquis UNIX, et adoptez des règles rigoureuses sur le positionnement des droits sur les fichiers.
En voulant tromper le pirate en changeant les chemins des fichiers vous gagnerez certes du temps sur une attaque, mais à quel prix, de plus vous ne ferez que gagner du temps, vous n'empêcherez pas l'attaque.
Avec le temps vous verrez que ce type de décisions engendrera du temps supplémentaire à la conception, à la maintenance, de gros soucis de mise à jour par des personnes tierces, et en plus un gros travail de documentation.

Mode opératoire

  • Etape 1 : repérer les binaires et les fichiers de configuration de votre service, et en faire une liste.
(Toutes les manipulations de fichiers doivent évidement être faites sous l'utilisateur root)
  • Etape 2 : trouver les chemins de chaque fichier ainsi que les dépendances de chaque binaires, les librairies objets partagées, etc...
(N'oubliez pas des outils comme sed et awk tout au long de ce processus).
  • Etape 3 : recréer dans la Jail, l'arborescence où vous devez poser vos fichiers, dans la majorité des cas faites des répertoires immuables.
  • Etape 4 : compiler et copier les programmes de type 'source standard'.
(ATTENTION : si vous compiler une version d'un service disponible sur l'hôte, mais spécifique à la Jail, prenez garde de bien sauvegarder les fichiers de configurations de votre hôte.)
  • Etape 5 : contrôler les droits sur les fichiers de votre Jail, faites des tests via la commande Jail, sur chacun des binaires exécutables pour vous assurer qu'il ne lui manque aucun composant, vous ne devez pas vous soucier des appels systèmes, votre Jail a évidement droit d'accès aux routines du noyau.
  • Etape 6 : créer les scripts d'initialisation de votre Jail, tant interne qu'externe, nous en verrons plusieurs types pour des cas différent, mais le modèle de ces scripts est celui de RC : - boite à état pour le lancement depuis l'extérieur - séquence Shell depuis l'intérieur de la Jail.

Quelques exemples

DNS

Dans le cas du DNS, c'est relativement simple, il suffit de faire une installation de BIND dans un répertoire du type /data/jails/dns/
(Comme vous l'avez constaté, dans le cas d'un service, on donne pour nom à la Jail, le nom du service). On recherche toutes les dépendances de named et de ndc :

  • named est le démon du DNS
  • ndc est le loader.

De base, le script rc /etc/rc.d/named peut chrooter (mais pas jailer) BIND et monter un système devfs à la racine du chroot. On pourra s'inspirer de ce script.

accès SSH

Deux scripts en Ruby sont utilisés : le premier permet de créer la jail, le second la lance.

Attention : ces scripts ont été écrit pour FreeBSD 4. Il faudra les adapter pour FreeBSD >= 5.

 
#!/usr/local/bin/ruby
 
require 'ipaddr'
 
puts 'Jail Installation and server initialisation'
puts '-------------------------------------------'
puts ''
 
# initialisation
data_path = String::new('/var/data')
data_symlink = String::new('/data')
jail_path =  String::new("#{data_symlink}/jail")
 
rc = IO::readlines("/etc/rc.conf").delete_if{|line| line !~ /ifconfig/}
rc.map!{|line| line  = /^ifconfig_.*\d+="inet\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+netmask\s+(\d{1,3}\.){3}\d{\
1,3}".*$/.match(line)[1]}
 
jail_ip = IPAddr.new "#{rc[0]}"
host_ip = IPAddr.new "#{rc[1]}"
 
 
jail_net = jail_ip.mask(24)
host_net = host_ip.mask(24)
 
table_exe = Array::new(['ls','cp','sh','pwd','vi','cat','mv','file','bash','clear','dig','echo','which','id','grep\
','cut','awk','perl','find','file','tar','gzip','diff','patch','md5','xargs','uniq','sort','sed','more','chmod','w\
c','test','touch'])
list_config_file =  Array::new(['/etc/termcap','/usr/share/misc/termcap','/usr/share/misc/magic'])
 
# methode recursive d'install des dependances et des outils
def install_in_jail(binary,jail_path)
  IO.readlines("|ldd #{binary} 2>&1").map!{|dep| dep=dep.split(/ /)[2]}.compact.delete_if {|x| x =~ /not/ }.each{|\
file_dep|
    system("mkdir -p #{jail_path}#{File.dirname(file_dep)}") unless File::exist?("#{jail_path}#{File.dirname(file_\
dep)}")
    system("cp #{file_dep} #{jail_path}#{File.dirname(file_dep)}") unless File::exist?("#{jail_path}#{file_dep}")
    install_in_jail(file_dep,jail_path)
  }
  system("mkdir -p #{jail_path}#{File.dirname(binary)}") unless File::exist?("#{jail_path}#{File.dirname(binary)}"\
)
  system("cp #{binary} #{jail_path}#{File.dirname(binary)}") if not File::exist?("#{jail_path}#{binary}")
 
end
 
# création de la jail
def create_jail_skeleton(data_path,jail_path,data_symlink)
  puts "* Building filetree for the jail."
  system("mkdir #{data_path}") if not File::exist?(data_path)
  system("ln -s #{data_path} #{data_symlink}") if not File::exist?(data_symlink)
  system("mkdir #{jail_path}") if not File::exist?(jail_path)
end
 
 
# copie des executables en liste avec les dependance dans le points de chroot
def install_executable(table_exe,jail_path)
  puts "* Installation of the jail's executables."
  table_exe.each{|exe|
    puts " => AJout de #{exe} et de ses dépendances  ..."
    if File::exist?(`which #{exe}`.chomp) then
      install_in_jail(`which #{exe}`.chomp,jail_path)
    else
      puts 'ERROR : command not found'
    end
  }
end
def create_ld_tools(jail_path)
  puts '* Installation of the ld tools and configuration files'
  system("mkdir #{jail_path}/libexec && cp /libexec/* #{jail_path}/libexec/") unless  File::exist?("#{jail_path}/l\
ibexec")
  system("mkdir -p #{jail_path}/var/run && cp /var/run/ld*.so.hints #{jail_path}/var/run/") unless  File::exist?("\
#{jail_path}/var/ru\
n")
 
end
 
def put_config(list_config_file,jail_path)
  puts '* Installation of arbitrary configuration files.'
  list_config_file.each{|config_file|
    puts " => AJout du fichier de configuration : #{config_file}"
    system("mkdir -p #{jail_path}#{File.dirname(config_file)}") unless  File::exist?("#{jail_path}#{File.dirname(c\
onfig_file)}")
    system("cp #{config_file} #{jail_path}#{File.dirname(config_file)}") unless  File::exist?("#{jail_path}#{confi\
g_file}")
  }
end
 
def make_temporary_path(jail_path)
  puts '* Building temporary paths.'
  system("mkdir #{jail_path}/tmp; chmod 1777 #{jail_path}/tmp") unless File::exist?("#{jail_path}/tmp")
  system("mkdir #{jail_path}/var/tmp; chmod 1777 #{jail_path}/var/tmp") unless File::exist?("#{jail_path}/var/tmp"\
)
 
end
 
def put_script(jail_path)
  puts '* Installing jail managing script'
  system("mkdir -p /usr/local/bin/") unless File::exist?("/usr/local/bin/")
  myscript = open("/usr/local/bin/manage_jail",'w+')
  DATA.each{|line|
    myscript.puts line
  }
  myscript.close
  system("chmod 700 /usr/local/bin/manage_jail")
end
 
# exemple pour l'install du port MySQL
def install_mysql(jail_path)
  puts '* Installing the mysql command in jail.'
  puts ' => Installing port.' if `pkg_info|grep mysql-client`.empty?
  system("cd /usr/ports/databases/mysql-client/; make install clean") if `pkg_info|grep mysql-client`.empty?
  puts ' => Installing binaries.'
  install_in_jail(`which mysql`.chomp,jail_path)
end
 
def create_sshd_config(jail_path)
  puts ' => make sshd configuraiton file'
  myconf = open("#{jail_path}/etc/ssh/sshd_config",'w+')
  myconf.puts('Port 23')
  myconf.puts('Protocol 2')
  myconf.puts('ListenAddress 0.0.0.0')
  myconf.puts('HostKey /etc/ssh/ssh_host_key')
  myconf.puts('HostKey /etc/ssh/ssh_host_dsa_key')
  myconf.puts('SyslogFacility AUTH')
  myconf.puts('PermitRootLogin no')
  myconf.puts('StrictModes yes')
  myconf.puts('RSAAuthentication yes')
  myconf.puts('PubkeyAuthentication yes')
  myconf.puts('AuthorizedKeysFile      .ssh/authorized_keys')
  myconf.puts('RhostsRSAAuthentication no')
  myconf.puts('HostbasedAuthentication no')
  myconf.puts('IgnoreUserKnownHosts no')
  myconf.puts('IgnoreRhosts yes')
  myconf.puts('PasswordAuthentication no')
  myconf.puts('PermitEmptyPasswords no')
  myconf.puts('ChallengeResponseAuthentication yes')
  myconf.puts('UsePAM yes')
  myconf.puts('GatewayPorts no')
  myconf.puts('AllowTcpForwarding no')
  myconf.puts('X11Forwarding no')
  myconf.puts('PrintMotd yes')
  myconf.puts('PrintLastLog yes')
  myconf.puts('TCPKeepAlive yes')
  myconf.puts('UseLogin no')
  myconf.puts('UsePrivilegeSeparation yes')
  myconf.puts('PermitUserEnvironment no')
  myconf.puts('Compression yes')
  myconf.puts('UseDNS yes')  
  myconf.puts('#Banner /some/path')
  myconf.puts('Subsystem       sftp    /usr/libexec/sftp-server')
  myconf.puts('PidFile /var/run/sshd.pid')
  myconf.puts('MaxStartups 10')def install_sshd(jail_path)
  myconf.close
end
 
 
def install_sshd(jail_path)
  puts '* Installing the sshd daemon in jail.'
  puts ' => Installing binaries.'
  install_in_jail(`which sshd`.chomp,jail_path)
  puts ' => Installing configuration.'
  system("mkdir -p #{jail_path}/etc/ssh") unless File::exist?("#{jail_path}/etc/ssh")
  system("cp /etc/ssh/moduli #{jail_path}/etc/ssh") unless File::exist?("#{jail_path}/etc/ssh/moduli")
  system("/usr/bin/ssh-keygen -t rsa1 -b 1024 -f #{jail_path}/etc/ssh/ssh_host_key -N ''") unless File::exist?("#{\
jail_path}/etc/ssh/ssh_host_key")
  system("/usr/bin/ssh-keygen -t dsa -f #{jail_path}/etc/ssh/ssh_host_dsa_key -N ''") unless File::exist?("#{jail_\
path}/etc/ssh/ssh_host_dsa_key")
  system("/usr/bin/ssh-keygen -t rsa -f #{jail_path}/etc/ssh/ssh_host_rsa_key -N ''") unless File::exist?("#{jail_\
path}/etc/ssh/ssh_host_rsa_key")
  create_sshd_config(jail_path) unless File::exist?("#{jail_path}/etc/ssh/ssh/sshd_config")
  system("mkdir -p #{jail_path}/var/empty") unless File::exist?("#{jail_path}/var/empty")
end
 
def start_pseudo_fs(jail_path)
  puts '* Starting pseudo FS.'
  puts ' => proc in host.' unless File::exist?("/proc")
  system("mkdir /proc") unless File::exist?("/proc")
  system("echo 'proc /proc procfs rw 0 0' >> /etc/fstab") if `grep ' /proc' /etc/fstab`.empty?
  system("mount /proc") if `mount |grep '/proc '`.empty?
  puts ' => dev in host.' unless File::exist?("#{jail_path}/dev")
  system("mkdir #{jail_path}/dev") unless File::exist?("#{jail_path}/dev")
  system("echo 'dev #{jail_path}/dev devfs rw 0 0' >> /etc/fstab") if `grep #{jail_path}/dev /etc/fstab`.empty?
  system("mount #{jail_path}/dev") if `mount |grep '#{jail_path}/dev '`.empty?
  puts ' => proc in jail.' unless File::exist?("#{jail_path}/proc")
  system("mkdir #{jail_path}/proc") unless File::exist?("#{jail_path}/proc")
  system("echo 'proc #{jail_path}/proc procfs rw 0 0' >> /etc/fstab") if `grep #{jail_path}/proc /etc/fstab`.empty\
?
  system("mount #{jail_path}/proc") if `mount |grep '#{jail_path}/proc '`.empty?
end
 
def install_pam(jail_path)
  puts '* Installing pam'
  puts ' => Puts libraries module PAM.'
  install_in_jail("/usr/lib/pam_unix.so",jail_path)
  puts ' => Puts configuration files.'
  system("mkdir -p #{jail_path}/etc/pam.d") unless File::exist?("#{jail_path}/etc/pam.d")
  if not File::exist?('#{jail_path}/etc/pam.d/sshd') then
    sshd_pam = open("#{jail_path}/etc/pam.d/sshd",'w')
    sshd_pam.puts('auth            required        pam_unix.so             no_warn try_first_pass')
    sshd_pam.puts('account         required        pam_unix.so')
    sshd_pam.puts('session         required        pam_unix.so')
    sshd_pam.puts('password        required        pam_unix.so             no_warn try_first_pass')
    sshd_pam.close
  end
end
 
def activate_syslog_in_jail(jail_path)
  puts '* Activate syslogd in jail.'
  puts ' => add in rc.'
  system("echo 'syslogd_enable=\"YES\"' >> /etc/rc.conf") if `grep 'syslogd_enable' /etc/rc.conf`.empty?
  system("echo 'syslogd_program=\"/usr/sbin/syslogd\"' >> /etc/rc.conf") if `grep 'syslogd_program' /etc/rc.conf`.\
empty?
  system("echo ' syslogd_flags=\"-s -p #{jail_path}/var/run/log\"' >> /etc/rc.conf") if `grep 'syslogd_flags' /etc\
/rc.conf`.empty?
  puts ' => reload syslogd'
  system("killall syslogd;syslogd -s -p #{jail_path}/var/run/log")
end
 
def tune_sysctl
  puts '* Tuning host Sysctl'
  sysctl_list = ['security.bsd.see_other_uids=0','net.inet.tcp.blackhole=1','net.inet.udp.blackhole=0','security.b\
sd.see_other_gids=0','security.jail.set_hostname_allowed=0','security.jail.allow_raw_sockets=1']
  sysctl = open("/etc/sysctl.conf",'w')
  sysctl_list.each{|item|
    system("sysctl -b #{item}")
    sysctl.puts item
  }
  sysctl.close
end
 
def patch_sshd_config_host(host_ip)
  sshd_config = IO::readlines('/etc/ssh/sshd_config')
  sshd_config.map!{|line|
    if line =~ /^.*ListenAddress\s+(\d+\.){3}\d+.*$/ then
      line = "ListenAddress #{host_ip}"
    else
      line = line
    end
  }
  sshd_config.delete_if{|line| line =~ /^#/}
  sshd_config.delete_if{|line| line =~ /^\n/}
  dest = open('/etc/ssh/sshd_config','w')
  sshd_config.each{|line| dest.puts line }
  dest.close
end
 
def init_user_in_jail
  puts '* Initialising users on host and jail'
  system("/usr/local/bin/manage_jail cp")
end
 
def add_crontab_line
  puts '* adding task managing user in crontab'
  system("echo '0 0 * * * root /usr/local/bin/manage_jail convert >/dev/null 2>&1' >> /etc/crontab") if `grep '/us\
r/local/bin/manage_jail' /etc/crontab`.empty?
end
 
def put_freebsd_rc_loader
  puts '* Putting rc loader FreeBSD for the jail.'
  jail_rc = open("/usr/local/etc/rc.d/jail.sh",'w')
  jail_rc.puts('#!/bin/sh')
  jail_rc.puts('/usr/local/bin/manage_jail $1')
  jail_rc.close
end
 
 
# MAIN
 
create_jail_skeleton(data_path,jail_path,data_symlink)
install_executable(table_exe,jail_path)
create_ld_tools(jail_path)
put_config(list_config_file,jail_path)
make_temporary_path(jail_path)
put_script(jail_path)
install_mysql(jail_path)
install_sshd(jail_path)
start_pseudo_fs(jail_path)
install_pam(jail_path)
activate_syslog_in_jail(jail_path)
tune_sysctl
patch_sshd_config_host(host_ip)
init_user_in_jail
add_crontab_line
put_freebsd_rc_loader
 
puts ' =*= jail OK'
 
__END__

 
#!/usr/local/bin/ruby
 
require 'ipaddr'
 
param = ARGV[0]
ID_LIMIT=500
data_root = "/data"
data_jail = "#{data_root}/jail"
 
rc = IO::readlines("/etc/rc.conf").delete_if{|line| line !~ /ifconfig/}
rc.map!{|line| line  = /^ifconfig_.*\d+="inet\s+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\s+netmask\s+(\d{1,3}\.){3}\d{\
1,3}".*$/.match(line)[1]}
jail_ip = IPAddr.new "#{rc[0]}"
host_ip = IPAddr.new "#{rc[1]}"
jail_net = jail_ip.mask(24)
host_net = host_ip.mask(24)
 
case param
 
when  'cp' then
  puts '* Adding in the jail path'
  system("cp /etc/master.passwd /etc/passwd /etc/spwd.db /etc/pwd.db #{data_jail}/etc")
  puts ' => Correction of the UNIX mode in jail'
  system("chmod 600 #{data_jail}/etc/master.passwd #{data_jail}/etc/spwd.db")
  puts ' =*= jail users OK'
 
when 'start'
if not File::exist?("#{data_jail}/var/run/sshd.pid") then
    puts '* Starting Jail'
    system("jail /data/jail omicron #{jail_ip} /usr/sbin/sshd")
    puts ' =*= Jail running OK'
  else
    puts ' =/= Jail already running NOK'
  end
 
when 'stop'
  if File::exist?("#{data_jail}/var/run/sshd.pid") then
    puts '* Stopping Jail'
        pid = `cat #{data_jail}/var/run/sshd.pid`.chomp
    system("kill #{pid}")
    puts ' =*= Jail stopping OK'
  else
    puts ' =/= Jail not running NOK'
end
 
end

Options

Certaines caractéristiques des jails sont configurables par des à partir de l'hôte principal.Voir man jail(8), chapitre Sysctl MIB Entries.

  • security.jail.chflags_allowed
Permission de modifier les ACL par l'utilisateur root des jails. La permission dépend aussi du "niveau de sécurité" courant kern.securelevel
  • security.jail.allow_raw_sockets
Permission d'utiliser les raw sockets.
  • security.jail.enforce_statfs
Visibilité des points de montages.

Dans l'exemple ci dessous, roxette est l'hôte qui héberge roxette-21 dans une jail.

# roxette:~# sysctl security.jail.enforce_statfs=0
security.jail.enforce_statfs: 1 -> 0
roxette-21:/# mount
/dev/ad0s3a on / (ufs, local, noatime)
devfs on /dev (devfs, local)
/dev/ad0s3e on /tmp (ufs, local, noatime, soft-updates)
/dev/ad0s3f on /usr (ufs, local, noatime, soft-updates)
/dev/ad0s3d on /var (ufs, local, soft-updates)
/dev/ad0s2 on /mnt/win (ntfs, local, noexec, nosuid, read-only)
linprocfs on /usr/compat/linux/proc (linprocfs, local)
devfs on /usr/jails/192.168.2.1/dev (devfs, local)

# sysctl security.jail.enforce_statfs=1
security.jail.enforce_statfs: 0 -> 1
roxette-21:/# mount
/dev/ad0s3f on / (ufs, local, noatime, soft-updates)
devfs on /dev (devfs, local)

# sysctl security.jail.enforce_statfs=2
security.jail.enforce_statfs: 1 -> 2
roxette-21:/# mount
/dev/ad0s3f on / (ufs, local, noatime, soft-updates)
  • security.jail.sysvipc_allowed
Permission d'utiliser les IPC system V (inter processus communications).
  • security.jail.socket_unixiproute_only
Utilisation seule des domaines PF_LOCAL, PF_INET, PF_ROUTE à l'intérieur d'une jail.
  • security.jail.set_hostname_allowed
Permission de changer le nom d'hôte.

Deux autres variables ont un usage par jail, chaque jail peut posséder sa propre valeur pour:

  • kern.securelevel
« Niveau de sécurité », le niveau de sécurité ne peut être qu'augmenté mais jamais diminué. Le niveau de sécurité limite certaines opérations. La page de man d'init fourni une description de ces niveaux, voir aussi security(7) et chflags(1).
  • kern.hostname


L'aspect pratique concernant les jails est qu'il est possible d'utiliser un niveau plus élevé dans une jail que dans l'hôte : par exemple avec une jail tournant avec un securelevel >= 1 et un hôte tournant en <= 0, il sera possible de supprimer les ACL à partir de l'hôte tout en l'interdisant à partir de la jail. Notez que cela n'empêche pas d'ajouter des ACL.

Dans l'exemple ci dessous, roxette est l'hôte et roxette-21 une jail.

roxette:~# sysctl security.jail.chflags_allowed=1
security.jail.chflags_allowed: 0 -> 1

roxette-21:/bin# sysctl kern.securelevel
kern.securelevel: -1

/bin/rcp porte le flag schg, on peut le changer :
roxette-21:/bin# ls -lo rcp
-r-sr-xr-x  1 root  wheel  schg 18332 29 mar 21:57 rcp*

roxette-21:/bin# chflags noschg rcp
roxette-21:/bin# ls -lo rcp
-r-sr-xr-x  1 root  wheel  - 18332 29 mar 21:57 rcp*

roxette-21:/bin# sysctl kern.securelevel=2
kern.securelevel: -1 -> 2
roxette-21:/bin# chflags schg rcp
roxette-21:/bin# ls -lo rcp
-r-sr-xr-x  1 root  wheel  schg 18332 29 mar 21:57 rcp*

roxette-21:/bin# chflags noschg rcp
chflags: rcp: Operation not permitted

À partir du passage en securelevel=2 dans la jail, on a pu ajouter le flag schg à /bin/rcp mais on ne peut plus l'enlever.

Note : Avec security.jail.chflags_allowed=0, chflags est tout simplement interdit, il n'est pas possible de modifier les ACL quelque soit le securelevel en cours des jails.

Limites

Rien n'est parfait et les jails ont quelques limitations.
- Seul IPv4 est supporté, IPv6 est à l'étude ou à divers stades de développement (je crois).
- Il serait souhaitable de pouvoir limiter les IPC à une jail (avoir un security.jail.sysvipc_allowed par jail).
- Il n'est pas possible d'allouer des ressources UC par jail. Les processus sont schédulés normalement. Une jail qui exécute un programme de type "bombe fork" impactera l'ensemble. Il est toutefois possible de jouer sur les limites (voir limits(1)).

Astuces

Points de montages

Plutôt que de recopier n fois les binaires ou l'arbre des ports, on peut utiliser des montages multiples en lecture seule. L'utilisation des montages unionfs(8) est aussi possible;mais unionfs est actuellement instable sous FreeBSD 6.
Le système de fichier nullfs(8) permet de monter une arborescence existante sous une autre arborescence.

# mount_nullfs -o ro /usr/ports /jails/192.168.2.1/usr/ports
# mount
/usr/ports on /jails/192.168.2.1/usr/ports (nullfs, local, read-only)

/etc/fstab peut-être utilisé pour monter le système au démarrage :

/usr/ports /jails/192.168.2.1/usr/ports nullfs ro 0 0

JAILER

sysutils/jailer est un utilitaire qui permet de lancer et d'arrêter une jail depuis celle-ci ou de l'extérieur. Il permet aussi d'éviter le problème de refcounting(9) puisque la jail n'est pas terminée proprement dit.
Jailer arrête les processus de la jail mais reste actif.

Scripts

Il y a quelques utilitaires concernant les jails dans /usr/ports/sysutils.
J'utilise deux scripts pour l'installation et la mise à jour d'une jail que j'ai pompé chez http://www.the-labs.com et adapté à mon usage : http://www.lamaiziere.net/private/jail_install http://www.lamaiziere.net/private/jail_update


Pour interdire l'exécution de certaines instructions interdites à l’intérieur d'une jail, vérifiez par exemple, en utilisant la clef security.jail.jailed:

 
within_a_jail()
{
    return $(sysctl -n security.jail.jailed)
}
 
if  ! within_a_jail;  then
	echo You r jailed.
else
	echo ok. Not jailed.
fi

Conclusion

Les jails FreeBSD sont simples à mettre en œuvre, sans remise en question du schéma Unix traditionnel. Elles sont en outre proprement intégrées au système et disponibles out of the box.

Elles apportent :
- Du buziness : certains hébergeurs proposent des serveurs semi-dédiés basés sur jail().
- De la sécurité : si un « méchant » parvient à compromettre un service confiné il aura du mal à aller plus loin. Mais ce n'est pas impossible, jail(8) a déjà eu des problèmes de sécurité (assez peu). Comme chaque jail a son IP, il est facile de filtrer au niveau du pare-feu. On peut aussi faire des statistiques jails par jails, par exemple le trafic réseau par IP.

- De la facilité : qui a déjà eu à mettre à jour Perl (ou pire libtool ou gettext) sera bien content de pouvoir faire la mise à jour jail par jail, en n'interrompant que le strict minimum à chaque fois. Mieux il est facile de copier une jail, de la lancer et de faire des tests pour vérifier si cela fonctionne sans risquer de casser quelque chose.

- Du fun : Des tas de machines virtuelles à disposition pour des expérimentations de toutes sortes.

Les jails c'est bon, mangez-en.

Références

Évolutions

Ça bouge du coté des jails, des évolutions sont disponibles en 8-CURRENT et depuis peu en 7.1-STABLE : http://svn.freebsd.org/viewvc/base?view=revision&revision=185435

Y'a plein de bonnes choses !
Plusieurs adresses IP pour une jail (voir même aucune adresse IP)
Support de IPv6
Support de SCTP
Possibilité d'affecter les processus d'une jail à des processeurs.
Possibilité de faire tourner une jail 32 bits dans un hôte 64 bits.
Pour les suivre, suivez le guide.

Exemples

Projets :