Lorsque l'on a besoin de créer un environnement de test il est nécessaire que sa composition soit strictement contrôlé (paramétrages, applications et services disponibles et lancées, etc). Il n'est du coup généralement pas conseillé d'utiliser sa machine de travail, sauf si elle ne sert qu'à cela, sous peine d'en détériorer le fonctionnement en cas de test malheureux ou de modifier sans le vouloir le comportement de ce que l'on cherche à tester. Nous sommes alors contraints d'avoir soit une machine virtuelle dédiée aux tests, soit une machine physique supplémentaire.
Ce serait sans compter sur la commande chroot qui permet dans certaines conditions d'obtenir un environnement de test ou d'intégration identique à celui en production, sans machine physique supplémentaire et sans que la machine principale soit ralentie ou compromise.
Comme vous le savez sûrement déjà, le système fichier d'un *NIX est construit autour d'une racine (le /) sur laquelle les partitions sont ensuite "montées" formant ainsi l'espace de fichier accessible. Ce que l'on sait moins c'est que cette racine est un paramètre du processus. Tout processus lancé peut avoir pour racine un chemin arbitraire issu de celle de son processus parent. Si ce paramètre n'est pas définit, ce qui est généralement le cas, c'est '/' qui est utilisé par défaut, en quelque sorte la racine de la racine du parent. La commande chroot permet de lancer une commande et dans la foulée de définir ce paramètre pour le processus engendré.
La commande chroot peut donc avoir deux paramètres principaux : un chemin dans l'arborescence et une commande. Ainsi, si en tant qu'utilisateur root nous lançons chroot /lapp /bin/bash, voilà ce qui se passe :
Le premier constat que nous pouvons tire de cela est qu'il faut que notre dossier /lapp contienne un bin/bash, mais aussi toutes les librairies dont a besoin bash pour fonctionner, ainsi que tous les dossiers systèmes qui lui sont nécessaire et qu'il s'attend à trouver comme sous une "vraie" racine. Ainsi la seule problématique posée sera le peuplement de notre nouvelle racine.
Le second constat est qu'un chroot n'est pas une virtualisation. Une virtualisation définit des conteneurs bien hermétiques avec leur mémoire propre, leur espace disque propre, leurs périphériques propres, etc. Le chroot ne fait rien de tout cela. La mémoire, le CPU, les périphériques (réseau, disque, écran, etc) sont partagées par l'environnement racine et chrooté. C'est d'ailleurs pour cela qu'un chroot est aussi rapide que si tout était lancé sur la racine principale.
Chroot ne fait donc que "faire croire" à un processus qu'il travaille sous la "vraie racine" alors qu'il n'est que dans un sous-dossier. Ainsi la seule garantie que fournit le chroot est l'étanchéité des fichiers dans le sens enfant-parent. C'est pour cet aspect que le chroot est d'ailleurs généralement utilisé, pour "mettre en prison" des applications critiques (serveur web, serveur mail, etc.) de sorte à ce que si une vulnérabilité permet à un cracker de casser le serveur, il ne puisse atteindre les fichiers se trouvant hors du chroot et ainsi limiter les dégâts. A noter que ce billet ne traite pas de cette utilisation critique du chroot et que d'un point de vue général il existe nombre d'exploits permettant de s'échapper de cette prison.
Malgré ses limitations, le chroot est juste parfait pour une utilisation : la création d'un environnement de test et/ou d'intégration pour des applications WEB. Il permet en effet très facilement de créer une configuration LA(P|M)P (Linux-Apache-Postgres/MySQL-PHP), facile à maintenir, facile à recréer, testable de la machine principale sans modification particulière comme si tout était installé "localement".
Notre but est donc ici de créer un environnement chrooté comprenant un Linux de base, Apache, Postgresql et PHP. La première étape est donc de créer notre future racine. Je le fait en /lapp mais cela pourrait être virtuellement n'importe où, y compris sur le dossier d'une clef mémoire ou sur un espace volatile type tmpfs.
Maintenant vient le moment de peupler notre racine. Là, il y a trois approches possibles.
Nous avons l'approche dite "bourrine" consistant à dupliquer sauvagement son environnement principal :
L'inconvénient est que c'est souvent très volumineux et peu adapté à un environnement de test contrôlé dans la mesure où des tas de choses inutiles sont injectées dans la nouvelle racine.
Ensuite il y a l'approche "minimaliste" qui cherche à ne copier que ce qui est nécessaire. Cela peut se faire à la main avec la commande ldd ou beaucoup plus simplement, comme me l'a indiqué Guyou, avec les outils makejail ou chrootbin. Mais cette technique est plus adaptée au lancement d'un serveur dans une prison chroot qu'à notre besoin.
Enfin nous avons l'approche "raisonnable" consistant à utiliser les outils fournis par les debian's et autres mandriva's qui permettant d'installer tout simplement sur une racine arbitraire une distribution complète. C'est clairement dans notre cas cette approche qui s'impose.
Sous mandriva (et distributions dérivées), l'installation d'un linux de base sur une racine se fait simplement par la commande suivante :
Simple et diablement efficace. Comme vous l'aurez noté, ce sont trois paquets qui sont ici installés: le linux de base, la langue française et le système de gestion de paquets. Ce dernier point nous offrira beaucoup de souplesse car au sein même du chroot il sera du coup possible d'installer, désinstaller ou même mettre à jour la distribution.
Les distributions basées sur debian disposent elle aussi d'une méthode très efficace pour créer une racine minimale en utilisant cette fois la commande debootsrap.
Vous pouvez remplacer sid par toute distribution debian disponible (etch, sarge, etc..). Pour le reste cela fonctionne comme la méthode précédente a la différence que la gestion de paquet n'est pas optionnelle et vous aurez donc automatiquement accès à apt-get dans le chroot.
Notre racine étant maintenant peuplée et les utilitaires que nous avons utilisé ont normalement pris soin de créer à la "racine" les dossier vitaux tmp, var, dev, sys et proc. Si ce n'est pas le cas, faites le vous-même.
Maintenant comme vous le savez, certains dossiers de Linux ne sont pas de "vrais" dossier mais une vue sur des variables du système. C'est le cas de /proc, /sys et /dev. Si nous lancions maintenant notre nouvelle racine, ces dossiers seraient donc vides provoquant des erreurs sur certaines fonctions. Il nous faut donc avant monter ces dossiers. Pour ce faire, nous allons simplement les "relier" (mount --bind) aux mêmes dossiers de la vraie racine.
Concernant le réseau dans un chroot, la configuration sera la même que celle de l'environnement parent. Tout fonctionnera donc sans avoir à faire quoi que ce soit. En revanche il est important de vérifier que la résolution de noms se fait correctement. Pour cela nous allons simplement recopier le fichier /etc/resolv :
Maintenant tout est en place, c'est le moment du lancement.
Voilà, une fois ceci exécuté, nous sommes "prisonnier" de la nouvelle racine et toutes les commandes qui suivent sont contrainte dans cet espace. Pour en sortir il suffit de faire un "exit". Comme vous le voyez tout fonctionne, vous pouvez utiliser le réseau, installer des paquets, etc. C'est ce que nous allons faire pour mettre en place notre environnement de test :
Ceci fait, vous pouvez constater que votre système fonctionne en lançant les services :
Maintenant, en allant, sur votre environnement principal aidé d'un navigateur web sur l'adresse http://localhost, vous devriez avoir accès à la page de test du serveur Apache. Pour le reste, à vous de jouer...
Comme vous l'aurez constaté, le chroot n'effectue pas, et heureusement, un démarrage réel du linux installé à la nouvelle racine. Il se contente d'exécuter /bin/bash ce qui vous oblige de votre côté à lancer les services à la main.
Pour rendre cela beaucoup plus simple à utiliser sur une base quotidienne, le plus propre reste de créer un service dédié au lancement de notre racine qui va créer le montages et lancer apache, postgres, etc... Son arrêt ferra l'inverse :
Ensuite dans notre racine il faut rajouter, et rendre exécutable, un fichier /init.sh contenant le démarrage/arrêt des services httpd et postgresql, et qui prendra en paramètre start ou stop :
Après, vous pouvez rendre le lancement de ce service automatique (avec chkconfig ou l'outil dédié à cela pour votre distribution), ou faire cela à la main :
Personnellement j'ai mis ce service en démarrage manuel et ajouté un profile dans gnome-terminal qui me lance directement la commande chroot /lapp /bin/sh.
La commande chroot ne manque donc pas d'intérêt en restant très simple à mettre en oeuvre si on en saisi bien les fondamentaux. C'est une bonne alternative à la "lourdeur" de la virtualisation et permet de créer à une vitesse record des environnements de développement rapides, efficaces et non-polluant pour le reste du système.
- répondre
Dab, le 22 October, 2006 - 13:08Ne vaut il pas mieux utiliser sudo plutot que de modifier le bit suid de chroot ?

D'autre part un chroot ne peut être considérer comme un environnement sécurisé. Il est apparement possible d'en sortir
La revue Misc ( Sept/Oct 2003 ) titrait "CHROOT Sécurité illusoire ou illusion de la sécurité."
Mais cela dit chroot reste très pratique pour monter un linux dans un autre
- répondre
Ulhume, le 22 October, 2006 - 16:47C'est un peu pour cela que ne m'engage pas sur ce sujet
))
Pour le coup de suid, j'ai tenté effectivement un sudo passerait mais cela revient un peu au même, mis à part qu'il faille configurer le dit sudo. Aprés c'est une question de goûts. Je ne changerais pas le suid sur n'importe quel binaire mais celui là ne me parrait (à tord) pas risque...
- répondre
Dab, le 7 November, 2007 - 14:33Mes remarques à 2 balles:
cerveur -> serveur
Tu peux aussi monter devpts et tmpfs (voir http://lfs.traduc.org/view/lfs-5.1.1-fr/chapter06/proc.html )
- répondre
tuxce , le 7 November, 2007 - 15:17Très bon article, ca m'a aussi permit de découvrir ton script mk-chroot (peut s'avérer utile).
pour ce qui est des répertoires copiés, ne serait-il pas plus simple de le monter avec un "-o bind":
si le but est juste de limiter les outils, par contre, peut etre que coté sécurité, c'est surement pas pareil.
- répondre
Ulhume, le 7 November, 2007 - 17:48@tuxce merci
Oui, ce script est assez pratique même si j'avoue que la version urpmi/debootsrap a aujourd'hui ma préférence car universelle. Dans 99% des cas (pour moi), j'utilise chroot pour créer un linux de test, qui doit donc être complet.
Sinon, tu as parfaitement raison, c'est tout aussi efficace pour un environnement complet et moins conseillé si l'on cherche à sécuriser un serveur (genre bind ou apache).
- répondre
Ulhume, le 7 November, 2007 - 17:51@Dab
A quoi ça sert ces deux montages ? En fait tmpfs je vois bien mais ça dépend de ce que tu cherches à faire, c'est un peu spécifique. Mais pour devpts je vois pas bien. En revanche j'ai oublié sysfs (je m'en suis rendu compte avec la fausse debian qui le pose dans /etc/fstab par défaut).
PS: merci pour l'ortho
- répondre
Dab, le 7 November, 2007 - 18:52franchement le devpts je ne sais pas très bien (c'est pas pour les terminaux virtuels ? ) et c'est sur que le tmpfs n'est utile que pour certaines applis (dixit le lien que je t'ai donné)
- répondre
Guyou , le 8 November, 2007 - 12:53J'ai eu besoin de faire une cage chroot il n'y a pas si longtemps.
Pour m'aider, j'ai utilisé deux softs bien pratiques :
- makejail
- chrootbin
Tout deux aident bien à peupler la cage en faisant peu d'effort.
- répondre
slc66, le 26 October, 2008 - 15:57Bonjour,
Voici encore un article super agréable à lire.
Merci
Pour ceux que ça intéresse, j'ai adapté ton script de démarrage pour une plate-forme Debian.
#
# Start or stop Nagios chroot environment
#
NAME=nagios
ROOT=/home/nagios
# Source function library.
. /lib/lsb/init-functions
case "${1}" in grep $ROOT)" ] then
("start")
if [ ! -z "$(mount
log_failure_msg "$NAME is already started"
exit 3
fi
log_begin_msg "Starting chroot for $NAME"
mount --bind /proc $ROOT/proc
mount --bind /dev $ROOT/dev
mount --bind /sys $ROOT/sys
#chroot $ROOT /init.sh start
exit $?
("stop") grep $ROOT)" ] then
if [ -z "$(mount
log_failure_msg "$NAME is already stopped"
exit 3
fi
log_begin_msg "Stopping chroot for $NAME" then then then
#chroot $ROOT /init.sh stop
umount $ROOT/proc
if [ $? -ne 0 ]
log_failure_msg "Unable to umount $ROOT/proc\n"
exit 3
fi
umount $ROOT/dev
if [ $? -ne 0 ]
log_failure_msg "Unable to umount $ROOT/dev\n"
exit 3
fi
umount $ROOT/sys
if [ $? -ne 0 ]
log_failure_msg "Unable to umount $ROOT/sys\n"
exit 3
fi
exit $?
("restart")
"${0}" stop
"${0}" start
(*)stoprestart}" >&2
log_success_msg "Usage: /etc/init.d/chroot-nagios {start
exit 3
esac
- répondre
Ulhume, le 28 October, 2008 - 22:11@slc66 et tu as même corrigé ma boulette du chemin en dur au lieu de passer par $ROOT (j'ai fait de même du coup)
Poster un nouveau commentaire