From f898e786bfb07399abf4e8173ee525c57eb41984 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Wed, 16 Jul 2025 03:04:44 +0200 Subject: [PATCH 2/3] lldap: add options to set important secrets --- nixos/modules/services/databases/lldap.nix | 64 ++++++++++++++++++++++ nixos/tests/lldap.nix | 16 +++++- 2 files changed, 77 insertions(+), 3 deletions(-) diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix index a9fbe8f7e11a..518b39ba7a86 100644 --- a/nixos/modules/services/databases/lldap.nix +++ b/nixos/modules/services/databases/lldap.nix @@ -37,6 +37,47 @@ in ''; }; + adminPasswordFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to a file containing the default admin password. + ''; + }; + + resetAdminPassword = mkOption { + type = types.nullOr ( + types.oneOf [ + types.bool + (types.enum [ "always" ]) + ] + ); + default = false; + description = '' + Force reset of the admin password. + + Break glass in case of emergency: if you lost the admin password, you + can set this to true to force a reset of the admin password to the value + of `adminPasswordFile`. + + Alternatively, you can set it to `"always"` to reset every time the server starts + which makes for a more declarative configuration. + + The difference between `true` and `"always"` is the former is intended for a one time fix + while the latter is intended for a declarative workflow. In practice, the result + is the same: the password gets reset. The only practical difference is the former + outputs a warning message while the latter outputs an info message. + ''; + }; + + jwtSecretFile = mkOption { + type = types.nullOr types.str; + default = null; + description = '' + Path to a file containing the default admin password. + ''; + }; + settings = mkOption { description = '' Free-form settings written directly to the `lldap_config.toml` file. @@ -108,6 +149,29 @@ in }; config = lib.mkIf cfg.enable { + assertions = [ + { + assertion = cfg.adminPasswordFile == null || cfg.resetAdminPassword != false; + message = '' + The default admin password is set declaratively with `adminPasswordFile` option but the `resetAdminPassword` is set to `false`. + This means the admin password can be changed through the UI and will drift from the one defined in your nix config. + Please set the `resetAdminPassword` option to `true` or `"always"`. + ''; + } + ]; + + services.lldap.environment = { + LLDAP_JWT_SECRET_FILE = lib.mkIf (cfg.jwtSecretFile != null) cfg.jwtSecretFile; + LLDAP_LDAP_USER_PASS_FILE = lib.mkIf (cfg.adminPasswordFile != null) cfg.adminPasswordFile; + LLDAP_FORCE_LDAP_USER_PASS_RESET = + if builtins.isString cfg.resetAdminPassword then + cfg.resetAdminPassword + else if cfg.resetAdminPassword then + "true" + else + "false"; + }; + systemd.services.lldap = { description = "Lightweight LDAP server (lldap)"; wants = [ "network-online.target" ]; diff --git a/nixos/tests/lldap.nix b/nixos/tests/lldap.nix index c2e48525a5f3..e88fa37ab83d 100644 --- a/nixos/tests/lldap.nix +++ b/nixos/tests/lldap.nix @@ -1,4 +1,7 @@ { ... }: +let + adminPassword = "mySecretPassword"; +in { name = "lldap"; @@ -7,6 +10,11 @@ { services.lldap = { enable = true; + + adminPasswordFile = toString (pkgs.writeText "adminPasswordFile" adminPassword); + resetAdminPassword = "always"; + enforceEnsure = true; + settings = { verbose = true; ldap_base_dn = "dc=example,dc=com"; @@ -22,8 +30,10 @@ machine.succeed("curl --location --fail http://localhost:17170/") - print( - machine.succeed('ldapsearch -H ldap://localhost:3890 -D uid=admin,ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w password') - ) + response = machine.fail('ldapsearch -H ldap://localhost:3890 -D uid=admin,ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w password') + print(response) + + response = machine.succeed('ldapsearch -H ldap://localhost:3890 -D uid=admin,ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w ${adminPassword}') + print(response) ''; } -- 2.49.0