personnalisation et auto installation d'une Ubuntu 24 LTS desktop
Sat, 09 Nov 2024 11:30:30 +0200Pour des raisons pro je maintenais une ISO Linux Ubuntu 20 puis 22 personnalisées. La création d’une ISO basée sur Debian utilise historiquement des programmes, scripts et un fichier .seed comme dans cet example.seed. Les programmes pour la construction de l’image sont entre autre chroot, xorriso, squashfs, des scripts bash etc.
Or depuis Ubuntu 23 environ, la construction d’une ISO a complètement changé et l’ancienne méthode ne fonctionne plus. Ce n’est pas plus mal de la part de Canonical de vouloir simplifier la personnalisation de leur OS.
Tout passe maintenant par un fichier autoinstall.yaml
dans lequel on décrira la configuration souhaitée : partitionnement, création d’utilisateur, programmes à préinstaller, etc.
Le bémol est que la documentation de Canonical à ce sujet est très basique et si l’on souhaite une préinstallation un peu plus complexe il y a peu d’infos. Voici la configuration que j’ai pu réaliser grâce à un blog et qui pourra certainement servir à des équipes qui souhaitent personnaliser et faciliter le déploiement d’Ubuntu au sein de leurs services.
Dans cet exemple les PC cibles possèdent un seul disque SSD, on souhaite le chiffrer et le partitionner en 3 (/ , /home, swap). Par défaut lorsqu’un disque est chiffré avec Luks, Linux demande au boot le mot de passe pour le déchiffrer. Ici on va utiliser la puce TPM2 pour stocker un mot un pass généré et auto unlock le disque. Cela a une certaine utilité car la clé est alors plus robuste que si l’utilisateur devait en saisir une plus simple à chaque boot.
Évidement si le PC est volé le disque sera lisible au démarrage mais le voleur devra se connecter au système en essayant de craquer le pass d’un des comptes.
L’autre besoin, non abordé ici, est de centraliser l’authentification des comptes vers par exemple un serveur LDAP ou un Active Directory. Les paquets sssd et sssd-tools realmd adcli krb5-user libnss-sss libpam-sss permettent de configurer cela.
autoinstall.yaml
autoinstall:
version: 1
# refresh-installer:
# update: true
# channel: latest/beta
shutdown: reboot
source:
id: ubuntu-desktop
locale: "fr_FR.UTF-8"
keyboard:
layout: fr
variant: ""
toggle: null
identity:
hostname: ubuntu-desktop
username: admin
password: '$1$sPm6PHMJ$OXDdHEovAWKO5Gu16xDvE1'
storage:
# layout:
# name: hybrid
# encrypted: yes
# sizing-policy: scaled
# password: tmp
config:
- id: disk0
name: disk0
type: disk
ptable: gpt
preserve: true
path: /dev/nvme0n1
grub_device: false
match:
ssd: true
- id: esp
type: partition
device: disk0
size: 1G
flag: boot
number: 1
wipe: superblock
grub_device: true
- id: esp-format
type: format
fstype: fat32
volume: esp
- id: boot
type: partition
device: disk0
size: 5G
number: 2
# preserve: true
- id: boot-format
type: format
fstype: ext4
volume: boot
- id: root
type: partition
device: disk0
size: -1
number: 3
wipe: superblock
- id: root-crypt
type: dm_crypt
dm_name: root-crypt
volume: root
key: tmp
- id: vgroot
type: lvm_volgroup
name: vgroot
devices: [root-crypt]
- id: lvroot
type: lvm_partition
name: lvroot
volgroup: vgroot
wipe: superblock
size: 100G
- id: lvroot-format
type: format
fstype: ext4
volume: lvroot
- id: lvswap
type: lvm_partition
name: lvswap
volgroup: vgroot
wipe: superblock
size: 20G
- id: lvswap-format
type: format
fstype: swap
volume: lvswap
- id: lvdata
type: lvm_partition
name: lvdata
volgroup: vgroot
wipe: superblock
size: -1
- id: lvdata-format
type: format
fstype: ext4
volume: lvdata
- id: boot-mount
type: mount
device: boot-format
path: /boot
- id: esp-mount
type: mount
device: esp-format
path: /boot/efi
- id: root-mount
type: mount
device: lvroot-format
path: /
- id: data-mount
type: mount
device: lvdata-format
path: /home
drivers:
install: true
snaps:
- name: teams-for-linux
classic: false
- name: localsend
classic: false
packages:
- vim
- adcli
- cifs-utils
- clevis
- clevis-tpm2
- clevis-luks
- clevis-initramfs
- initramfs-tools
- tss2
- openssh-server
- openssl
- git
- krb5-user
- libnss-sss
- libpam-sss
- live-tools
- oddjob
- oddjob-mkhomedir
- pwgen
- realmd
- samba
- samba-common
- samba-common-bin
- sssd
- sssd-tools
- ntp
- whois
- python3-pip
- python3-apt
- evolution
- evolution-ews
- gnome-shell-extensions
- powertop
- htop
- emacs
- libfuse2
- libnss3-tools
- ncdu
- qtcreator
- curl
- cryptsetup
late-commands:
- curtin in-target --target=/target -- curl https://codeberg.org/fredix/netboot/raw/branch/main/cryptsetup.sh -o /root/cryptsetup.sh
- curtin in-target --target=/target -- /bin/bash /root/cryptsetup.sh
user-data:
timezone: Europe/Paris
runcmd:
- git clone https://codeberg.org/fredix/netboot.git /home/admin/netboot
- chown -R admin:admin /home/admin/netboot
Tout d’abord on créé un compte admin qui sera en théorie le seul compte à authentification locale qui permettra d’administrer le PC à distance. Le mot de pass est chiffré avec openssl
echo "adminpass" > localpass
openssl passwd -in localpass
$1$sPm6PHMJ$OXDdHEovAWKO5Gu16xDvE1
J’ai laissé la section layout comme exemple en commentaire. En effet la documentation de Canonical propose cette section si l’on souhaite chiffrer le disque et stocker le mot de pass dans la TPM2. Or c’est une fausse piste car il n’est plus possible ensuite de définir des partitions personnalisées. Si la section layout est définie alors la section config ne sera pas lu par l’installateur.
La section config décrit le disque à chiffrer et les partitions à créer. Ici le disque est positionné en dur vers /dev/nvme0n1
c’est éventuellement à changer mais il est possible que path soit facultatif. Ensuite on créé les partitions pour l’EFI et boot (esp, boot) et la dernière root qui prend tout le reste du disque et qui est chiffré avec un mot de passe temporaire tmp.
Dans cette partition chiffrée on créé des volumes LVM dans lesquels il y aura les partitions / , /home , swap , les 2 premières sont formatées en ext4. La swap est encore utile si l’utilisateur souhaite passer son PC en hibernation. J’ai mis pour l’exemple 100Go pour la partition / et tout le reste pour /home, c’est à adapter en fonction de votre matériel et vos besoins.
On installe ensuite des paquets snap et deb en fonction des besoins utlisateurs. Attention les paquets clevis et cryptsetup sont obligatoire car il vont permettre d’auto unlock le disque chiffré et de générer une nouvelle clé Luks.
Pour cela on demande à l’auto installateur de récupérer le script cryptsetup.sh et de l’exécuter.
cryptsetup.sh
#!/bin/bash
echo -n "tmp" > /root/tmpkey
chmod 400 /root/tmpkey
clevis luks bind -f -k /root/tmpkey -d /dev/nvme0n1p3 tpm2 '{}'
openssl rand -hex 16 > /root/key
chmod 400 /root/key
cryptsetup luksAddKey /dev/nvme0n1p3 /root/key < /root/tmpkey
Ce script utilise le programme clevis (voir une doc sur le wiki de Arch) pour binder la partition chiffrée avec la puce TPM2 et déclencher l’auto-unlock. En cadeau je vous fais sauter des jours de debug avec le echo -n
car sans le saut de ligne clevis indiquera que le mot de pass est incorrect sans plus de précision 😑
Ensuite on génère une clé aléatoire que l’on ajoute comme nouvelle clé à Luks. Si vous avez suivi vous devriez lever un problème. En effet si le PC a un problème matériel et qu’il est nécessaire de récupérer des données utlisateur sur le disque, il ne sera pas possible de le déchiffrer car la clé est sur le disque chiffré dans /root/key.
Il est nécessaire de bien sauvegarder cette clé en dehors du PC, dans Bitwarden par exemple ou bien de déployer un outil de gestion de parc type GLPI ou FusionInventory pour qu’il la remonte automatiquement dans l’inventaire.
J’ai installé ces scripts dans mon dépôt public https://codeberg.org/fredix/netboot
que vous pouvez utiliser pour tester. La suite est de booter votre PC sur une clé USB Ubuntu 24 LTS desktop standard et se connecter au Wifi si vous n’etes pas en filaire. Arrivé sur l’écran Type d’installation il faut choisir Installation automatisée et saisir l’URL suivante :
https://codeberg.org/fredix/netboot/raw/branch/main/autoinstall.yaml
A savoir que si l’URL mène vers un certificat auto-signé ou signé par une autorité inconnue des navigateurs cela ne fonctionnera pas.
Lorsque l’installation est finie le PC reboot puis demande de saisir le mot de pass pour déchiffrer le disque. Il ne faut rien saisir, il suffit d’appuyer sur la touche Echap pour voir le log et attendre que l’installation se termine.
A la fin il va y avoir un git clone du dépôt git comme indiqué dans la section user-data : git clone https://codeberg.org/fredix/netboot.git /home/admin/netboot
Il vous suffit d’ajouter tout vos scripts dans ce dépôt pour lancer ensuite une post installation afin de finir de configurer le système, notamment lancer les configurations de sssd, l’installation de l’agent de votre gestionnaire de parc et des outils comme Rustdesk.
Dans cette photo on voit que le dépôt netboot a bien été cloné, la commande lsblk
montre que le chiffrement et les partitions sont en place.
Si vous avez des soucis vérifiez bien que TPM2 est activé dans le BIOS et que le PC en possède une évidement, sauf s’il est trop vieux.
La dernière sécurité à mettre en place est la protection du menu de boot Grub. Un attaquant qui y aurait accès pourrait modifier les paramètres de boot du kernel et obtenir l’accès root. Il suffit de lancer ce script en post installation pour ajouter un mot de pass généré si on veut accéder à l’édition du menu.
grub.sh
#!/bin/bash
# We need sudo privileges for this script
if [ $EUID != 0 ]; then
sudo "$0" "$@"
exit $?
fi
echo "set superusers=\"it\"" >> /etc/grub.d/40_custom
pass=$(pwgen -B 16 1)
echo "$pass" > /root/grub-key
chmod 700 /root/grub-key
password=$(echo -e "$pass\n$pass" | grub-mkpasswd-pbkdf2 | awk '/grub.pbkdf/{print $NF}')
echo "password_pbkdf2 it $password" >> /etc/grub.d/40_custom
# nécessaire si l'on veut éviter de saisir le mot de pass à chaque boot
sed -i "s/echo \"menuentry '\$(echo \"\$os\" | grub_quote)' \${CLASS}/echo \"menuentry '\$(echo \"\$os\" | grub_quote)' \${CLASS} --unrestricted/g" /etc/grub.d/10_linux
# Set grub keymap
grub-kbdcomp -o /boot/grub/french.gkb fr
echo "GRUB_TERMINAL_INPUT=\"at_keyboard\"" >> /etc/default/grub
echo "insmod keylayouts" >> /etc/grub.d/40_custom
echo 'keymap $prefix/french.gkb' >> /etc/grub.d/40_custom
#sed -i 's/#\(GRUB_TERMINAL=.*\)/\1/g' /etc/default/grub
sed -i 's/GRUB_TIMEOUT=0/GRUB_TIMEOUT=5/g' /etc/default/grub
sed -i 's/GRUB_TIMEOUT_STYLE=hidden/GRUB_TIMEOUT_STYLE=menu/g' /etc/default/grub
update-grub
Un utilisateur it est créé avec un mot de pass généré et stocké dans /root/grub-key (et à copier en dehors du PC).
Attention, bizarrement sur mon PC Asus de test le menu grub ne s’est pas affiché et le PC n’a pas booté tant que je n’appuyais pas sur la touche Echap. Le problème était résolu en forcant
GRUB_TERMINAL=console
mais alors le clavier dans grub reste en qwerty ce qui peut être bloquant si on doit saisir le mot de pass grub.
La première chose serait de ne plus avoir à booter par clé USB mais par un serveur PXE comme iVentoy. Mais à ce jour un bug plante l’auto-installation, l’installateur voit le disque monté par iVentoy et se crash.
Une autre optimisation serait de modifier le cloud init de l’ISO pour ajouter l’URL vers le fichier autoinstallation.yaml pour rendre l’install automatique.
On peut renforcer la sécurité en utlisant une Yubikey au lieu de la puce TPM2 comme le permet Clevis. Dans ce cas le PC sera illisible en cas de vol sans la Yubikey, mais également en cas de perte de celle-ci. A moduler selon la politique de sécurité choisie.
Je ferais peut être une suite en attendant vous avez une bonne base pour personnaliser automatiquement Ubuntu.
(Ce texte a été écrit avec VNote)