update lldap patches
This commit is contained in:
parent
b50f45e17e
commit
12f2b4f49e
6 changed files with 573 additions and 267 deletions
|
|
@ -16,9 +16,9 @@
|
||||||
originPkgs = nixpkgs.legacyPackages.${system};
|
originPkgs = nixpkgs.legacyPackages.${system};
|
||||||
shbPatches = originPkgs.lib.optionals (system == "x86_64-linux") [
|
shbPatches = originPkgs.lib.optionals (system == "x86_64-linux") [
|
||||||
# Get rid of lldap patches when https://github.com/NixOS/nixpkgs/pull/425923 is merged.
|
# Get rid of lldap patches when https://github.com/NixOS/nixpkgs/pull/425923 is merged.
|
||||||
./patches/0001-lldap-lldap-0.6.1-unstable-2025-07-16.patch
|
./patches/0001-lldap-add-options-to-set-important-secrets.patch
|
||||||
./patches/0002-lldap-add-options-to-set-important-secrets.patch
|
./patches/0002-lldap-lldap-0.6.1-unstable-2025-07-16.patch
|
||||||
./patches/0003-lldap-bootstrap-init-unstable-2025-07-16-lldap-add-e.patch
|
./patches/0003-lldap-bootstrap-init-unstable-2025-07-17-lldap-add-e.patch
|
||||||
|
|
||||||
# Leaving commented out as an example.
|
# Leaving commented out as an example.
|
||||||
# (originPkgs.fetchpatch {
|
# (originPkgs.fetchpatch {
|
||||||
|
|
|
||||||
|
|
@ -336,9 +336,6 @@ in
|
||||||
services.lldap = {
|
services.lldap = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
|
||||||
adminPasswordFile = toString cfg.ldapUserPassword.result.path;
|
|
||||||
jwtSecretFile = toString cfg.jwtSecret.result.path;
|
|
||||||
resetAdminPassword = "always";
|
|
||||||
enforceEnsure = true;
|
enforceEnsure = true;
|
||||||
|
|
||||||
environment = {
|
environment = {
|
||||||
|
|
@ -352,9 +349,12 @@ in
|
||||||
|
|
||||||
ldap_host = "127.0.0.1";
|
ldap_host = "127.0.0.1";
|
||||||
ldap_port = cfg.ldapPort;
|
ldap_port = cfg.ldapPort;
|
||||||
|
|
||||||
ldap_base_dn = cfg.dcdomain;
|
ldap_base_dn = cfg.dcdomain;
|
||||||
|
|
||||||
|
ldap_user_pass_file = toString cfg.ldapUserPassword.result.path;
|
||||||
|
force_ldap_user_pass_reset = "always";
|
||||||
|
jwt_secret_file = toString cfg.jwtSecret.result.path;
|
||||||
|
|
||||||
verbose = cfg.debug;
|
verbose = cfg.debug;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
197
patches/0001-lldap-add-options-to-set-important-secrets.patch
Normal file
197
patches/0001-lldap-add-options-to-set-important-secrets.patch
Normal file
|
|
@ -0,0 +1,197 @@
|
||||||
|
From d7b92a627d2c5248cf3934f6b94bc57885a208a4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: ibizaman <ibizaman@tiserbox.com>
|
||||||
|
Date: Wed, 16 Jul 2025 03:04:44 +0200
|
||||||
|
Subject: [PATCH 1/3] 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
|
||||||
|
|
||||||
|
|
@ -1,133 +0,0 @@
|
||||||
From f898e786bfb07399abf4e8173ee525c57eb41984 Mon Sep 17 00:00:00 2001
|
|
||||||
From: ibizaman <ibizaman@tiserbox.com>
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
@ -1,14 +1,127 @@
|
||||||
From 5bc3588204e71a77d7e0bfccc4cb70e6ccfbb54b Mon Sep 17 00:00:00 2001
|
From ac7fcd01a20cfd93e2ac2b558f38bfa500e5f28a Mon Sep 17 00:00:00 2001
|
||||||
From: ibizaman <ibizaman@tiserbox.com>
|
From: ibizaman <ibizaman@tiserbox.com>
|
||||||
Date: Tue, 15 Jul 2025 18:42:42 +0200
|
Date: Tue, 15 Jul 2025 18:42:42 +0200
|
||||||
Subject: [PATCH 1/3] lldap: lldap 0.6.1 -> unstable-2025-07-16
|
Subject: [PATCH 2/3] lldap: lldap 0.6.1 -> unstable-2025-07-16
|
||||||
|
|
||||||
---
|
---
|
||||||
|
nixos/modules/services/databases/lldap.nix | 43 ++++++++++---
|
||||||
|
nixos/tests/lldap.nix | 8 ++-
|
||||||
.../0001-parameterize-frontend-location.patch | 64 -------------------
|
.../0001-parameterize-frontend-location.patch | 64 -------------------
|
||||||
pkgs/by-name/ll/lldap/package.nix | 30 +++++----
|
pkgs/by-name/ll/lldap/package.nix | 30 +++++----
|
||||||
2 files changed, 16 insertions(+), 78 deletions(-)
|
4 files changed, 55 insertions(+), 90 deletions(-)
|
||||||
delete mode 100644 pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch
|
delete mode 100644 pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch
|
||||||
|
|
||||||
|
diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix
|
||||||
|
index 1607754d7d9c..1095021b3f35 100644
|
||||||
|
--- a/nixos/modules/services/databases/lldap.nix
|
||||||
|
+++ b/nixos/modules/services/databases/lldap.nix
|
||||||
|
@@ -2,7 +2,6 @@
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
- utils,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
|
||||||
|
@@ -103,6 +102,16 @@ in
|
||||||
|
example = "postgres://postgres-user:password@postgres-server/my-database";
|
||||||
|
};
|
||||||
|
|
||||||
|
+ ldap_user_pass = mkOption {
|
||||||
|
+ type = types.nullOr types.str;
|
||||||
|
+ default = null;
|
||||||
|
+ description = ''
|
||||||
|
+ Password for default admin password.
|
||||||
|
+
|
||||||
|
+ Unsecure: Use `ldap_user_pass_file` settings instead.
|
||||||
|
+ '';
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
ldap_user_pass_file = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
@@ -163,18 +172,32 @@ in
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
+ assertions = [
|
||||||
|
+ {
|
||||||
|
+ assertion =
|
||||||
|
+ (cfg.settings.ldap_user_pass_file or null) != null || (cfg.settings.ldap_user_pass or null) != null;
|
||||||
|
+ message = "lldap: Default admin user password must be set. Please set the `ldap_user_pass` or better the `ldap_user_pass_file` setting.";
|
||||||
|
+ }
|
||||||
|
+ {
|
||||||
|
+ assertion =
|
||||||
|
+ (cfg.settings.ldap_user_pass_file or null) == null || (cfg.settings.ldap_user_pass or null) == null;
|
||||||
|
+ message = "lldap: Both `ldap_user_pass` and `ldap_user_pass_file` settings should not be set at the same time. Set one to `null`.";
|
||||||
|
+ }
|
||||||
|
+ ];
|
||||||
|
+
|
||||||
|
warnings =
|
||||||
|
- lib.optionals
|
||||||
|
- (
|
||||||
|
- (cfg.settings.ldap_user_pass_file or null) != null
|
||||||
|
- && cfg.settings.force_ldap_user_pass_reset == false
|
||||||
|
- && cfg.silenceForceUserPassResetWarning == false
|
||||||
|
- )
|
||||||
|
+ lib.optionals (cfg.settings.ldap_user_pass or null != null) [
|
||||||
|
+ ''
|
||||||
|
+ lldap: Unsecure `ldap_user_pass` setting is used. Prefer `ldap_user_pass_file` instead.
|
||||||
|
+ ''
|
||||||
|
+ ]
|
||||||
|
+ ++ lib.optionals
|
||||||
|
+ (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.
|
||||||
|
+ lldap: The `force_ldap_user_pass_reset` setting is set to `false` which 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` or `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`.
|
||||||
|
''
|
||||||
|
];
|
||||||
|
diff --git a/nixos/tests/lldap.nix b/nixos/tests/lldap.nix
|
||||||
|
index aea6b2058727..8e38d4bdefa3 100644
|
||||||
|
--- a/nixos/tests/lldap.nix
|
||||||
|
+++ b/nixos/tests/lldap.nix
|
||||||
|
@@ -6,7 +6,7 @@ in
|
||||||
|
name = "lldap";
|
||||||
|
|
||||||
|
nodes.machine =
|
||||||
|
- { pkgs, ... }:
|
||||||
|
+ { pkgs, lib, ... }:
|
||||||
|
{
|
||||||
|
services.lldap = {
|
||||||
|
enable = true;
|
||||||
|
@@ -14,6 +14,8 @@ in
|
||||||
|
settings = {
|
||||||
|
verbose = true;
|
||||||
|
ldap_base_dn = "dc=example,dc=com";
|
||||||
|
+
|
||||||
|
+ ldap_user_pass = "password";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
environment.systemPackages = [ pkgs.openldap ];
|
||||||
|
@@ -23,7 +25,8 @@ in
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.lldap.settings = {
|
||||||
|
- ldap_user_pass_file = toString (pkgs.writeText "adminPasswordFile" adminPassword);
|
||||||
|
+ ldap_user_pass = lib.mkForce null;
|
||||||
|
+ ldap_user_pass_file = lib.mkForce (toString (pkgs.writeText "adminPasswordFile" adminPassword));
|
||||||
|
force_ldap_user_pass_reset = "always";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@@ -32,6 +35,7 @@ in
|
||||||
|
{ ... }:
|
||||||
|
{
|
||||||
|
services.lldap.settings = {
|
||||||
|
+ ldap_user_pass = lib.mkForce null;
|
||||||
|
ldap_user_pass_file = toString (pkgs.writeText "adminPasswordFile" "password");
|
||||||
|
force_ldap_user_pass_reset = false;
|
||||||
|
};
|
||||||
diff --git a/pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch b/pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch
|
diff --git a/pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch b/pkgs/by-name/ll/lldap/0001-parameterize-frontend-location.patch
|
||||||
deleted file mode 100644
|
deleted file mode 100644
|
||||||
index c33f5a7afa10..000000000000
|
index c33f5a7afa10..000000000000
|
||||||
|
|
@ -1,28 +1,22 @@
|
||||||
From 75b9437558a22eabff74339569f98b567f0a0d04 Mon Sep 17 00:00:00 2001
|
From 7f30b1ad931cf9af3a1fa3caded057f9b71672bc Mon Sep 17 00:00:00 2001
|
||||||
From: ibizaman <ibizaman@tiserbox.com>
|
From: ibizaman <ibizaman@tiserbox.com>
|
||||||
Date: Wed, 16 Jul 2025 17:51:57 +0200
|
Date: Wed, 16 Jul 2025 17:51:57 +0200
|
||||||
Subject: [PATCH 3/3] lldap-bootstrap: init unstable-2025-07-17, lldap: add
|
Subject: [PATCH 3/3] lldap-bootstrap: init unstable-2025-07-17, lldap: add
|
||||||
ensure options
|
ensure options
|
||||||
|
|
||||||
---
|
---
|
||||||
nixos/modules/services/databases/lldap.nix | 282 +++++++++++++++++++-
|
nixos/modules/services/databases/lldap.nix | 339 +++++++++++++++++++-
|
||||||
nixos/tests/lldap.nix | 116 +++++++-
|
nixos/tests/lldap.nix | 137 +++++++-
|
||||||
pkgs/by-name/ll/lldap-bootstrap/package.nix | 54 ++++
|
pkgs/by-name/ll/lldap-bootstrap/package.nix | 54 ++++
|
||||||
3 files changed, 446 insertions(+), 6 deletions(-)
|
pkgs/by-name/ll/lldap/package.nix | 6 +-
|
||||||
|
4 files changed, 525 insertions(+), 11 deletions(-)
|
||||||
create mode 100644 pkgs/by-name/ll/lldap-bootstrap/package.nix
|
create mode 100644 pkgs/by-name/ll/lldap-bootstrap/package.nix
|
||||||
|
|
||||||
diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix
|
diff --git a/nixos/modules/services/databases/lldap.nix b/nixos/modules/services/databases/lldap.nix
|
||||||
index 518b39ba7a86..b7c4c85f751f 100644
|
index 1095021b3f35..b23e89ba6794 100644
|
||||||
--- a/nixos/modules/services/databases/lldap.nix
|
--- a/nixos/modules/services/databases/lldap.nix
|
||||||
+++ b/nixos/modules/services/databases/lldap.nix
|
+++ b/nixos/modules/services/databases/lldap.nix
|
||||||
@@ -2,13 +2,82 @@
|
@@ -8,6 +8,84 @@
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
- utils,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
|
|
||||||
let
|
let
|
||||||
cfg = config.services.lldap;
|
cfg = config.services.lldap;
|
||||||
format = pkgs.formats.toml { };
|
format = pkgs.formats.toml { };
|
||||||
|
|
@ -31,7 +25,13 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
+
|
+
|
||||||
+ ensureFormat = pkgs.formats.json { };
|
+ ensureFormat = pkgs.formats.json { };
|
||||||
+ ensureGenerate =
|
+ ensureGenerate =
|
||||||
+ name: source: ensureFormat.generate name (lib.filterAttrsRecursive (n: v: v != null) source);
|
+ let
|
||||||
|
+ filterNulls = lib.filterAttrsRecursive (n: v: v != null);
|
||||||
|
+
|
||||||
|
+ filteredSource =
|
||||||
|
+ source: if builtins.isList source then map filterNulls source else filterNulls source;
|
||||||
|
+ in
|
||||||
|
+ name: source: ensureFormat.generate name (filteredSource source);
|
||||||
+
|
+
|
||||||
+ ensureFieldsOptions = name: {
|
+ ensureFieldsOptions = name: {
|
||||||
+ name = mkOption {
|
+ name = mkOption {
|
||||||
|
|
@ -96,10 +96,12 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
+ paths = lib.mapAttrsToList genOne source;
|
+ paths = lib.mapAttrsToList genOne source;
|
||||||
+ }
|
+ }
|
||||||
+ }/configs";
|
+ }/configs";
|
||||||
|
+
|
||||||
|
+ quoteVariable = x: "\"${x}\"";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.lldap = with lib; {
|
options.services.lldap = with lib; {
|
||||||
@@ -16,6 +85,8 @@ in
|
@@ -15,6 +93,8 @@ in
|
||||||
|
|
||||||
package = mkPackageOption pkgs "lldap" { };
|
package = mkPackageOption pkgs "lldap" { };
|
||||||
|
|
||||||
|
|
@ -108,9 +110,9 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
environment = mkOption {
|
environment = mkOption {
|
||||||
type = with types; attrsOf str;
|
type = with types; attrsOf str;
|
||||||
default = { };
|
default = { };
|
||||||
@@ -146,6 +217,172 @@ in
|
@@ -169,6 +249,186 @@ in
|
||||||
};
|
If that is okay for you and you want to silence the warning, set this option to `true`.
|
||||||
};
|
'';
|
||||||
};
|
};
|
||||||
+
|
+
|
||||||
+ ensureUsers = mkOption {
|
+ ensureUsers = mkOption {
|
||||||
|
|
@ -254,17 +256,31 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
+ type = types.str;
|
+ type = types.str;
|
||||||
+ default = "admin";
|
+ default = "admin";
|
||||||
+ description = ''
|
+ description = ''
|
||||||
+ Username of an admin user with which to connect to the LLDAP service.
|
+ Username of the default admin user with which to connect to the LLDAP service.
|
||||||
+
|
+
|
||||||
+ By default, it is the default admin username `admin`.
|
+ By default, it is `"admin"`.
|
||||||
+ If using another user, it must be managed manually.
|
+ Extra admin users can be added using the `services.lldap.ensureUsers` option and adding them to the correct groups.
|
||||||
|
+ '';
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ ensureAdminPassword = mkOption {
|
||||||
|
+ type = types.nullOr types.str;
|
||||||
|
+ defaultText = "config.services.lldap.settings.ldap_user_pass";
|
||||||
|
+ default = cfg.settings.ldap_user_pass or null;
|
||||||
|
+ description = ''
|
||||||
|
+ Password of an admin user with which to connect to the LLDAP service.
|
||||||
|
+
|
||||||
|
+ By default, it is the same as the password for the default admin user 'admin'.
|
||||||
|
+ If using a password from another user, it must be managed manually.
|
||||||
|
+
|
||||||
|
+ Unsecure. Use `services.lldap.ensureAdminPasswordFile` option instead.
|
||||||
+ '';
|
+ '';
|
||||||
+ };
|
+ };
|
||||||
+
|
+
|
||||||
+ ensureAdminPasswordFile = mkOption {
|
+ ensureAdminPasswordFile = mkOption {
|
||||||
+ type = types.nullOr types.str;
|
+ type = types.nullOr types.str;
|
||||||
+ defaultText = "config.services.lldap.adminPasswordFile";
|
+ defaultText = "config.services.lldap.settings.ldap_user_pass_file";
|
||||||
+ default = cfg.adminPasswordFile;
|
+ default = cfg.settings.ldap_user_pass_file or null;
|
||||||
+ description = ''
|
+ description = ''
|
||||||
+ Path to the file containing the password of an admin user with which to connect to the LLDAP service.
|
+ Path to the file containing the password of an admin user with which to connect to the LLDAP service.
|
||||||
+
|
+
|
||||||
|
|
@ -281,9 +297,9 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
@@ -158,8 +395,40 @@ in
|
@@ -183,15 +443,58 @@ in
|
||||||
Please set the `resetAdminPassword` option to `true` or `"always"`.
|
(cfg.settings.ldap_user_pass_file or null) == null || (cfg.settings.ldap_user_pass or null) == null;
|
||||||
'';
|
message = "lldap: Both `ldap_user_pass` and `ldap_user_pass_file` settings should not be set at the same time. Set one to `null`.";
|
||||||
}
|
}
|
||||||
+ {
|
+ {
|
||||||
+ assertion =
|
+ assertion =
|
||||||
|
|
@ -292,48 +308,95 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
+ || cfg.ensureUserFields != { }
|
+ || cfg.ensureUserFields != { }
|
||||||
+ || cfg.ensureGroupFields != { }
|
+ || cfg.ensureGroupFields != { }
|
||||||
+ || cfg.enforceEnsure
|
+ || cfg.enforceEnsure
|
||||||
+ -> cfg.ensureAdminPasswordFile != null;
|
+ -> cfg.ensureAdminPassword != null || cfg.ensureAdminPasswordFile != null;
|
||||||
+ message = ''
|
+ message = ''
|
||||||
+ Some ensure options are set but no admin user password is set.
|
+ lldap: Some ensure options are set but no admin user password is set.
|
||||||
+ Add a default password to `adminPasswordFile` to manage the admin user declaratively
|
+ Add a default password to the `ldap_user_pass` or `ldap_user_pass_file` setting and set `force_ldap_user_pass_reset` to `true` to manage the admin user declaratively
|
||||||
+ or create a user manually and set its password in `ensureAdminPasswordFile`.
|
+ or create an admin user manually and set its password in `ensureAdminPasswordFile` option.
|
||||||
+ '';
|
+ '';
|
||||||
+ }
|
+ }
|
||||||
+ {
|
+ {
|
||||||
+ assertion = cfg.enforceEnsure -> !someUsersBelongToNonEnsuredGroup;
|
+ assertion = cfg.enforceEnsure -> !someUsersBelongToNonEnsuredGroup;
|
||||||
+ message = ''
|
+ message = ''
|
||||||
+ Some users belong to groups not present in the ensureGroups attr,
|
+ lldap: Some users belong to groups not present in the ensureGroups attr,
|
||||||
+ add the following groups or remove them from the groups a user belong to:
|
+ add the following groups or remove them from the groups a user belong to:
|
||||||
+ ${lib.concatStringsSep ", " (map (x: "\"${x}\"") userGroupNotInEnsuredGroup)}
|
+ ${lib.concatMapStringsSep quoteVariable ", " userGroupNotInEnsuredGroup}
|
||||||
+ '';
|
+ '';
|
||||||
+ }
|
+ }
|
||||||
|
+ (
|
||||||
|
+ let
|
||||||
|
+ getNames = source: lib.flatten (lib.mapAttrsToList (x: v: v.name) source);
|
||||||
|
+ allNames = getNames cfg.ensureUserFields ++ getNames cfg.ensureGroupFields;
|
||||||
|
+ validFieldName = name: lib.match "[a-zA-Z0-9-]+" name != null;
|
||||||
|
+ in
|
||||||
|
+ {
|
||||||
|
+ assertion = lib.all validFieldName allNames;
|
||||||
|
+ message = ''
|
||||||
|
+ lldap: The following custom user or group fields have invalid names. Valid characters are: a-z, A-Z, 0-9, and dash (-).
|
||||||
|
+ The offending fields are: ${
|
||||||
|
+ lib.concatMapStringsSep quoteVariable ", " (lib.filter (x: !(validFieldName x)) allNames)
|
||||||
|
+ }
|
||||||
|
+ '';
|
||||||
|
+ }
|
||||||
|
+ )
|
||||||
];
|
];
|
||||||
|
|
||||||
+ warnings = (
|
warnings =
|
||||||
+ lib.optionals (!cfg.enforceEnsure && (lib.debug.traceValSeq someUsersBelongToNonEnsuredGroup)) [
|
- lib.optionals (cfg.settings.ldap_user_pass or null != null) [
|
||||||
|
+ (lib.optionals (cfg.ensureAdminPassword != null) [
|
||||||
|
+ ''
|
||||||
|
+ lldap: Unsecure option `ensureAdminPassword` is used. Prefer `ensureAdminPasswordFile` instead.
|
||||||
|
+ ''
|
||||||
|
+ ])
|
||||||
|
+ ++ (lib.optionals (cfg.settings.ldap_user_pass or null != null) [
|
||||||
|
''
|
||||||
|
lldap: Unsecure `ldap_user_pass` setting is used. Prefer `ldap_user_pass_file` instead.
|
||||||
|
''
|
||||||
|
- ]
|
||||||
|
- ++ lib.optionals
|
||||||
|
+ ])
|
||||||
|
+ ++ (lib.optionals
|
||||||
|
(cfg.settings.force_ldap_user_pass_reset == false && cfg.silenceForceUserPassResetWarning == false)
|
||||||
|
[
|
||||||
|
''
|
||||||
|
@@ -200,7 +503,15 @@ in
|
||||||
|
It also means changing the setting `ldap_user_pass` or `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`.
|
||||||
|
''
|
||||||
|
- ];
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
+ ++ (lib.optionals (!cfg.enforceEnsure && someUsersBelongToNonEnsuredGroup) [
|
||||||
+ ''
|
+ ''
|
||||||
+ Some users belong to groups not managed by the configuration here,
|
+ Some users belong to groups not managed by the configuration here,
|
||||||
+ make sure the following groups exist or the service will not start properly:
|
+ make sure the following groups exist or the service will not start properly:
|
||||||
+ ${lib.concatStringsSep ", " (map (x: "\"${x}\"") userGroupNotInEnsuredGroup)}
|
+ ${lib.concatStringsSep ", " (map (x: "\"${x}\"") userGroupNotInEnsuredGroup)}
|
||||||
+ ''
|
+ ''
|
||||||
+ ]
|
+ ]);
|
||||||
+ );
|
|
||||||
+
|
systemd.services.lldap = {
|
||||||
services.lldap.environment = {
|
description = "Lightweight LDAP server (lldap)";
|
||||||
LLDAP_JWT_SECRET_FILE = lib.mkIf (cfg.jwtSecretFile != null) cfg.jwtSecretFile;
|
@@ -223,6 +534,26 @@ in
|
||||||
LLDAP_LDAP_USER_PASS_FILE = lib.mkIf (cfg.adminPasswordFile != null) cfg.adminPasswordFile;
|
|
||||||
@@ -193,6 +462,17 @@ in
|
|
||||||
+ ''
|
+ ''
|
||||||
${lib.getExe cfg.package} run --config-file ${format.generate "lldap_config.toml" cfg.settings}
|
${lib.getExe cfg.package} run --config-file ${format.generate "lldap_config.toml" cfg.settings}
|
||||||
'';
|
'';
|
||||||
+ postStart = ''
|
+ postStart = ''
|
||||||
+ export LLDAP_URL=http://127.0.0.1:${toString cfg.settings.http_port}
|
+ export LLDAP_URL=http://127.0.0.1:${toString cfg.settings.http_port}
|
||||||
+ export LLDAP_ADMIN_USERNAME=${cfg.ensureAdminUsername}
|
+ export LLDAP_ADMIN_USERNAME=${cfg.ensureAdminUsername}
|
||||||
+ export LLDAP_ADMIN_PASSWORD_FILE=${cfg.ensureAdminPasswordFile}
|
+ export LLDAP_ADMIN_PASSWORD=${
|
||||||
+ export USER_CONFIGS_DIR=${generateEnsureConfigDir "users" cfg.ensureUsers}
|
+ if cfg.ensureAdminPassword != null then cfg.ensureAdminPassword else ""
|
||||||
|
+ }
|
||||||
|
+ export LLDAP_ADMIN_PASSWORD_FILE=${
|
||||||
|
+ if cfg.ensureAdminPasswordFile != null then cfg.ensureAdminPasswordFile else ""
|
||||||
|
+ }
|
||||||
|
+ export USER_CONFIGS_DIR=${lib.traceVal (generateEnsureConfigDir "users" cfg.ensureUsers)}
|
||||||
+ export GROUP_CONFIGS_DIR=${generateEnsureConfigDir "groups" cfg.ensureGroups}
|
+ export GROUP_CONFIGS_DIR=${generateEnsureConfigDir "groups" cfg.ensureGroups}
|
||||||
+ export USER_SCHEMAS_DIR=${generateEnsureConfigDir "userFields" cfg.ensureUserFields}
|
+ export USER_SCHEMAS_DIR=${
|
||||||
+ export GROUP_SCHEMAS_DIR=${generateEnsureConfigDir "groupFields" cfg.ensureGroupFields}
|
+ generateEnsureConfigDir "userFields" (lib.mapAttrs (n: v: [ v ]) cfg.ensureUserFields)
|
||||||
|
+ }
|
||||||
|
+ export GROUP_SCHEMAS_DIR=${
|
||||||
|
+ generateEnsureConfigDir "groupFields" (lib.mapAttrs (n: v: [ v ]) cfg.ensureGroupFields)
|
||||||
|
+ }
|
||||||
+ export DO_CLEANUP=${if cfg.enforceEnsure then "true" else "false"}
|
+ export DO_CLEANUP=${if cfg.enforceEnsure then "true" else "false"}
|
||||||
+ ${lib.getExe cfg.bootstrap-package}
|
+ ${lib.getExe cfg.bootstrap-package}
|
||||||
+ '';
|
+ '';
|
||||||
|
|
@ -341,42 +404,42 @@ index 518b39ba7a86..b7c4c85f751f 100644
|
||||||
StateDirectory = "lldap";
|
StateDirectory = "lldap";
|
||||||
StateDirectoryMode = "0750";
|
StateDirectoryMode = "0750";
|
||||||
diff --git a/nixos/tests/lldap.nix b/nixos/tests/lldap.nix
|
diff --git a/nixos/tests/lldap.nix b/nixos/tests/lldap.nix
|
||||||
index e88fa37ab83d..7d2e65699a0f 100644
|
index 8e38d4bdefa3..e0bc2fdf07fd 100644
|
||||||
--- a/nixos/tests/lldap.nix
|
--- a/nixos/tests/lldap.nix
|
||||||
+++ b/nixos/tests/lldap.nix
|
+++ b/nixos/tests/lldap.nix
|
||||||
@@ -1,6 +1,8 @@
|
@@ -1,6 +1,9 @@
|
||||||
{ ... }:
|
{ ... }:
|
||||||
let
|
let
|
||||||
adminPassword = "mySecretPassword";
|
adminPassword = "mySecretPassword";
|
||||||
+ alicePassword = "AlicePassword";
|
+ alicePassword = "AlicePassword";
|
||||||
+ bobPassword = "BobPassword";
|
+ bobPassword = "BobPassword";
|
||||||
|
+ charliePassword = "CharliePassword";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
name = "lldap";
|
name = "lldap";
|
||||||
@@ -19,21 +21,125 @@ in
|
@@ -26,7 +29,7 @@ in
|
||||||
verbose = true;
|
{
|
||||||
ldap_base_dn = "dc=example,dc=com";
|
services.lldap.settings = {
|
||||||
};
|
ldap_user_pass = lib.mkForce null;
|
||||||
|
- ldap_user_pass_file = lib.mkForce (toString (pkgs.writeText "adminPasswordFile" adminPassword));
|
||||||
|
+ ldap_user_pass_file = toString (pkgs.writeText "adminPasswordFile" adminPassword);
|
||||||
|
force_ldap_user_pass_reset = "always";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@@ -40,13 +43,104 @@ in
|
||||||
|
force_ldap_user_pass_reset = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
+
|
+
|
||||||
+ ensureUsers = {
|
|
||||||
+ alice = {
|
|
||||||
+ email = "alice@example.com";
|
|
||||||
+ password_file = toString (pkgs.writeText "alicePasswordFile" alicePassword);
|
|
||||||
+ groups = [ "mygroup" ];
|
|
||||||
+ };
|
|
||||||
+ };
|
|
||||||
+
|
|
||||||
+ ensureGroups = {
|
|
||||||
+ mygroup = { };
|
|
||||||
+ };
|
|
||||||
};
|
|
||||||
environment.systemPackages = [ pkgs.openldap ];
|
|
||||||
+
|
|
||||||
+ specialisation = {
|
|
||||||
+ withAlice.configuration =
|
+ withAlice.configuration =
|
||||||
+ { ... }:
|
+ { ... }:
|
||||||
+ {
|
+ {
|
||||||
+ services.lldap = {
|
+ services.lldap = {
|
||||||
|
+ enforceEnsure = true;
|
||||||
|
+
|
||||||
|
+ # This password was set in the "differentAdminPassword" specialisation.
|
||||||
|
+ ensureAdminPasswordFile = toString (pkgs.writeText "adminPasswordFile" adminPassword);
|
||||||
|
+
|
||||||
+ ensureUsers = {
|
+ ensureUsers = {
|
||||||
+ alice = {
|
+ alice = {
|
||||||
+ email = "alice@example.com";
|
+ email = "alice@example.com";
|
||||||
|
|
@ -395,12 +458,46 @@ index e88fa37ab83d..7d2e65699a0f 100644
|
||||||
+ { ... }:
|
+ { ... }:
|
||||||
+ {
|
+ {
|
||||||
+ services.lldap = {
|
+ services.lldap = {
|
||||||
|
+ enforceEnsure = true;
|
||||||
|
+
|
||||||
|
+ # This time we check that ensureAdminPasswordFile correctly defaults to `settings.ldap_user_pass_file`
|
||||||
|
+ settings = {
|
||||||
|
+ ldap_user_pass = lib.mkForce "password";
|
||||||
|
+ force_ldap_user_pass_reset = "always";
|
||||||
|
+ };
|
||||||
|
+
|
||||||
+ ensureUsers = {
|
+ ensureUsers = {
|
||||||
+ bob = {
|
+ bob = {
|
||||||
+ email = "bob@example.com";
|
+ email = "bob@example.com";
|
||||||
+ password_file = toString (pkgs.writeText "bobPasswordFile" bobPassword);
|
+ password_file = toString (pkgs.writeText "bobPasswordFile" bobPassword);
|
||||||
+ groups = [ "othergroup" ];
|
+ groups = [ "bobgroup" ];
|
||||||
+ displayName = "Bob";
|
+ displayName = "Bob";
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ ensureGroups = {
|
||||||
|
+ bobgroup = { };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ withAttributes.configuration =
|
||||||
|
+ { ... }:
|
||||||
|
+ {
|
||||||
|
+ services.lldap = {
|
||||||
|
+ enforceEnsure = true;
|
||||||
|
+
|
||||||
|
+ settings = {
|
||||||
|
+ ldap_user_pass = lib.mkForce adminPassword;
|
||||||
|
+ force_ldap_user_pass_reset = "always";
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ ensureUsers = {
|
||||||
|
+ charlie = {
|
||||||
|
+ email = "charlie@example.com";
|
||||||
|
+ password_file = toString (pkgs.writeText "charliePasswordFile" charliePassword);
|
||||||
|
+ groups = [ "othergroup" ];
|
||||||
|
+ displayName = "Charlie";
|
||||||
+ myattribute = 2;
|
+ myattribute = 2;
|
||||||
+ };
|
+ };
|
||||||
+ };
|
+ };
|
||||||
|
|
@ -424,69 +521,83 @@ index e88fa37ab83d..7d2e65699a0f 100644
|
||||||
+ };
|
+ };
|
||||||
+ };
|
+ };
|
||||||
+ };
|
+ };
|
||||||
+ };
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
- testScript = ''
|
testScript =
|
||||||
+ testScript =
|
{ nodes, ... }:
|
||||||
+ { nodes, ... }:
|
let
|
||||||
+ let
|
- specializations = "${nodes.machine.system.build.toplevel}/specialisation";
|
||||||
+ specializations = "${nodes.machine.system.build.toplevel}/specialisation";
|
+ specialisations = "${nodes.machine.system.build.toplevel}/specialisation";
|
||||||
+ in
|
in
|
||||||
+ ''
|
''
|
||||||
machine.wait_for_unit("lldap.service")
|
machine.wait_for_unit("lldap.service")
|
||||||
machine.wait_for_open_port(3890)
|
@@ -56,6 +150,9 @@ in
|
||||||
machine.wait_for_open_port(17170)
|
machine.succeed("curl --location --fail http://localhost:17170/")
|
||||||
|
|
||||||
machine.succeed("curl --location --fail http://localhost:17170/")
|
adminPassword="${adminPassword}"
|
||||||
+ adminPassword="${adminPassword}"
|
+ alicePassword="${alicePassword}"
|
||||||
+ alicePassword="${alicePassword}"
|
+ bobPassword="${bobPassword}"
|
||||||
+ bobPassword="${bobPassword}"
|
+ charliePassword="${charliePassword}"
|
||||||
|
|
||||||
|
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}'
|
||||||
|
@@ -70,18 +167,50 @@ in
|
||||||
|
raise Exception("Expected failure, had success")
|
||||||
|
return response
|
||||||
|
|
||||||
|
+ def parse_ldapsearch_output(output):
|
||||||
|
+ return {n:v for (n, v) in (x.split(': ', 2) for x in output.splitlines() if x != "")}
|
||||||
+
|
+
|
||||||
+ def try_login(user, password, expect_success=True):
|
with subtest("default admin password"):
|
||||||
+ code, response = machine.execute(f'ldapsearch -H ldap://localhost:3890 -D uid={user},ou=people,dc=example,dc=com -b "ou=people,dc=example,dc=com" -w {password}')
|
try_login("admin", "password", expect_success=True)
|
||||||
+ print(response)
|
try_login("admin", adminPassword, expect_success=False)
|
||||||
+ if expect_success:
|
|
||||||
+ if code != 0:
|
with subtest("different admin password"):
|
||||||
+ raise Exception("Expected failure, had success")
|
- machine.succeed('${specializations}/differentAdminPassword/bin/switch-to-configuration test')
|
||||||
+ else:
|
+ machine.succeed('${specialisations}/differentAdminPassword/bin/switch-to-configuration test')
|
||||||
+ if code == 0:
|
try_login("admin", "password", expect_success=False)
|
||||||
+ raise Exception(f"Expected success, had failure {code}")
|
try_login("admin", adminPassword, expect_success=True)
|
||||||
|
|
||||||
|
with subtest("change admin password has no effect"):
|
||||||
|
- machine.succeed('${specializations}/differentAdminPassword/bin/switch-to-configuration test')
|
||||||
|
+ machine.succeed('${specialisations}/differentAdminPassword/bin/switch-to-configuration test')
|
||||||
|
try_login("admin", "password", expect_success=False)
|
||||||
|
try_login("admin", adminPassword, expect_success=True)
|
||||||
+
|
+
|
||||||
+ with subtest("only default admin user"):
|
+ with subtest("with alice"):
|
||||||
+ print(try_login("admin", "password", expect_success=False))
|
+ machine.succeed('${specialisations}/withAlice/bin/switch-to-configuration test')
|
||||||
+ print(try_login("admin", adminPassword, expect_success=True))
|
+ try_login("alice", "password", expect_success=False)
|
||||||
+ print(try_login("alice", "password", expect_success=False))
|
+ try_login("alice", alicePassword, expect_success=True)
|
||||||
+ print(try_login("alice", alicePassword, expect_success=False))
|
+ try_login("bob", "password", expect_success=False)
|
||||||
+ print(try_login("bob", "password", expect_success=False))
|
+ try_login("bob", bobPassword, expect_success=False)
|
||||||
+ print(try_login("bob", bobPassword, expect_success=False))
|
+
|
||||||
|
+ with subtest("with bob"):
|
||||||
- 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')
|
+ machine.succeed('${specialisations}/withBob/bin/switch-to-configuration test')
|
||||||
- print(response)
|
+ try_login("alice", "password", expect_success=False)
|
||||||
+ with subtest("with alice"):
|
+ try_login("alice", alicePassword, expect_success=False)
|
||||||
+ machine.succeed('${specializations}/withAlice/bin/switch-to-configuration test')
|
+ try_login("bob", "password", expect_success=False)
|
||||||
+ print(try_login("admin", "password", expect_success=False))
|
+ try_login("bob", bobPassword, expect_success=True)
|
||||||
+ print(try_login("admin", adminPassword, expect_success=True))
|
+
|
||||||
+ print(try_login("alice", "password", expect_success=False))
|
+ with subtest("with attributes"):
|
||||||
+ print(try_login("alice", alicePassword, expect_success=True))
|
+ machine.succeed('${specialisations}/withAttributes/bin/switch-to-configuration test')
|
||||||
+ print(try_login("bob", "password", expect_success=False))
|
+
|
||||||
+ print(try_login("bob", bobPassword, expect_success=False))
|
+ response = machine.succeed(f'ldapsearch -LLL -H ldap://localhost:3890 -D uid=admin,ou=people,dc=example,dc=com -b "dc=example,dc=com" -w {adminPassword} "(uid=charlie)"')
|
||||||
|
+ 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}')
|
+ charlie = parse_ldapsearch_output(response)
|
||||||
- print(response)
|
+ if charlie.get('myattribute') != "2":
|
||||||
+ with subtest("with attributes"):
|
+ raise Exception(f'Unexpected value for attribute "myattribute": {charlie.get('myattribute')}')
|
||||||
+ machine.succeed('${specializations}/withBob/bin/switch-to-configuration test')
|
+
|
||||||
+ print(try_login("admin", "password", expect_success=False))
|
+ response = machine.succeed(f'ldapsearch -LLL -H ldap://localhost:3890 -D uid=admin,ou=people,dc=example,dc=com -b "dc=example,dc=com" -w {adminPassword} "(cn=othergroup)"')
|
||||||
+ print(try_login("admin", adminPassword, expect_success=True))
|
+ print(response)
|
||||||
+ print(try_login("alice", "password", expect_success=False))
|
+ othergroup = parse_ldapsearch_output(response)
|
||||||
+ print(try_login("alice", alicePassword, expect_success=False))
|
+ if othergroup.get('mygroupattribute') != "Managed by NixOS":
|
||||||
+ print(try_login("bob", "password", expect_success=False))
|
+ raise Exception(f'Unexpected value for attribute "mygroupattribute": {othergroup.get('mygroupattribute')}')
|
||||||
+ print(try_login("bob", bobPassword, expect_success=True))
|
'';
|
||||||
'';
|
|
||||||
}
|
}
|
||||||
diff --git a/pkgs/by-name/ll/lldap-bootstrap/package.nix b/pkgs/by-name/ll/lldap-bootstrap/package.nix
|
diff --git a/pkgs/by-name/ll/lldap-bootstrap/package.nix b/pkgs/by-name/ll/lldap-bootstrap/package.nix
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 000000000000..1459784bf8e5
|
index 000000000000..d31bad79ce67
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/pkgs/by-name/ll/lldap-bootstrap/package.nix
|
+++ b/pkgs/by-name/ll/lldap-bootstrap/package.nix
|
||||||
@@ -0,0 +1,54 @@
|
@@ -0,0 +1,54 @@
|
||||||
|
|
@ -508,8 +619,8 @@ index 000000000000..1459784bf8e5
|
||||||
+ src = fetchFromGitHub {
|
+ src = fetchFromGitHub {
|
||||||
+ owner = "ibizaman";
|
+ owner = "ibizaman";
|
||||||
+ repo = "lldap";
|
+ repo = "lldap";
|
||||||
+ rev = "14b083c9cf6c13802b7477af3b85894f2b7d1c81";
|
+ rev = "649a7023da17df70df88152844d2aa1ba0b64440";
|
||||||
+ hash = "sha256-olQGLgjLE7la5fYiCgC4tpaxyhFT9ZvRWLtASoaqq9k=";
|
+ hash = "sha256-89SoFqSm3dwVcPQM3lulY26o411NbdS6gTuolw2+e+U=";
|
||||||
+ };
|
+ };
|
||||||
+
|
+
|
||||||
+ dontBuild = true;
|
+ dontBuild = true;
|
||||||
|
|
@ -544,6 +655,24 @@ index 000000000000..1459784bf8e5
|
||||||
+ mainProgram = "lldap-bootstrap";
|
+ mainProgram = "lldap-bootstrap";
|
||||||
+ };
|
+ };
|
||||||
+}
|
+}
|
||||||
|
diff --git a/pkgs/by-name/ll/lldap/package.nix b/pkgs/by-name/ll/lldap/package.nix
|
||||||
|
index 41e4a332018e..dc145bbb3fa5 100644
|
||||||
|
--- a/pkgs/by-name/ll/lldap/package.nix
|
||||||
|
+++ b/pkgs/by-name/ll/lldap/package.nix
|
||||||
|
@@ -19,10 +19,10 @@ let
|
||||||
|
version = "unstable-2025-07-16";
|
||||||
|
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
- owner = "lldap";
|
||||||
|
+ owner = "ibizaman";
|
||||||
|
repo = "lldap";
|
||||||
|
- rev = "78337bce722c3573d9fc6eafe345a3dbce4b9119";
|
||||||
|
- hash = "sha256-/djLboAQwK/KQ0u9vzoOdDHwh/BQSvMa8lQkABn10Cw=";
|
||||||
|
+ rev = "93922b7b0f7f8ac294151ec61b9b21e50e504ab5";
|
||||||
|
+ hash = "sha256-p2PUaaD6OrQ+eCkcDZd6x61gERQFQSDEkxwl2s6y8rY=";
|
||||||
|
};
|
||||||
|
|
||||||
|
useFetchCargoVendor = true;
|
||||||
--
|
--
|
||||||
2.49.0
|
2.49.0
|
||||||
|
|
||||||
Loading…
Reference in a new issue