ng'stuff

🔭 🎶 🕺

Confier ses secrets à systemd

Fondation des systèmes Linux orchestrant les dépendances entre services, systemd s’impose comme choix privilégié pour piloter les instances applicatives projet. Ses composants permettent un contrôle optimal des droits et comportements attendus sans surcouche supplémentaire.

Que ce soit pour accéder à une base de données, envoyer les mails ou interagir avec une API externe, les services ont couramment besoin de secrets / credentials pour fonctionner.

API

Un mécanisme simple est alors embarqué par systemd pour cibler précisément leur accès. Présentons-le sans détour :

  • Le paramètre LoadCredential=secretfile:/run/secrets/file du service
  • La variable d’environnement $CREDENTIALS_DIRECTORY que systemd définit

/run/secrets/file est ici un fichier secret (géré par un système adéquat). Au runtime, $CREDENTIALS_DIRECTORY pointera vers un dossier temporaire dans lequel systemd placera un fichier secretfile qui contiendra le contenu de /run/secrets/file.

Si et c’est justement l’intérêt — montrons-le en présentant d’abord la méthode basique sans LoadCredential.

Méthode basique

  1. Exposer un fichier secret possédé par un user donné, disons app-user,
  2. Transférer le chemin vers ce fichier au service voulu par argument ou variable d’environnement.

Méthode précise

  1. Monter un fichier secret possédé par l’administrateur, disons root,
  2. Définir LoadCredential=secretfile:/run/secrets/file
  3. Récupérer le secret en lisant $CREDENTIALS_DIRECTORY/secretfile

Les secrets présents dans le dossier $CREDENTIALS_DIRECTORY appartiendront ainsi au même user que le service, même s’il est dynamique et bien que /run/secrets/file appartienne à root (du moment que root pilote le service).

systemd-creds

Implémentation

systemd
[Service]
DynamicUser=yes
ExecStart=/usr/bin/app
Restart=on-failure
LoadCredential=secretfile:/run/secrets/file

Nix, favorisant toujours les solutions standard et déclaratives, intègre systemd naturellement. Main dans la main, ils permettent alors de piloter une infrastructure de services de façon déclarative, sûre et sans couche conteneur.

On propose donc une version NixOS, en supposant sops-nix déjà configuré :

nix
{ config, pkgs, ... }:
{
  sops.secrets."file".owner = "root";

  systemd.services.site = {
    serviceConfig = {
      DynamicUser = true;
      ExecStart = "${pkgs.app}/bin/app";
      Restart = "on-failure";
      LoadCredential = [ "secretfile:${config.sops.secrets."file".path}" ];
    };
  };
}
Retour aux articles 👀