Jail FreeBSD

Un article de Diablotins.org.


Image:brush.png

Sommaire

LES JAILS FreeBSD

PRINCIPE

OBJECTIFS D'UNE JAIL

Le présent document se propose d'expliquer les principes et la mise en œuvre des jails 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.

Ici le système utilisé est FreeBSD 6.2.

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 partitionner l'environnement tout en gardant le modèle simple des droits Unix. Dans une jail, les utilisateurs sont confinés dans la prison, le super-utilisateur (root) conserve son rôle mais ses actions sont limitées depuis une jail.

La solution des jails

Les jails apportent une solution en partitionnant l'environnement au niveau des processus, des systèmes de fichiers et des ressources réseaux.

L'environnement est partitionnĂ© en :

  • 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 fortements 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 impactants 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), les processus emprisonnés ne peuvent que s'attacher à cette adresse IP. 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 local), les autres domaines sont interdits.

[source: <http://docs.freebsd.org/44doc/papers/jail/jail.html>]

INTERLUDE PRATIQUE

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 jail, nous utilisons un alias sur une interface ici lo0 :

# ifconfig lo0 alias 192.168.2.1 netmask 0xffffffff

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 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 on s'appelle ?
# ./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

Ici nous somme root mais sur l'hĂ´te (ie pas dans la jail).

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 jailĂ©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
(On notera que les processus root tournant en jail 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 retournont dans notre sh emprisonné et quittont le shell.

# exit
(lĂ  on retourne Ă  l'hĂ´te)
# jls

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

CAS D'UTILISATION DE JAIL

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, ou bien de confiner un service particulier à la manière de chroot, mais en mieux.

Sur la mise en œuvre de 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 jailé, 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 surcout en terme de puissance est donc extrèmement faible.

Par exemple pour un processus jailĂ©, 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);
[...]
Sur la mise en œuvre de services isolés

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 DES JAILS

OBJECTIFS

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.

CONVENTION DE TOPOLOGIE RESEAU ET D'ADRESSAGE

On considĂ©rera pour notre document une machine physique ayant deux interfaces rĂ©seaux : xl0 et xl1 (ces noms de devices sont ceux des cartes reseaux 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 prendrons 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.

CONFIGURATION DE L'HÔTE

Modification du fichier /etc/rc.conf de 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"

Aspect réseaux et super démon 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="NO"
Le cas de SENDMAIL

Pour Sendmail, 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 flag -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 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"
Mise en place des l'alias IP des jails et de l'adresse de l'hĂ´te

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.25
  [...]

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. )

Configuration des 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.

Configuration de SSH

Pour sshd, 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 conf de sshd, /etc/ssh/sshd_config, on ajoute :

  # vi /etc/ssh/sshd_config
  --
  [...]
  ListenAddress 192.168.1.1
  [...]
Vérification des routes existantes

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 -a |grep net.inet.ip.forwarding

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

   # sysctl -w 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

Suppression de 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 -w net.link.ether.inet.log_arp_wrong_iface=1
   # echo "net.link.ether.inet.log_arp_wrong_iface=1" >> /etc/sysctl.conf

MISE EN Ĺ’UVRE DE MACHINES VIRTUELLES

OBJECTIF

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, il exécute le script /etc/rc. Le script rc poursuit l'initialisation de la machine et le lancement des 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 jail, 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 DE LA JAIL

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":

   # 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 a 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).

LANCEMENT DE LA JAIL À PARTIR DES SCRIPTS RC

Les scripts de dĂ©marrage "rc" 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 jail "test1", d'adresse IP 192.168.2.1, sur la racine qui nous avons prĂ©cĂ©dement construite. Le paramètre devfs_ruleset="devfsrules_jail" est important : le devfs montĂ© sur la jail ne contiendra que les fichiers spĂ©ciaux strictements 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

on la relance

# /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

Et on rentre dans la jail (id = 4)

# jexec 4 csh
[ ici on est dans la prison ]
Jetons 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 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 DE LA JAIL

Le monde utilisé par une jail doit être maintenu synchro 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.

INSTALLATION DE 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 : cvsup / portsnap de l'arbre des ports, make install, pkg_add, portupgrade, etc...

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.

MISE EN PLACE D'UNE JAIL DE SERVICE

OBJECTIFS

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.

PRECAUTIONS ET MODE OPERATOIRE GENERAL

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.

METHODE GENERALE

Description

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 : 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.
Compilation des outils et des binaires du service ou nécessaire au service

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Ă©fix, (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.

Remarque : pour les rĂ©pertoires, il faut bien penser 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.

Politique sur la structure de l'arborescence d'une jail de service

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.

ETUDES DE CAS ET IMPLEMENTATION

Cas simple le 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.

Cas complexe l'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 SUR LES JAILS

Certaines caractéristiques des jails sont configurables par des sysctl à partir de l'hôte principal pour toutes les jails. Voir man jail.

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 et roxette-21 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 sysctl ont une valeur par jail, chaque jail peut posseder sa propre valeur : kern.securelevel et kern.hostname.

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).

L'aspect pratique concernant les jails est qu'il est possible d'utiliser un niveau plus Ă©lĂ©vĂ© 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.

LIMITATIONS DES JAILS

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

MONTAGES MULTIPLES

PlutĂ´t que de recopier n fois les binaires ou l'arbre des ports, on peut utiliser des montages multiples en read-only. L'utilisation des montages unionfs est aussi possible mais unionfs est actuellement instable sous FreeBSD 6.

Le système de fichier nullfs 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 fs 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 puisque la jail n'est pas terminée proprement dit, jailer arrête les processus de la jail mais reste actif.

UTILITAIRES

Il y a quelques utilitaires concernant les jails dans 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

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 jailĂ© il aura du mal Ă  aller plus loin. Mais ce n'est pas impossible, jail() 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 parefeu. On peut aussi faire des stats 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 Ă  dispo pour des expĂ©rimentations de toutes sortes.

Les jails c'est bon, mangez-en.

REFERENCES

Livre "The Design and Implementation of the FreeBSD Operating System." http://docs.freebsd.org/44doc/papers/jail/jail.html (par les auteurs) http://www.the-labs.com/FreeBSD/JailTools/ (plutĂ´t pour FreeBSD 4, mais infos utiles) http://www.freebsddiary.org/jail-6.php

Récupérée de « http://diablotins.org/index.php/Jail_FreeBSD »
Projets :
Boîte à outils