nixos/n8n: handle _FILE env variables with systemd's LoadCredential (#460626)
This commit is contained in:
@@ -6,6 +6,18 @@
|
||||
}:
|
||||
let
|
||||
cfg = config.services.n8n;
|
||||
|
||||
envVarToCredName = varName: lib.toLower varName;
|
||||
|
||||
# Partition environment variables into regular and file-based (_FILE suffix)
|
||||
regularEnv = lib.filterAttrs (name: _value: !(lib.hasSuffix "_FILE" name)) cfg.environment;
|
||||
fileBasedEnv = lib.filterAttrs (name: _value: lib.hasSuffix "_FILE" name) cfg.environment;
|
||||
|
||||
# Transform file-based env vars to point to credentials directory
|
||||
fileBasedEnvTransformed = lib.mapAttrs' (
|
||||
varName: _secretPath: lib.nameValuePair varName "%d/${envVarToCredName varName}"
|
||||
) fileBasedEnv;
|
||||
|
||||
in
|
||||
{
|
||||
imports = [
|
||||
@@ -32,6 +44,18 @@ in
|
||||
description = ''
|
||||
Environment variables to pass to the n8n service.
|
||||
See <https://docs.n8n.io/hosting/configuration/environment-variables/> for available options.
|
||||
|
||||
Environment variables ending with `_FILE` are automatically handled as secrets:
|
||||
they are loaded via systemd credentials for secure access with `DynamicUser=true`.
|
||||
|
||||
This can be useful to pass secrets via tools like `agenix` or `sops-nix`.
|
||||
'';
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
N8N_ENCRYPTION_KEY_FILE = "/run/n8n/encryption_key";
|
||||
DB_POSTGRESDB_PASSWORD_FILE = "/run/n8n/db_postgresdb_password";
|
||||
WEBHOOK_URL = "https://n8n.example.com";
|
||||
}
|
||||
'';
|
||||
type = lib.types.submodule {
|
||||
freeformType =
|
||||
@@ -92,15 +116,22 @@ in
|
||||
description = "n8n service";
|
||||
after = [ "network.target" ];
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
environment = cfg.environment // {
|
||||
HOME = config.services.n8n.environment.N8N_USER_FOLDER;
|
||||
};
|
||||
environment =
|
||||
regularEnv
|
||||
// {
|
||||
HOME = config.services.n8n.environment.N8N_USER_FOLDER;
|
||||
}
|
||||
// fileBasedEnvTransformed;
|
||||
serviceConfig = {
|
||||
Type = "simple";
|
||||
ExecStart = lib.getExe cfg.package;
|
||||
Restart = "on-failure";
|
||||
StateDirectory = "n8n";
|
||||
|
||||
LoadCredential = lib.mapAttrsToList (
|
||||
varName: secretPath: "${envVarToCredName varName}:${secretPath}"
|
||||
) fileBasedEnv;
|
||||
|
||||
# Basic Hardening
|
||||
NoNewPrivileges = "yes";
|
||||
PrivateTmp = "yes";
|
||||
|
||||
+10
-1
@@ -1,7 +1,8 @@
|
||||
{ lib, ... }:
|
||||
{ lib, pkgs, ... }:
|
||||
let
|
||||
port = 5678;
|
||||
webhookUrl = "http://example.com";
|
||||
secretFile = toString (pkgs.writeText "n8n-encryption-key" "test-encryption-key-12345");
|
||||
in
|
||||
{
|
||||
name = "n8n";
|
||||
@@ -18,6 +19,8 @@ in
|
||||
WEBHOOK_URL = webhookUrl;
|
||||
N8N_TEMPLATES_ENABLED = false;
|
||||
DB_PING_INTERVAL_SECONDS = 2;
|
||||
# !!! Don't do this with real keys. The /nix store is world-readable!
|
||||
N8N_ENCRYPTION_KEY_FILE = secretFile;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -25,6 +28,8 @@ in
|
||||
testScript = ''
|
||||
machine.wait_for_unit("n8n.service")
|
||||
machine.wait_for_console_text("Editor is now accessible via")
|
||||
|
||||
# Test regular environment variables
|
||||
machine.succeed("curl --fail -vvv http://localhost:${toString port}/")
|
||||
machine.succeed("grep -qF ${webhookUrl} /etc/systemd/system/n8n.service")
|
||||
machine.succeed("grep -qF 'HOME=/var/lib/n8n' /etc/systemd/system/n8n.service")
|
||||
@@ -32,5 +37,9 @@ in
|
||||
machine.succeed("grep -qF 'N8N_DIAGNOSTICS_ENABLED=false' /etc/systemd/system/n8n.service")
|
||||
machine.succeed("grep -qF 'N8N_TEMPLATES_ENABLED=false' /etc/systemd/system/n8n.service")
|
||||
machine.succeed("grep -qF 'DB_PING_INTERVAL_SECONDS=2' /etc/systemd/system/n8n.service")
|
||||
|
||||
# Test _FILE environment variables
|
||||
machine.succeed("grep -qF 'LoadCredential=n8n_encryption_key_file:${secretFile}' /etc/systemd/system/n8n.service")
|
||||
machine.succeed("grep -qF 'N8N_ENCRYPTION_KEY_FILE=%d/n8n_encryption_key_file' /etc/systemd/system/n8n.service")
|
||||
'';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user