Intéressé par des cours d'informatique en ligne ?
Visitez mon nouveau site https://www.yesik.it !

Au sens strict, User Mode Linux (UML) est un portage du noyau Linux vers Linux. L'idée peut sembler saugrenue au premier abord, mais ouvre des perspectives intéressantes en offrant la possibilité de démarrer un nouveau noyau comme un processus utilisateur ordinaire.

Vous l'avez compris, ce principe s'apparente à la notion de virtualisation tellement à la mode ces temps-ci. Comparativement à d'autres technologies comme Xen ou VMWare, UML a l'inconvénient de ne pouvoir lancer que des noyaux Linux. Pas d'autres OS. Par contre, une instance du noyau Linux lancée par UML tourne entièrement en mode utilisateur. Et n'exige pas d'avoir un noyau spécial sur la machine physique qui sert d'hôte. C'est pratique quand vous ne pouvez pas – ou ne voulez pas – installer un noyau spécifiquement patché pour faire tourner un système de virtualisation. Ou encore, comme c'est le cas avec mon portable, quand le hardware ne supporte pas ces solutions ou que vous ne voulez pas vous priver de fonctionnalités incompatibles comme la gestion d'énergie ACPI.

Dans cet article, nous allons faire nos premiers pas avec User Mode Linux en créant et démarrant notre toute première machine virtuelle UML. Il s'agira ici d'une machine virtuelle sous Debian/Lenny – sans accès au réseau.

Installation

Mon hôte (ma machine physique) est aussi une machine sous Debian. Et comme User Mode Linux est disponible dans le gestionnaire de paquets, l'installation à proprement parler se résume en une ligne:

sh# apt-get install user-mode-linux user-mode-linux-doc

Voilà, c'est tout! Fin? Non, pas tout à fait...

Démarrer le noyau

A strictement parler, l'installation d'UML est terminée. Désormais, vous pouvez utiliser la commande linux pour créer et démarrer une instance de machine virtuelle UML. Mais si vous essayez maintenant, vous aurez l'impression que quelque-chose manque. A juste titre:

sh$ linux
Locating the bottom of the address space ... 0x0
Locating the top of the address space ... 0xc0000000
Core dump limits :
	soft - 0
	hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking for tmpfs mount on /dev/shm...OK
Checking PROT_EXEC mmap in /dev/shm/...OK
Checking for the skas3 patch in the host:
  - /proc/mm...not found: No such file or directory
  - PTRACE_FAULTINFO...not found
  - PTRACE_LDT...not found
UML running in SKAS0 mode
Linux version 2.6.26 (2.6.26) (root@lart) (gcc version 4.3.2 (Debian 4.3.2-1.1) ) #2 Thu Mar 11 18:42:19 UTC 2010
Built 1 zonelists in Zone order, mobility grouping on.  Total pages: 8128
Kernel command line: root=98:0
PID hash table entries: 128 (order: 7, 512 bytes)
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 28492k available
SLUB: Genslabs=12, HWalign=128, Order=0-3, MinObjects=0, CPUs=1, Nodes=1
Mount-cache hash table entries: 512
[...]
Couldn't stat "root_fs" : err = 2
Failed to initialize ubd device 0 :Couldn't determine size of device's file
registered taskstats version 1
VFS: Cannot open root device "98:0" or unknown-block(98,0)
Please append a correct "root=" boot option; here are the available partitions:
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(98,0)

EIP: 0073:[<b7f18410>] CPU: 0 Not tainted ESP: 007b:bff25894 EFLAGS: 00200246
    Not tainted
EAX: 00000000 EBX: 00003000 ECX: 00000013 EDX: 00003000
ESI: 00002ffc EDI: 0000003b EBP: bff25920 DS: 007b ES: 007b
09c67e7c:  [<0809ef94>] notifier_call_chain+0x34/0x70
09c67ea0:  [<08312f2a>] panic+0x71/0x104
09c67ebc:  [<08049c03>] mount_block_root+0x15f/0x27f
09c67ef4:  [<080e92e7>] sys_mknod+0x27/0x30
09c67f08:  [<08049d7e>] mount_root+0x5b/0x60
09c67f1c:  [<08049eb6>] prepare_namespace+0x133/0x17d
09c67f34:  [<08049147>] kernel_init+0x92/0x290
09c67f50:  [<0805878f>] tcp_congestion_default+0x0/0x13
09c67fa4:  [<08080db8>] finish_task_switch+0x28/0x90
09c67fbc:  [<08072e40>] run_kernel_thread+0x30/0x50
09c67fd8:  [<08072e2f>] run_kernel_thread+0x1f/0x50
09c67fe4:  [<0805f7bb>] new_thread_handler+0x6b/0xa0
09c67fe8:  [<080490b5>] kernel_init+0x0/0x290

Segmentation fault

Ce premier test – bien que terminé par un segmentation fault – nous apprend que UML est correctement installé et fonctionne! En effet, si vous observez attentivement l'affichage vous noterez qu'une instance du noyau Linux 2.6 est bien démarrée. Remarquez aussi que j'ai utilisé cette commande sous un compte utilisateur (prompt se terminant par $).

Alors pourquoi ce plantage de la machine virtuelle? Et bien parce que pour démarrer une machine, vous avez besoin qu'elle soit équipée d'un système d'exploitation. Or, dans un système Unix-like, le noyau n'est qu'une partie de ce système. Il nous manque encore l'ensemble des utilitaires qui constituent le sytème de base. Pour parler plus trivialement, nous n'avons que la partie linux de la machine virtuelle GNU/Linux que nous tentons de lancer.

Installer un système de base

Création de la partition

Qui dit installer un système, dit créer une partition. Par contre, pour vous éviter de devoir monopoliser une partition réelle pour chaque machine virtuelle, UML peut utiliser divers supports pour servir de root filesystem. Le plus simple pour cette introduction sera de travailler sur un fichier image. Autrement dit, un fichier contenant l'image d'une partition linux. Une partition – pardon – un fichier de 300 méga-octets est très largement suffisant:

sh# mkdir uml-images
sh# cd uml-images
sh# dd if=/dev/zero of=lenny-image count=0 obs=1MB seek=300
0+0 records in
0+0 records out
0 bytes (0 B) copied, 1.8928e-05 s, 0.0 kB/s

300MB ou 0B?

J'attire votre attention sur la commande dd ci-dessus. Celle-ci indique 0 octet copié. Or j'ai demandé la création d'un fichier de 300 méga-octets. Et j'ai pourtant l'air content de moi...

L'explication s'appelle sparse file. En effet, certains systèmes de fichiers (dont ext3) permettent de créer des fichiers sans que ceux-ci n'utilisent réellement de place sur le disque tant que rien n'y est écrit. Vous pouvez le vérifier avec la commande ls -ls:

sh# ls -ls
total 0
0 -rw-r--r-- 1 root root 300000000 2010-03-19 21:41 lenny-image

Remarquez comme mon fichier a pour taille logique 300000000 octets, alors que physiquement il n'occupe que 0 bloc d'espace disque. L'espace disque ne sera réellement occupé qu'au fur et à mesure des écritures sur le disque.

Maintenant que le fichier image est créé, il faut le formater. Comme n'importe quelle partition. Ici, je choisis de formater avec le système de fichier ext3:

sh# mkfs -t ext3 lenny-image
2fs 1.41.3 (12-Oct-2008)
lenny-image is not a block special device.
Proceed anyway? (y,n) y
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
73440 inodes, 292968 blocks
14648 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=67633152
36 block groups
8192 blocks per group, 8192 fragments per group
2040 inodes per group
Superblock backups stored on blocks: 
        8193, 24577, 40961, 57345, 73729, 204801, 221185 

Writing inode tables: done                            
Creating journal (8192 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 20 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

Installer le système de base

Installation

Sous Debian vous aurez besoin du paquet debootstrap pour ce qui suit. Si ce logiciel n'est pas déjà sur votre machine utilisez le gestionnaire de paquets de votre distribution pour procéder à l'installation.

sh# apt-get install debootstrap

Maintenant que nous disposons d'une partition prète à l'accueillir, reste à installer le système de base. Ici, ce sera un système GNU/Debian installé avec l'utilitaire Debian ad-hoc: debootstrap. debootstrap ne peut pas directement installer sur une image, c'est pourquoi il est nécessaire de d'abord monter l'image via un loop device:

sh# mount lenny-image /mnt -t ext3 -o loop
sh# debootstrap lenny /mnt http://10.129.36.102:9999/debian
[...]
I: Base system installed successfully.

Miroir, miroir...

L'installation par debootstrap nécessite de télécharger beaucoup de paquets. Je vous conseille donc d'utiliser un miroir local ou un cache apt. C'est cette dernière option que j'ai choisie dans l'exemple ci-dessus. En effet, sur ma machine http://10.129.36.102 tourne apt-cacher-ng.

Si vous préférez télécharger à partir d'un miroir officiel, vous modifierez la commande en conséquence:

sh# debootstrap lenny /mnt http://ftp.fr.debian.org/debian

Ceci dit, je ne peux que vous encourager à installer un cache apt pour réduire votre consommation en bande passante – et accessoirement la charge sur les miroirs officiels de Debian.

Post-installation

debootstrap étant entièrement automatique, certaines étapes liées à la configuration du nouveau système sont laissées à la charge de l'utilisateur en post-installation. Ici, nous nous contenterons:

Pour faire ces (petites) configurations, nous allons nous chrooter dans la partition. Ceci nous permettra d'agir comme si nous étions logué sur la machine:

sh# chroot /mnt
sh# echo uml > /etc/hostname
sh# passwd root
Enter new UNIX password: 
Retype new UNIX password: 
passwd: password updated successfully
sh# perl -i -n -e '
       print "0:2345:respawn:/sbin/getty 38400 tty0\n" if /1:.*tty1$/;
       print if not /^0:.*tty0/' /etc/inittab
sh# perl  -i -n -e '
       print "tty0\n" if /^tty1$/;
       print if not /^tty0$/' /etc/securetty 
sh# exit # quitte chroot

perl -i

J'utilise ci-dessus perl pour modifier deux fichiers de configuration. Mon idée étant de pouvoir incorporer l'ensemble des étapes dans un script afin d'automatiser la configuration d'une machine virtuelle.

Mais si vous êtes allergiques à perl – et que vous préférez modifier les fichiers à la main voici le détail. Donc, tout d'abord, j'ajoute au fichier /etc/inittab la ligne suivante pour que le login soit possible sur la console 0 (la console 0 étant la console dans laquelle UML démarre – celle où s'affichent les messages du noyau):

0:2345:respawn:/sbin/getty 38400 tty0

De plus, sur un système Linux moderne, il est aussi nécessaire d'indiquer à partir de quels terminaux root peut se connecter. Or, par défaut, sous Debian, tty0 ne fait pas parti des terminaux autorisés. C'est pour y remédier que j'ajoute également une ligne contenant tty0 au fichier /etc/securetty.

La post-installation est maintenant terminée. Nous pouvons démonter la partition:

sh# umount /mnt

Démarrer la machine

Il est possible de passer à UML un certain nombre de paramètres au démarrage. En fait, le mécanisme mis en jeu est sensiblement le même que celui utilisé pour passer des paramètres de boot au noyau à partir d'un bootloader comme LILO ou GRUB.

Ainsi, pour indiquer à UML d'utiliser notre fichier image comme partition de démarrage, nous utiliserons l'option ubda=lenny-image. Je vais aussi utiliser plusieurs autres options histoire de vous familiariser avec:

con=pts
Cette option précise à UML d'attacher les terminaux de la machine virtuelle sur les pseudo-terminaux de la machine réelle. Je l'utilise ici, car, par défaut, UML attache un certain nombre de consoles sur des terminaux X. D'abord, ça m'énerve ces pop up qui surgissent quand je lance une machine virtuelle, mais surtout, ça pose des problèmes quand on lance une machine UML à partir d'une connexion à l'hôte distante (par ssh, par exemple).
con0=fd:0,fd:1
J'utilise une règle spéciale pour la console 0 qui sera attachée à l'entrée standard et à la sortie standard de la console dans laquelle la machine virtuelle a été lancée (en clair: la console virtuelle 0 sera la console réelle dans laquelle vous aurez lancé UML). Toujours pour la même raison: si je lance une machine UML à distance, je souhaite pouvoir me loguer dessus via le même terminal.
umid=uml
Cette option est juste un identifiant pour administrer la machine virtuelle à l'aide de uml_mconsole que nous verrons dans quelques instants. Si vous l'omettez, UML générera un identifiant aléatoire. L'utilisation de cette option est à ranger au chapitre des bonnes habitudes.
sh$ linux ubda=lenny-image con=pts  con0=fd:0,fd:1 umid=uml
Locating the bottom of the address space ... 0x0
Locating the top of the address space ... 0xc0000000
Core dump limits :
	soft - 0
	hard - NONE
Checking that ptrace can change system call numbers...OK
Checking syscall emulation patch for ptrace...OK
Checking advanced syscall emulation patch for ptrace...OK
Checking for tmpfs mount on /dev/shm...OK
Checking PROT_EXEC mmap in /dev/shm/...OK
Checking for the skas3 patch in the host:
  - /proc/mm...not found: No such file or directory
  - PTRACE_FAULTINFO...not found
  - PTRACE_LDT...not found
UML running in SKAS0 mode
Linux version 2.6.26 (2.6.26) (root@lart) (gcc version 4.3.2 (Debian 4.3.2-1.1) ) #2 Thu Mar 11 18:42:19 UTC 2010
[...]
Virtual console 1 assigned device '/dev/pts/6'
Virtual console 2 assigned device '/dev/pts/7'
Virtual console 3 assigned device '/dev/pts/8'
Virtual console 4 assigned device '/dev/pts/10'
Virtual console 5 assigned device '/dev/pts/11'
Virtual console 6 assigned device '/dev/pts/12'

Debian GNU/Linux 5.0 uml tty0

uml login: 

Et voilà: vous avez démarré une machine virtuelle UML. J'insiste un peu, mais remarquez que j'ai lancé cette machine virtuelle en tant qu'utilisateur de la machine hôte. Mais je peux me connecter en administrateur à la machine virtuelle:

uml login: root
Password: 
Last login: Mon Mar 22 18:13:07 UTC 2010 on tty0
Linux uml 2.6.26 #2 Thu Mar 11 18:42:19 UTC 2010 i686

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
uml:~# 

Piège:

Si vous n'êtes pas familier avec la notion de virtualisation, la situation peut sembler étrange: vous pouvez être root sur une machine lancée en tant qu'utilisateur.

Faille de sécurité? Escalade de privilège? Non: en fait vous êtes root, mais sur votre machine virtuelle. Et ce root n'est pas le même que le root de la machine hôte. Toute la subtilité est là. Autrement dit, vous pouvez faire tout ce que vous voulez de votre machine virtuelle – mais vous êtes limité dans vos actions sur l'hôte aux privilèges qu'y possède l'utilisateur qui a lancé la machine virtuelle.

Console d'administration

Avant de terminer, un mot sur uml_mconsole, la console d'administration. Celle-ci permet d'agir sur la machine virtuelle de l'extérieur de celle-ci. Par analogie, la console d'administration vous permet un peu de faire ce que vous pouvez faire dans le monde réel avec une machine physique, comme appuyer sur le bouton marche/arrêt ou installer une nouvelle carte réseau. A ceci prêt que vous pouvez agir à chaud sur la machine:

sh$ uml_mconsole uml
(uml) help
OK Commands: 
    version - Get kernel version 
    help - Print this message 
    halt - Halt UML 
    reboot - Reboot UML 
    config <dev>=<config> - Add a new device to UML;  
	same syntax as command line 
    config <dev> - Query the configuration of a device 
    remove <dev> - Remove a device from UML 
    sysrq <letter> - Performs the SysRq action controlled by the letter 
    cad - invoke the Ctrl-Alt-Del handler 
    stop - pause the UML; it will do nothing until it receives a 'go' 
    go - continue the UML after a 'stop' 
    log <string> - make UML enter <string> into the kernel log
    proc <file> - returns the contents of the UML's /proc/<file>
    stack <pid> - returns the stack of the specified pid

Additional local mconsole commands:
    quit - Quit mconsole
    switch <socket-name> - Switch control to the given machine
    log -f <filename> - use contents of <filename> as UML log messages
    mconsole-version - version of this mconsole program
    int  - Interrupt UML session

Piège:

uml_mconsole est à utiliser à partir d'une console de la machine hôte! Pas à partir d'une console de la machine virtuelle.

Pour essayer la console d'administration, vous pouvez par exemple rebooter la machine:

(uml) reboot
OK 

Si vous avez sous les yeux la console de la machine virtuelle, vous constaterez qu'après avoir tapé cette commande, la machine virtuelle redémarre comme si vous aviez appuyé sur le bouton reset d'une machine physique.

Conclusion

Ce rapide tour d'horizon est terminé. La machine virtuelle que nous avons installée est totalement utilisable. Bien qu'isolée du réseau – et de son hôte – pour l'instant. Je vous encourage à jouer un peu avec, pour vous familiariser avec UML. Ou plutôt pour bien vous rendre compte que cette machine virtuelle se comporte presqu'en tout point comme une machine réelle.

Ressources