Gérer ses secrets quand on utilise git et qu'on partage son code (et Nixos)

Gestion des secrets dans un environnement déclaratif et public

Quand on met sa configuration système en ligne, encore plus quand on utilise git/gitea/gitlab/github, la gestion des secrets est importante.

Comment stocker un mot de passe sans le refiler à toute la terre ?

Il existe un projet libre, nommé age qui permet de chiffrer un fichier à partir de sa clé GPG ou SSH

Je n’utilise pas GPG pour plein de raisons. Par contre j’utilise SSH :)

Sous nixos, on dispose d’un outil qui utilise age, il s’agit de agenix

agenix va déchiffrer le fichier age que nous aurons généré (celui qui est stocké dans git) en un fichier que nous aurons choisi. Le contenu sera alors en clair sur le système, lisible en fonction des droits qu’on aura défini.

Sur la page github, il y a un tutorial, mais lors de la mise en oeuvre, il m’a manqué quelques informations. Le but de se billet, est donc de voir la mise en place de la solution.

Installation

La documentation officielle est limpide à ce sujet. Moi j’ai opté pour l’installation nix-channel qui est plus proche de mes setups. Je utilise ni niv ni flakes

Pour paraphraser la documentation, il suffit d’ajouter le dépot et de mettre à jour le référentiel :

sudo nix-channel --add https://github.com/ryantm/agenix/archive/main.tar.gz agenix
sudo nix-channel --update

Pour chiffrer le fichier, nous avons besoins de l’utilitaire agenix et pour que le système déchiffre le fichier, il a besoin du module. Il n’est pas nécessaire d’avoir les 2 sur le même poste. Seul le module est, bien sur, obligatoire.

Pour installer le module, il faut ajouter au fichier configuration.nix dans la zone d’import : <agenix/modules/age.nix>

Pour installer le programme il faut ajouter la ligne suivante n’import ou :

environment.systemPackages = [ (pkgs.callPackage <agenix/pkgs/agenix.nix> {}) ];

Ou vous pouvez centraliser comme moi, la gestion de vos secrets dans un fichier agenix.nix qui sera lui-même importé dans le configuration.nix

`
{ pkgs, ... }: {
  imports = [
    <agenix/modules/age.nix>
  ];

  environment.systemPackages = [ (pkgs.callPackage <agenix/pkgs/agenix.nix> {}) ]

  age.secrets.secret_restic = {
     file = ./secrets/secret_restic.age;
     path = "/run/restic_pass";
     owner = "restic";
     group = "restic";
  };
}
`

Comme on peut le voir, j’ai définit une variable age.secrets.secret_restic qui contient le chemin relatif vers le fichier chiffré, le path du fichier non chiffré, son owner et son groupe d’appartenance.

path, owner et group sont facultatifs.

Par défaut, agenix déchiffre dans /run/agenix.d/xx/secret_restic ou xx correspond à un nombre et représente la génération. Ce nombre varie à chaque appel de nixos-rebuild switch on comprend assez facilement l’interet de définir une copie fixe qui sera dans path

owner et group permettent de donner le droit à accéder au secret à des comptes précis.

Il nous reste à voir la génération du fichier age et la configuration de ma sauvegarde.

Comment s’articule secrets.nix, GPG, SSH et agenix ?

Nous avons 1 fichier age par mot de passe, et un fichier sercret.nix qui permet de définir quel clé SSH ou GPG peux déchiffrer le fichier age le tout dans un dossier secrets

Comme je souhaite utiliser une clé ssh, je dois déterminer quel clé est disponible :

ssh-keyscan 192.168.10.114

Attention, la commande nixos-rebuild ne va pas connaitre votre clé dans $HOME/.ssh mais va travailler à partir de /etc/ssh

Le fichier secrets.nix

Exemple d’un fichier secrets.nix

`
let
  restic = "ssh-ed25519
  AAAAC3NzaC1lZDI1NTE5AAAAIGgO3EpoG14fn0VYC69sSS0iI5ZEB4qx9adFS+L5U5ZB";
  users_backup = [ restic];
in
{
  "secret_restic.age".publicKeys = users_restic;
}
`

Exemple avec une autre clé public

`
let
  nextcloud-db = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKUA1RW6JwZasspAp8qmFRFnlV5WXjhLfStAAkM+KYLv";
  restic = "ssh-ed25519
  AAAAC3NzaC1lZDI1NTE5AAAAIGgO3EpoG14fn0VYC69sSS0iI5ZEB4qx9adFS+L5U5ZB";
  users_backup = [ restic];
  users_nextcloud = [ restic nextcloud-db ];
in
{
  "secret_restic.age".publicKeys = users_restic;
  "secret_nextclouddb.age".publicKeys = users_nextcloud;
}
`

Subtilité ici, car le groupe users_nexcloud est constitué de restic et nextcloudb

  • on défini autant de variable que nous avons clés publics, ici il s’agit de nextcloud-db et restic
  • on affecte le ou les comptes à des groupes. Ici le compte restic appartient au groupe users_backup et les comptes restic et nextcloud-db appartiennent au groupe users_nextcloud
  • on définit quel groupe peux éditer/modifier quel fichier age. Donc ici le groupe users_restic peux déchiffrer/modifier/créer le fichier secret_restic.age et le groupe users_nextcloud (donc les détenteurs des clés publics attachés à nextcloud-db ainsi que restic) peut éditer/modifier/créer le fichier secret_nextclouddb.age

Petite précision, on parle de user, mais nextcloud-db et restic ne sont pas des comptes utilisateur au sens unix, mais bien des variables qui permettent de nommée le détenteur de la clés public

Lorsque le fichier secrets.nix est créé, on peux générer nos fichiers age

Génération du fichier age

La génération du fichier age se fait donc après la création du fichier secrets.nix

Agenix s’appuis sur ce fichier pour chiffrer et permettre aux différent comptes défini de modifier ou lire le fichier généré. Parfait pour le travail d’équipe.

Ainsi pour chiffrer ou modifier mon mot de passe :

agenix -e secret_restic.age -i /etc/ssh/ssh_host_ed25519_key.pub

Comme on l’a vu un peu plus haut, le fichier secrets, permet de déclarer des clés, de les associer à des comptes, ou variables. Ces comptes vont être intégrés à un groupe d’utilisateur et c’est au groupe que l’on donne l’autorisation de déchiffrer le fichier.

En d’autres termes, on va autoriser 1 ou plusieurs utilisateurs, à partir de leur clé SSH ou GPG, d’accèder au fichier age déchiffré.

Utilisation dans le fichier backup.nix

Dans le fichier ci-dessous, on peut voir que l’utilisation dans une configuration est transparente, et pour cause, le fichier age est déchiffré dans /run/restic_pass

    `
    {config, pkgs, ...}:
    {
    services.restic.backups = {
      localbackup = {
          initialize = true;
          passwordFile = "/run/restic_pass";
          paths = [ "/var/lib/nextcloud/data/" ];
          repository = "rest:https://back.atlanticaweb.fr/Nextcloud";
          timerConfig = {
            OnCalendar = "00:05";
            RandomizedDelaySec = "5h";
          };
        };
    };
    }
    `

Pour finir

A partir de là, vous pouvez générer votre système avec nixos-rebuild –switch Vos services vons pouvoir fonctionner avec des mots de passes aux accès réduits, et ces fichiers seront versionnés dans git, sans risque de compromission.

Vous pouvez retrouver le code complet ici

Généré avec Hugo
Thème Stack conçu par Jimmy