«« sommaire »»

II.1 – Personnalisation de la configuration

Christophe BLAESS - juillet 2021

Dans la première partie, nous avons découvert le fonctionnement de base de Yocto. Nous avons réussi à produire et tester des images pour différentes plateformes. Toutefois nous n’avons absolument rien modifié au contenu de l’image. Dans cette nouvelle partie nous allons personnaliser notre système embarqué. Pour la plupart des constructions dans les séquences à venir nous emploierons une cible Arm émulée par Qemu pour la simplicité des tests et des captures d’écran. Néanmoins les opérations décrites ici seront adaptables sur n’importe quelle carte supportée par Yocto.

Débutons donc cette séquence avec la commande :

[Yocto-lab]$ source  poky/oe-init-build-env  build-qemu
 [...]
[build-qemu]$

Jusqu’à présent nous n’avons modifié aucun fichier fourni par Yocto. Nous nous sommes contentés d’indiquer sur la ligne de commande la machine cible désirée par l'intermédiaire d'une variable d'environnement.

On se doute bien que cela n’est pas une solution pérenne. Tout d’abord on risque d’oublier d'initialiser une variable au moment de lancer un build, mais en outre nous allons très vite avoir besoin de remplir une — voire plusieurs — dizaines de variables pour ajuster le contenu de notre système, ce qui n’est pas manipulable depuis la ligne de commande.

Fichier local.conf

Nous allons commencer par intervenir dans le fichier «local.conf» se trouvant dans le sous-répertoire «conf/» de notre répertoire de compilation. Ultérieurement nous créerons notre propre image pour regrouper notre configuration de manière plus réutilisable et plus facile à versionner indépendamment Yocto.

J’ai l’habitude de regrouper les modifications que j’apporte à «local.conf» vers le début de ce fichier en les encadrant clairement pour pouvoir identifier facilement mes actions. En voici un exemple :

[...]
# <LOGILIN>

MACHINE = "qemuarm"
DL_DIR = "${TOPDIR}/../downloads"
SSTATE_DIR = "${TOPDIR}/../sstate-cache"

# </LOGILIN>
[...]

Bien sûr dès que la complexité des modifications dépasse quelques lignes, il devient préférable de les suivre par l’intermédiaire d’un système de gestion de versions comme git.

Les variables renseignées ici sont les suivantes :

Nous pouvons relancer une génération d’image avec :

[build-qemu]$ bitbake  core-image-base

La cible «core-image-base» est un peu plus riche que «core-image-minimal» que nous avons employée dans les séquences précédentes. Une fois la compilation terminée, on démarre l’émulateur avec :

[build-qemu]$ runqemu  qemuarm

La première différence avec l’image minimale est l’apparition d’un splashscreen, une barre de progression graphique pendant le démarrage comme nous le voyons sur la figure II.1-1.

Le nombre de services démarrés au boot semble également un peu plus important qu’auparavant :

Poky (Yocto Project Reference Distro) 3.3.1 qemuarm /dev/ttyAMA0

qemuarm login: root
root@qemuarm:~# ps 
  PID USER       VSZ STAT COMMAND
    1 root      1440 S    init [5]
    2 root         0 SW   [kthreadd]
    3 root         0 IW<  [rcu_gp]
    4 root         0 IW<  [rcu_par_gp]
    5 root         0 IW   [kworker/0:0-pm]
    6 root         0 IW<  [kworker/0:0H-ev]
    7 root         0 IW   [kworker/u2:0-ev]
    8 root         0 IW<  [mm_percpu_wq]
    9 root         0 SW   [rcu_tasks_kthre]
   10 root         0 SW   [rcu_tasks_rude_]
   11 root         0 SW   [rcu_tasks_trace]
   12 root         0 SW   [ksoftirqd/0]
   13 root         0 IW   [rcu_preempt]
   14 root         0 SW   [migration/0]
   15 root         0 SW   [cpuhp/0]
   16 root         0 SW   [kdevtmpfs]
   17 root         0 IW<  [netns]
   18 root         0 IW   [kworker/0:1-eve]
   19 root         0 SW   [oom_reaper]
   20 root         0 IW<  [writeback]
   21 root         0 SW   [kcompactd0]
   37 root         0 IW<  [cryptd]
   55 root         0 IW<  [kblockd]
   56 root         0 IW<  [blkcg_punt_bio]
   57 root         0 IW<  [md]
   58 root         0 SW   [watchdogd]
   59 root         0 IW<  [kworker/0:1H-kb]
   60 root         0 IW<  [rpciod]
   61 root         0 IW<  [kworker/u3:0]
   62 root         0 IW<  [xprtiod]
   82 root         0 SW   [kswapd0]
   83 root         0 IW<  [nfsiod]
   84 root         0 IW<  [cifsiod]
   85 root         0 IW<  [smb3decryptd]
   86 root         0 IW<  [cifsfileinfoput]
   87 root         0 IW<  [cifsoplockd]
   89 root         0 SW   [hwrng]
   90 root         0 IW<  [ttm_swap]
   91 root         0 IW   [kworker/u2:1-ev]
   92 root         0 IW<  [raid5wq]
   93 root         0 IW<  [dm_bufio_cache]
   94 root         0 IW<  [ipv6_addrconf]
  107 root         0 IW   [kworker/0:2-eve]
  108 root         0 SW   [jbd2/vda-8]
  109 root         0 IW<  [ext4-rsv-conver]
  144 root      2380 S    /sbin/udevd -d
  157 root         0 IW   [kworker/u2:2]
  262 messageb  2204 S    /usr/bin/dbus-daemon --system
  267 rpc       1872 S    /usr/sbin/rpcbind
  284 root      2280 S    /sbin/syslogd -n -O /var/log/messages
  288 root      2280 S    /sbin/klogd -n
  295 avahi     2844 S    avahi-daemon: running [qemuarm.local]
  296 avahi     2740 S    avahi-daemon: chroot helper
  305 root      2280 S    {start_getty} /bin/sh /bin/start_getty 115200 ttyAMA0 vt102
  306 root      2280 S    /sbin/getty 38400 tty1
  308 root      2368 S    -sh
  311 root      2368 R    ps
root@qemuarm:~#

Utilisateurs et mots de passe

Comme auparavant nous pouvons nous connecter sous l’identité «root» sans mot de passe. Ceci est évidemment très dérangeant pour des raisons de sécurité :

Nous souhaitons donc fixer un mot de passe pour l’administrateur root (par exemple «linux») et créer un compte utilisateur standard, disons «guest», avec le mot de passe «welcome».

Bien évidemment ces mots de passe sont des exemples typiques de ce qu’il ne faut pas faire. Ils existent dans des dictionnaires et sont suffisamment courts pour être trouvés par force brute. Un bon mot de passe est un mot de passe long et facile à mémoriser (par exemple une association de plusieurs mots sans cohérence entre eux). Inutile de mélanger des lettres majuscules, minuscules et des chiffres, ça n’ajoute que très peu d’entropie à la chaîne de caractères. À mon avis, la meilleure explication est celle de Randall Munroe sur son site XKCD :

Pour ajouter un utilisateur standard et modifier le mot de passe de root nous allons intervenir dans le fichier «local.conf». Tout d’abord nous devons ajouter une ligne un peu mystérieuse :

INHERIT += "extrausers"

Elle indique simplement que notre configuration va pouvoir utiliser les services d’une classe définie dans Poky permettant la personnalisation des utilisateurs. On notera la syntaxe «+=» pour indiquer que la chaîne de caractères «extrausers» est ajoutée à la liste des classes contenues dans «INHERIT».

Pour créer le compte utilisateur «guest» on ajoutera la ligne suivante :

EXTRA_USERS_PARAMS += "useradd  -P welcome  guest;"

On ajoute (+=) ainsi une commande (useradd) dans la variable de paramétrage de la classe extrausers. Plusieurs commandes peuvent être enchaînées, elles sont séparées par un deux-points.

La commande «useradd» appelée n’est pas celle du système («/usr/sbin/useradd» sur la plupart des distributions) mais est fournie par Poky dans l’ensemble des outils du système de développement.

Attention ! Depuis le dernier trimestre 2021 il n'est plus possible (pour les raisons de sécurité expliquéesci-dessous) d'utiliser l'option -P avec useradd et usermod. Il est devenu obligatoire d'utiliser l'option -p décrite un peu plus loin. Cela complique un tout petit peu la modification des mots de passe mais c'est une très bonne nouvelle en ce qui concerne la sécurité de votre projet.

On remarquera que non seulement le mot de passe «welcome» est mal choisi parce qu’il est trop simple, mais également parce qu’il utilise les lettres «w» et «m» qui se trouvent à des emplacements différents suivant le type de clavier (Azerty / Qwerty). Lorsque l’émulateur «qemuarm» démarre il est configuré avec un clavier Qwerty. Il nous faut donc taper «zelco,e» au lieu de «welcome» !

Nous pouvons ajouter un mot de passe pour root en invoquant la commande «usermod» ainsi :

EXTRA_USERS_PARAMS += "usermod  -P linux  root;"

Si nous relançons le build, nous pouvons tester nos connexions au bout de quelques dizaines de secondes :

Poky (Yocto Project Reference Distro) 3.3.1 qemuarm /dev/ttyAMA0
qemuarm login: root
Password: (linux)
root@qemuarm:~# whoami
root
root@qemuarm:~# exit
logout

Poky (Yocto Project Reference Distro) 3.3.1 qemuarm /dev/ttyAMA0
qemuarm login: guest
Password: (welcome)
qemuarm:~$ whoami
guest
qemuarm:~$ su -
Password: (linux)
root@qemuarm:~# whoami
root
root@qemuarm:~# exit
logout
qemuarm:~$ exit
logout

Cela fonctionne, mais la situation n’est pas très satisfaisante. Bien sûr les mots de passe sont hachés avant d’être inscrits sur la cible. On ne peut en aucun cas être retrouver (dans un temps raisonnable) les mots de passe initiaux, même en étudiant le contenu du fichier «/etc/shadow» qui contient les résultats du hachage (à condition évidemment d’utiliser des mots de passe solides, pas les exemples simplissimes que j’ai employés ici et qui se trouvent dans tous les dictionnaires de crack).

Néanmoins les mots en clair se trouvent dans le fichier «local.conf» sur la machine de production. Qu’en est-il de sa sécurité ? Peut-on compromettre la sécurité de tous les produits déjà déployés chez les clients simplement à cause d’un fichier en clair sur le poste d’un développeur ? La réponse est évidemment «non !», il va falloir chiffrer les mots de passe également dans le fichier «local.conf» de la machine de production.

Pour cela, les commandes «useradd» ou «usermod» acceptent une option «-p» (à la place de «-P») pour fournir un mot de passe haché par la fonction crypt() de la bibliothèque C. Pour accéder à cette fonction on peut faire beaucoup de choses différentes, mais j’ai écrit un petit utilitaire que je vous propose d'employer.

Le projet s’appelle password-encryption, il est sous licence GPL et disponible ainsi :

[build-qemu]$ git  clone  https://github.com/cpb-/password-encryption 
Clonage dans 'password-encryption'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (26/26), done.
remote: Total 42 (delta 19), reused 37 (delta 14), pack-reused 0
Dépaquetage des objets: 100% (42/42), fait.
[build-qemu]$ cd  password-encryption/
[password-encryption]$ make
gcc -Wall -W -c  hasher.c
gcc -o hasher hasher.o -lcrypt

On peut hacher un mot de passe simplement ainsi :

[password-encryption]$ ./hasher linux
$5$p4pbIZkJ$rdM34E4hsys2v5vSUnj6mbd9jcLQSzKH59rDDDWhvxC

L’algorithme utilisé par défaut est SHA-256, mais on peut préférer un hachage MD5 ou SHA-512. Une option «-e» est bien utile pour nous : elle permet de protéger à l’affichage les caractères «$» en les précédant par des backslashes «\». Nous pouvons ainsi copier-coller le hachage pour le placer dans «local.conf» sans risque d’interprétation erronée par bitbake sous forme de noms de variables.

[password-encryption]$ ./hasher  -e  linux
\$5\$IlhPmUHu\$AFPAiidalJ5Llt5YSqKoKXUbiWJjbvtRYT0AvzJp6p7 
[password-encryption]$ ./hasher  -e  welcome
\$5\$gFkqOeNU\$BOtEK2RhJIu.MxFT4jqtaYGYdw85fiRbc4hplRWI/63
[password-encryption]$ cd  ..
[build-qemu]$ 

Je modifie donc mon fichier «local.conf» pour devenir :

# <LOGILIN>

MACHINE = "qemuarm"
DL_DIR = "${TOPDIR}/../downloads"
SSTATE_DIR = "${TOPDIR}/../sstate-cache"

INHERIT += "extrausers"

EXTRA_USERS_PARAMS += "useradd -p '\$5\$IlhPmUHu\$AFPAiidalJ5Llt5YSqKoKXUbiWJjbvtRYT0AvzJp6p7' guest;" 

EXTRA_USERS_PARAMS += "usermod -p '\$5\$gFkqOeNU\$BOtEK2RhJIu.MxFT4jqtaYGYdw85fiRbc4hplRWI/63' root;"

# </LOGILIN> 

Attention à ne pas oublier d'encadrer le mot de passe par des quotes simples (apostrophes) et l'ensemble de la chaîne par des quotes doubles (guillemets). En outre, il faut utiliser l'option «-p» de useradd et usermode en remplacement de «-P».

Après recompilation de core-image-base, la connexion fonctionne toujours à l’identique mais le fichier «local.conf» n’est plus aussi critique en cas d’indiscrétion ou de fuite de données.

Hostname

Une seconde étape de personnalisation, toujours dans le fichier «local.conf» concerne le nom de la machine. Visible dans le prompt il vaut par défaut «qemuarm». Nous pouvons le personnaliser en ajoutant la ligne suivante dans «local.conf» :

hostname_pn-base-files = "mybox"

La syntaxe de cette ligne est plus complexe qu’elle en a l’air. Lorsque bitake analyse les recettes et les fichiers de configuration, il lit cette ligne ainsi : «Lors du traitement de la recette dont le nom de package (pn pour Package Name) est base-files, configurer la variable hostname avec la valeur mybox».

En règle générale, dans une affectation de variable, le caractère «souligné» (underscore) «_» permet de préciser ou restreindre la portée de l’opération. La chaîne de caractères «_pn» peut être imaginée comme une sorte d’opérateur pour restreindre la modification de l’affectation de la variable à la recette dont le nom est «base-files».

Comme son nom l’indique, la recette «base-files», livrée avec Poky, fournit un ensemble de fichiers de configuration standards comme /etc/fstab, /etc/host.conf, /etc/shells, etc. En outre elle fournit des méthodes pour personnaliser hostname, et proposer un message de bienvenue «Poky (Yocto Project Reference Distro) 3.3.1 qemuarm /dev/tty1» avant l’invite de connexion.

Ce message peut être modifié en surchargeant le fichier par défaut, nous le ferons dans une prochaine étape.

Après recompilation, la connexion au système affiche :

Poky (Yocto Project Reference Distro) 3.3.1 mybox /dev/ttyAMA0 

mybox login: root
Password: (linux)
root@mybox:~#

Conclusion

Nous avons édité dans cette séquence le fichier «local.conf» se trouvant dans le sous-dossier «conf/» du répertoire de compilation. Pour le moment nous avons simplement configuré les utilisateurs (et mots de passe) et le nom de machine, dès la prochaine séquence nous allons ajouter des utilitaires dans notre système embarqué.

Ce document est placé sous licence Creative Common CC-by-nc. Vous pouvez copier son contenu et le réemployer à votre gré pour une utilisation non-commerciale. Vous devez en outre mentionner sa provenance.

«« sommaire »»