lldap: add options to set important secrets
This commit is contained in:
committed by
Herwig Hochleitner
parent
1bda23e1fb
commit
42b6d62699
@@ -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" ];
|
||||
|
||||
+63
-9
@@ -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/")
|
||||
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')
|
||||
)
|
||||
'';
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
'';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user