Richard Dern

ChatGPT a cassé mon serveur - mais il l'a réparé

ChatGPT a cassé mon serveur - mais il l'a réparé

Suite à un article relayé par le journal du hacker, j’ai décidé de passer à SOPS pour la gestion des secrets dans ma configuration de NixOS.

En parallèle, j’ai confié à ChatGPT les clés de mon réseau : je l’ai rendu capable d’administrer l’ensemble de mes serveurs. C’est lui qui a procédé à la migration vers SOPS, et ça a été un succès total pendant plusieurs jours. Jusqu’à aujourd’hui, que je redémarre mon serveur et qu’il ne réponde plus.

(kernel) panique

Après avoir branché un écran et un clavier, je vois que le chargement de NixOS bute (avec un kernel panic) sur la création du fichier de secret déchiffré pour la clé d’API de MeiliSearch, en raison d’un utilisateur inconnu (meilisearch). Logique : je (le module NixOS) n’ai pas créé d’utilisateur spécifique pour MeiliSearch.

Je me dis que ce n’est pas bien grave, il me suffit de redémarrer sur une génération antérieure de NixOS. Après tout, le système est fait pour ça. Sauf que, c’est plus compliqué que ça, et aucune génération ne pouvait plus booter.

Je redémarre la machine sur une clé d’installation de NixOS, pour au moins avoir un shell et accéder à mes fichiers sur le disque, au cas où tout espoir de reprise serait vaincu, puis j’en profite quand même pour voir ce que c’est que cette histoire d’utilisateur inconnu.

Je monte les disques pour que l’installateur y ait accès :

mkdir /mnt/boot
mount /dev/disk/by-label/nixos /mnt
mount /dev/disk/by-label/boot /mnt/boot

Et j’inspecte ma configuration, stockée dans /mnt/etc/nixos/. Je réalise alors que ChatGPT avait affecté ce fameux utilisateur meilisearch au fichier concerné :

secrets."services/meilisearch/master-key" = mkSecret {
    path = "/etc/nixos/secrets/services/meilisearch/master-key";
    owner = "meilisearch";
    group = "meilisearch";
    restartUnits = [ "meilisearch.service" ];
};

C’est parfaitement logique pour un outil prédictif : s’il y a un service meilisearch, l’administrateur a forcément créé un utilisateur meilisearch. Principe de base de la sécurité informatique.

Mais le paquet fournit par NixOS ne crée pas automatiquement cet utilisateur, et en prime, ce fichier doit être lisible à un utilisateur particulier (que j’avais pourtant indiqué à ChatGPT dans mes instructions).

Je corrige la déclaration d’appartenance de ce fichier selon ma convenance, puis j’essaye de me chroot avec nix-enter afin d’appliquer ma configuration. Et là, les ennuis ont réellement commencé.

Shell en état de choc

[root@nixos:~]# nixos-enter --root /mnt setting up /etc
... setting up secrets...
/nix/store/r6wx09ss7v1775x67pypl5gqzwm71irg-sops-install-secrets-0.0.1/bin/sops-install-secrets: manifest is not valid: failed to lookup user 'meilisearch': user: unknown user meilisearch
Activation script snippet 'setupSecrets' failed (1) /nix/var/nix/profiles/system/activate: line 247: /run/current-system/sw/bin/mktemp: No such file or directory

Le problème à ce stade, c’est que j’ai cramé mes tokens Codex jusqu’au 3 avril (on est le 1er). Il n’y a que le chat en ligne qui peut m’aider, et il n’a pas accès à ma configuration (que je n’ai pas envie de lui envoyer compte tenu de sa complexité). Néanmoins, il me suggère quand même de faire autrement, sans chroot, avec un simple nixos-install --root /mnt. La commande échoue sur error: path '/var/lib/sops-nix/recovery-keys.txt' does not exist.

Le point important est le suivant : en Nix, un chemin écrit comme un vrai chemin Nix, par exemple /var/lib/..., est traité comme un objet path et il doit exister au moment de l’évaluation. La documentation Nix indique bien qu’un chemin référencé doit exister, et que l’existence d’un chemin peut être testée à l’évaluation avec builtins.pathExists. Donc, dans ma configuration, au lieu de faire :

sops.age.keyFile = /var/lib/sops-nix/recovery-keys.txt;

Je devais faire :

sops.age.keyFile = "/var/lib/sops-nix/recovery-keys.txt";

Notez les guillemets.

Après cette rapide correction, je relance l’installation :

[root@nixos:~]# nixos-install --root /mnt
copying channel...
building the configuration in /mnt/etc/nixos/configuration.nix...
these 2 derivations will be built:
  /nix/store/r4bgszxqmfikbsg9xv40a1p034gl597g-manifest.json.drv
  /nix/store/rf2zl9lh7qiidifvckfr1c0kl633ss97-nixos-system-server-main-25.11.8023.4590696c8693.drv
building '/nix/store/r4bgszxqmfikbsg9xv40a1p034gl597g-manifest.json.drv'...
building '/nix/store/rf2zl9lh7qiidifvckfr1c0kl633ss97-nixos-system-server-main-25.11.8023.4590696c8693.drv'...
/nix/store/isdjvp7wwxwi1a7c8vp3l8hgrdh5yfny-nixos-system-server-main-25.11.8023.4590696c8693
installing the boot loader...
setting up /etc...
setting up secrets...
sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age13kq73j2ka8y5tq7nl4cccajk9dftz45k2jl7v2ymuuu24n5frues58h2r5
/nix/var/nix/profiles/system/activate: line 230: /run/current-system/sw/bin/mktemp: No such file or directory
/nix/var/nix/profiles/system/sw/bin/bash: line 12: /run/current-system/bin/switch-to-configuration: No such file or directory

ChatGPT me conseille alors de rebooter sans tenir compte de ces erreurs. Je redémarre, mais je vois qu’il n’y a aucune entrée GRUB pour la nouvelle génération (logique : regardez la dernière ligne d’historique ci-dessus). Malheureusement, nouveau kernel panic, cette fois sur l’absence de mktemp. Nouveau redémarrage sur la clé et nouveau montage des disques, et en faisant nixos-enter, je commence à me dire qu’il ne sait pas où chercher ses exécutables.

[root@nixos:~]# ls -l /mnt/nix/var/nix/profiles/system/bin
ls -l /mnt/nix/var/nix/profiles/system/sw/bin | grep bootctl
ls: cannot access '/mnt/nix/var/nix/profiles/system/bin': No such file or directory
ls: cannot access '/mnt/nix/var/nix/profiles/system/sw/bin': No such file or directory

On sait qu’un truc déconne bien quand même ls ne répond pas… Pourtant, je vois que de nouvelles générations ont été produites :

/run/current-system/sw/bin/ls -l /boot/loader/entries | /run/current-system/sw/bin/tail -n 10

Me montre bien un fichier nixos-generation-481.conf. C’est donc à l’étape de la génération du bootloader que ça déconne.

Laisser NixOS gérer les chemins de fichiers…

On a finalement déterminé la cause exacte de mon problème :

tmp=$(/run/current-system/sw/bin/mktemp)
trap 'rm -f "$tmp"' EXIT

/run/current-system/sw/bin/ssh-to-age -private-key -i "$agentSshKey" > "$tmp"

On ne devrait jamais déclarer un chemin vers un exécutable sans l’extrapoler depuis pkgs. Il a suffi de corriger cela :

tmp=$(${pkgs.coreutils}/bin/mktemp -p /run sops-age-key.XXXXXXXXXX)
trap '${pkgs.coreutils}/bin/rm -f "$tmp"' EXIT

${pkgs.ssh-to-age}/bin/ssh-to-age -private-key -i "$agentSshKey" > "$tmp"

Nouveau nixos-install. Les dernières étapes sont toujours problématiques, notamment la mise à jour de GRUB. Mais au moins, je pouvais enfin accéder à mon chroot, en précisant à nixos-enter la génération 482 qui devait avoir réglé mes problèmes, afin de régénérer GRUB manuellement :

[root@nixos:~]# nixos-enter --root /mnt --system /nix/var/nix/profiles/system-482-link -- \
  /nix/var/nix/profiles/system-482-link/sw/bin/bash --noprofile --norc
setting up /etc...
setting up secrets...
sops-install-secrets: Imported /etc/ssh/ssh_host_ed25519_key as age key with fingerprint age13kq73j2ka8y5tq7nl4cccajk9dftz45k2jl7v2ymuuu24n5frues58h2r5
/nix/var/nix/profiles/system-482-link/activate: line 230: /run/current-system/sw/bin/mktemp: No such file or directory
bash-5.3# /nix/var/nix/profiles/system-482-link/sw/bin/mkdir -p /run
/nix/var/nix/profiles/system-482-link/sw/bin/ln -sfn /nix/var/nix/profiles/system-482-link /run/current-system
export PATH=/run/current-system/sw/bin:/nix/var/nix/profiles/system-482-link/sw/bin:$PATH
NIXOS_INSTALL_BOOTLOADER=1 /run/current-system/bin/switch-to-configuration boot
Running in a chroot, enabling --graceful.
Copied "/nix/store/bigkpra9jw48fip69q4wndsf4kb3d2w9-systemd-258.3/lib/systemd/boot/efi/systemd-bootx64.efi" to "/boot/EFI/systemd/systemd-bootx64.efi".
Copied "/nix/store/bigkpra9jw48fip69q4wndsf4kb3d2w9-systemd-258.3/lib/systemd/boot/efi/systemd-bootx64.efi" to "/boot/EFI/BOOT/BOOTX64.EFI".
⚠️  Mount point '/boot' which backs the random seed file is world accessible, which is a security hole!  ⚠️
⚠️ Random seed file '/boot/loader/random-seed' is world accessible, which is a security hole! ⚠️
Random seed file /boot/loader/random-seed successfully refreshed (32 bytes).
Created EFI boot entry "Linux Boot Manager".
bash-5.3# /run/current-system/sw/bin/ls -l /boot/loader/entries | /run/current-system/sw/bin/tail -n 10
total 16
-rwxr-xr-x 1 root root 473 Apr  1 15:25 nixos-generation-479.conf
-rwxr-xr-x 1 root root 473 Apr  1 15:25 nixos-generation-480.conf
-rwxr-xr-x 1 root root 473 Apr  1 15:25 nixos-generation-481.conf
-rwxr-xr-x 1 root root 473 Apr  1 15:25 nixos-generation-482.conf
bash-5.3#

Un dernier nixos-install et, cette fois, il va jusqu’au bout, sans le moindre message d’erreur. J’ai redémarré, les entrées GRUB étaient mises à jour, et NixOS a démarré sans broncher. Aucune perte à déplorer.

Conclusion

Franchement, c’est flippant à mort quand on fait ce genre d’expérience pour la première fois. J’en ai voulu à ChatGPT, à SOPS et à NixOS, et j’étais en train de me demander si je n’allais pas formater et réinstaller.

Le problème s’est révélé être double :

Je suis totalement responsable du second point étant donné que je ne le fais pas toujours. Mais ChatGPT aurait pu prendre les devants et m’aider à les corriger, au lieu de se contenter de (mal) faire comme moi.

Mais maintenant, je sais quelles instructions supplémentaires je dois donner à ChatGPT pour éviter qu’il ne commette à nouveau ce genre d’erreur (ne jamais indiquer des chemins sans extrapoler pkgs quand c’est possible, par exemple). Je n’ai pas moins confiance en ce que produit ChatGPT. Je ne crois pas que l’on doit donner moins de privilèges aux LLM. Je crois juste qu’ils ont besoin d’être cadrés, et je ne l’ai pas fait suffisamment.

Échanger autour de ce texte

Si vous souhaitez réagir publiquement, un fil dédié vous attend.

Ouvrir le fil de discussion

Taxonomies

Entreprises
Tags