197 lines
7.4 KiB
Diff
197 lines
7.4 KiB
Diff
From 3b7d45e30f3dbf329abfb6ba659d10357761e2ab Mon Sep 17 00:00:00 2001
|
|
From: ibizaman <ibizaman@tiserbox.com>
|
|
Date: Wed, 16 Jul 2025 03:04:44 +0200
|
|
Subject: [PATCH 1/4] lldap: add options to set important secrets
|
|
|
|
---
|
|
nixos/modules/services/databases/lldap.nix | 71 +++++++++++++++++++++
|
|
nixos/tests/lldap.nix | 72 +++++++++++++++++++---
|
|
2 files changed, 134 insertions(+), 9 deletions(-)
|
|
|
|
diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix
|
|
index a9fbe8f7e11a..1607754d7d9c 100644
|
|
--- a/nixos/modules/services/databases/lldap.nix
|
|
+++ b/nixos/modules/services/databases/lldap.nix
|
|
@@ -102,12 +102,83 @@ in
|
|
default = "sqlite://./users.db?mode=rwc";
|
|
example = "postgres://postgres-user:password@postgres-server/my-database";
|
|
};
|
|
+
|
|
+ ldap_user_pass_file = mkOption {
|
|
+ type = types.nullOr types.str;
|
|
+ default = null;
|
|
+ description = ''
|
|
+ Path to a file containing the default admin password.
|
|
+
|
|
+ If you want to update the default admin password through this setting,
|
|
+ you must set `force_ldap_user_pass_reset` to `true`.
|
|
+ Otherwise changing this setting will have no effect
|
|
+ unless this is the very first time LLDAP is started and its database is still empty.
|
|
+ '';
|
|
+ };
|
|
+
|
|
+ force_ldap_user_pass_reset = mkOption {
|
|
+ type = types.oneOf [
|
|
+ types.bool
|
|
+ (types.enum [ "always" ])
|
|
+ ];
|
|
+ default = false;
|
|
+ description = ''
|
|
+ Force reset of the admin password.
|
|
+
|
|
+ Set this setting to `"always"` to update the admin password when `ldap_user_pass_file` changes.
|
|
+ Setting to `"always"` also means any password update in the UI will be overwritten next time the service restarts.
|
|
+
|
|
+ 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.
|
|
+ '';
|
|
+ };
|
|
+
|
|
+ jwt_secret_file = mkOption {
|
|
+ type = types.nullOr types.str;
|
|
+ default = null;
|
|
+ description = ''
|
|
+ Path to a file containing the JWT secret.
|
|
+ '';
|
|
+ };
|
|
};
|
|
};
|
|
+
|
|
+ # TOML does not allow null values, so we use null to omit those fields
|
|
+ apply = lib.filterAttrsRecursive (_: v: v != null);
|
|
+ };
|
|
+
|
|
+ silenceForceUserPassResetWarning = mkOption {
|
|
+ type = types.bool;
|
|
+ default = false;
|
|
+ description = ''
|
|
+ Disable warning when the admin password is set declaratively with the `ldap_user_pass_file` setting
|
|
+ but the `force_ldap_user_pass_reset` is set to `false`.
|
|
+
|
|
+ This can lead to the admin password to drift from the one given declaratively.
|
|
+ If that is okay for you and you want to silence the warning, set this option to `true`.
|
|
+ '';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable {
|
|
+ warnings =
|
|
+ lib.optionals
|
|
+ (
|
|
+ (cfg.settings.ldap_user_pass_file or null) != null
|
|
+ && cfg.settings.force_ldap_user_pass_reset == false
|
|
+ && cfg.silenceForceUserPassResetWarning == false
|
|
+ )
|
|
+ [
|
|
+ ''
|
|
+ lldap: The default admin password is declared with the setting `ldap_user_pass_file`, but `force_ldap_user_pass_reset` 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.
|
|
+ It also means changing the setting `ldap_user_pass_file` will have no effect on the admin password.
|
|
+ Either set `force_ldap_user_pass_reset` to `"always"` or silence this warning by setting the option `services.lldap.silenceForceUserPassResetWarning` to `true`.
|
|
+ ''
|
|
+ ];
|
|
+
|
|
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..aea6b2058727 100644
|
|
--- a/nixos/tests/lldap.nix
|
|
+++ b/nixos/tests/lldap.nix
|
|
@@ -1,4 +1,7 @@
|
|
{ ... }:
|
|
+let
|
|
+ adminPassword = "mySecretPassword";
|
|
+in
|
|
{
|
|
name = "lldap";
|
|
|
|
@@ -7,23 +10,74 @@
|
|
{
|
|
services.lldap = {
|
|
enable = true;
|
|
+
|
|
settings = {
|
|
verbose = true;
|
|
ldap_base_dn = "dc=example,dc=com";
|
|
};
|
|
};
|
|
environment.systemPackages = [ pkgs.openldap ];
|
|
+
|
|
+ specialisation = {
|
|
+ differentAdminPassword.configuration =
|
|
+ { ... }:
|
|
+ {
|
|
+ services.lldap.settings = {
|
|
+ ldap_user_pass_file = toString (pkgs.writeText "adminPasswordFile" adminPassword);
|
|
+ force_ldap_user_pass_reset = "always";
|
|
+ };
|
|
+ };
|
|
+
|
|
+ changeAdminPassword.configuration =
|
|
+ { ... }:
|
|
+ {
|
|
+ services.lldap.settings = {
|
|
+ ldap_user_pass_file = toString (pkgs.writeText "adminPasswordFile" "password");
|
|
+ force_ldap_user_pass_reset = false;
|
|
+ };
|
|
+ };
|
|
+ };
|
|
};
|
|
|
|
- testScript = ''
|
|
- machine.wait_for_unit("lldap.service")
|
|
- machine.wait_for_open_port(3890)
|
|
- machine.wait_for_open_port(17170)
|
|
+ testScript =
|
|
+ { nodes, ... }:
|
|
+ let
|
|
+ specializations = "${nodes.machine.system.build.toplevel}/specialisation";
|
|
+ in
|
|
+ ''
|
|
+ machine.wait_for_unit("lldap.service")
|
|
+ machine.wait_for_open_port(3890)
|
|
+ machine.wait_for_open_port(17170)
|
|
+
|
|
+ machine.succeed("curl --location --fail http://localhost:17170/")
|
|
+
|
|
+ adminPassword="${adminPassword}"
|
|
+
|
|
+ def try_login(user, password, expect_success=True):
|
|
+ cmd = f'ldapsearch -H ldap://localhost:3890 -D uid={user},ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w {password}'
|
|
+ code, response = machine.execute(cmd)
|
|
+ print(cmd)
|
|
+ print(response)
|
|
+ if expect_success:
|
|
+ if code != 0:
|
|
+ raise Exception(f"Expected success, had failure {code}")
|
|
+ else:
|
|
+ if code == 0:
|
|
+ raise Exception("Expected failure, had success")
|
|
+ return response
|
|
+
|
|
+ with subtest("default admin password"):
|
|
+ try_login("admin", "password", expect_success=True)
|
|
+ try_login("admin", adminPassword, expect_success=False)
|
|
|
|
- machine.succeed("curl --location --fail http://localhost:17170/")
|
|
+ with subtest("different admin password"):
|
|
+ machine.succeed('${specializations}/differentAdminPassword/bin/switch-to-configuration test')
|
|
+ try_login("admin", "password", expect_success=False)
|
|
+ try_login("admin", adminPassword, expect_success=True)
|
|
|
|
- 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')
|
|
- )
|
|
- '';
|
|
+ with subtest("change admin password has no effect"):
|
|
+ machine.succeed('${specializations}/differentAdminPassword/bin/switch-to-configuration test')
|
|
+ try_login("admin", "password", expect_success=False)
|
|
+ try_login("admin", adminPassword, expect_success=True)
|
|
+ '';
|
|
}
|
|
--
|
|
2.49.0
|
|
|