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/filedu service - La variable d’environnement
$CREDENTIALS_DIRECTORYque 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
- Exposer un fichier secret possédé par un user donné, disons app-user,
- Transférer le chemin vers ce fichier au service voulu par argument ou variable d’environnement.
Méthode précise
- Monter un fichier secret possédé par l’administrateur, disons root,
- Définir
LoadCredential=secretfile:/run/secrets/file - 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).
Implémentation
[Service]
DynamicUser=yes
ExecStart=/usr/bin/app
Restart=on-failure
LoadCredential=secretfile:/run/secrets/fileNix, 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é :
{ 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}" ];
};
};
}