From e9bde1ace01fbdda0e42d321935aa098b5720777 Mon Sep 17 00:00:00 2001 From: Maximilian Bosch Date: Tue, 8 Apr 2025 12:52:35 +0200 Subject: [PATCH] module: prohibit store-paths for environmentFile The store is world-readable, so secrets shouldn't end up there in the first place. On top, `types.path` has the following behavior: * `toString foo` returns the absolute path * `${foo}` copies the path silently into the store and returns the store-path. This happens without any real feedback, so this can be caused by an innocent looking change. To address this problem, `pathsWith` was introduced into which allows absolute paths represented as string, but rejects things pointing to the store and path literals which may be copied later on. --- module.nix | 11 ++++++++--- tests/minimal-vmtest.nix | 17 ++++++++++------- tests/override-scope.nix | 16 ++++++++++------ 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/module.nix b/module.nix index 970e3c0..5d8a117 100644 --- a/module.nix +++ b/module.nix @@ -44,6 +44,11 @@ let ; settingsFormat = pkgs.formats.yaml { }; + + pathToSecret = types.pathWith { + inStore = false; + absolute = true; + }; in { options.services = { @@ -81,7 +86,7 @@ in }; environmentFile = mkOption { - type = types.nullOr types.path; + type = types.nullOr pathToSecret; default = null; example = "/run/secrets/authentik/authentik-env"; description = '' @@ -105,7 +110,7 @@ in enable = mkEnableOption "authentik LDAP outpost"; environmentFile = mkOption { - type = types.nullOr types.path; + type = types.nullOr pathToSecret; default = null; example = "/run/secrets/authentik-ldap/authentik-ldap-env"; description = '' @@ -128,7 +133,7 @@ in enable = mkEnableOption "authentik RADIUS outpost"; environmentFile = mkOption { - type = types.nullOr types.path; + type = types.nullOr pathToSecret; default = null; example = "/run/secrets/authentik-radius/authentik-radius-env"; description = '' diff --git a/tests/minimal-vmtest.nix b/tests/minimal-vmtest.nix index 0586757..dfb7c52 100644 --- a/tests/minimal-vmtest.nix +++ b/tests/minimal-vmtest.nix @@ -3,12 +3,6 @@ authentik-version, nixosModules, }: -let - # use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile) - authentik-env = pkgs.writeText "authentik-test-secret-env" '' - AUTHENTIK_SECRET_KEY=thissecretwillbeinthenixstore - ''; -in pkgs.nixosTest { name = "authentik"; nodes = { @@ -23,9 +17,18 @@ pkgs.nixosTest { "${pkgs.path}/nixos/tests/common/x11.nix" ]; + # Keep in mind that the secret still ends up in the store and is world-readable because the + # systemd-tmpfiles config lands in the store. + # This is just a trick to not pass a store-path (which is prohibited) to `environmentFile` + # without having to integrate secret managers like agenix or sops-nix into the test. + # Don't do this in production. + systemd.tmpfiles.rules = [ + "f /etc/authentik.env 0700 root root - AUTHENTIK_SECRET_KEY=notastorepath" + ]; + services.authentik = { enable = true; - environmentFile = authentik-env; + environmentFile = "/etc/authentik.env"; nginx = { enable = true; host = "localhost"; diff --git a/tests/override-scope.nix b/tests/override-scope.nix index 96db5ee..02af90a 100644 --- a/tests/override-scope.nix +++ b/tests/override-scope.nix @@ -18,11 +18,6 @@ */ let - # use a root-owned EnvironmentFile in production instead (services.authentik.environmentFile) - authentik-env = pkgs.writeText "authentik-test-secret-env" '' - AUTHENTIK_SECRET_KEY=thissecretwillbeinthenixstore - ''; - customWelcome = "Welcome to custom authentik"; # creates a new scope using python 3.12 for mkPoetryEnv @@ -61,9 +56,18 @@ pkgs.nixosTest { "${pkgs.path}/nixos/tests/common/x11.nix" ]; + # Keep in mind that the secret still ends up in the store and is world-readable because the + # systemd-tmpfiles config lands in the store. + # This is just a trick to not pass a store-path (which is prohibited) to `environmentFile` + # without having to integrate secret managers like agenix or sops-nix into the test. + # Don't do this in production. + systemd.tmpfiles.rules = [ + "f /etc/authentik.env 0700 root root - AUTHENTIK_SECRET_KEY=notastorepath" + ]; + services.authentik = { enable = true; - environmentFile = authentik-env; + environmentFile = "/etc/authentik.env"; nginx = { enable = true; host = "localhost";