From 481131b7f475ebd2174bf931b855bec71d93ea64 Mon Sep 17 00:00:00 2001 From: Flakebi Date: Wed, 21 May 2025 08:49:52 +0200 Subject: [PATCH 1/2] nixos/kanidm: Fix bind paths MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. We bound the directory of certificates, this lead to forced read-only binds of these directories, even if they should have been bound read-write for other files in there. Looking at the history, there seems to be no compelling reason for this, so switch to binding the files directly. 2. `/run/kanidmd` is configured as `RuntimeDirectory` so bound automatically and we don’t need to specify it explicitly. (cherry picked from commit c4f052c08ae5724614fda2913280233cb3c142f0) --- nixos/modules/services/security/kanidm.nix | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix index 67b219595458..9e624abf599b 100644 --- a/nixos/modules/services/security/kanidm.nix +++ b/nixos/modules/services/security/kanidm.nix @@ -54,15 +54,10 @@ let ++ optional (cfg.provision.extraJsonFile != null) cfg.provision.extraJsonFile ++ mapAttrsToList (_: x: x.basicSecretFile) cfg.provision.systems.oauth2 ); - secretDirectories = unique ( - map builtins.dirOf ( - [ - cfg.serverSettings.tls_chain - cfg.serverSettings.tls_key - ] - ++ optionals cfg.provision.enable provisionSecretFiles - ) - ); + secretPaths = [ + cfg.serverSettings.tls_chain + cfg.serverSettings.tls_key + ] ++ optionals cfg.provision.enable provisionSecretFiles; # Merge bind mount paths and remove paths where a prefix is already mounted. # This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount @@ -881,7 +876,7 @@ in ( defaultServiceConfig // { - BindReadOnlyPaths = mergePaths (defaultServiceConfig.BindReadOnlyPaths ++ secretDirectories); + BindReadOnlyPaths = mergePaths (defaultServiceConfig.BindReadOnlyPaths ++ secretPaths); } ) { @@ -895,8 +890,6 @@ in BindPaths = [ - # To create the socket - "/run/kanidmd:/run/kanidmd" # To store backups cfg.serverSettings.online_backup.path ] From 52831b648ce45992b78eaf6f05cda8754d5e150c Mon Sep 17 00:00:00 2001 From: Ilan Joselevich Date: Tue, 27 May 2025 18:06:34 +0300 Subject: [PATCH 2/2] nixos/kanidm: merge recursively with extraJsonFile Previously, if you set group memberships in both locations, they will get replaced by the ones in extraJsonFile, which is unexpected as it kicks users from the group. Now the state files get merged recursively, including the arrays. (cherry picked from commit 3b6b50dfadb4e861d55fb4d2c66325fc7e321236) --- nixos/modules/services/security/kanidm.nix | 8 ++--- nixos/tests/kanidm-provisioning.nix | 38 ++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/nixos/modules/services/security/kanidm.nix b/nixos/modules/services/security/kanidm.nix index 9e624abf599b..1fc864f79eea 100644 --- a/nixos/modules/services/security/kanidm.nix +++ b/nixos/modules/services/security/kanidm.nix @@ -180,7 +180,9 @@ let finalJson = if cfg.provision.extraJsonFile != null then - "<(${lib.getExe pkgs.jq} -s '.[0] * .[1]' ${provisionStateJson} ${cfg.provision.extraJsonFile})" + '' + <(${lib.getExe pkgs.yq-go} '. *+ load("${cfg.provision.extraJsonFile}") | (.. | select(type == "!!seq")) |= unique' ${provisionStateJson}) + '' else provisionStateJson; @@ -437,10 +439,8 @@ in description = '' A JSON file for provisioning persons, groups & systems. Options set in this file take precedence over values set using the other options. - In the case of duplicates, `jq` will remove all but the last one - when merging this file with the options. + The files get deeply merged, and deduplicated. The accepted JSON schema can be found at . - Note: theoretically `jq` cannot merge nested types, but this does not pose an issue as kanidm-provision's JSON scheme does not use nested types. ''; type = types.nullOr types.path; default = null; diff --git a/nixos/tests/kanidm-provisioning.nix b/nixos/tests/kanidm-provisioning.nix index 8f0ca0ec0859..6866a16427d4 100644 --- a/nixos/tests/kanidm-provisioning.nix +++ b/nixos/tests/kanidm-provisioning.nix @@ -235,6 +235,29 @@ import ./make-test-python.nix ( }; }; + specialisation.extraJsonFile.configuration = + { ... }: + { + services.kanidm.provision = lib.mkForce { + enable = true; + idmAdminPasswordFile = pkgs.writeText "idm-admin-pw" provisionIdmAdminPassword; + + extraJsonFile = pkgs.writeText "extra-json.json" ( + builtins.toJSON { + persons.testuser2.displayName = "Test User 2"; + groups.testgroup1.members = [ "testuser2" ]; + } + ); + + groups.testgroup1 = { }; + + persons.testuser1 = { + displayName = "Test User 1"; + groups = [ "testgroup1" ]; + }; + }; + }; + security.pki.certificateFiles = [ certs.ca.cert ]; networking.hosts."::1" = [ serverDomain ]; @@ -516,6 +539,21 @@ import ./make-test-python.nix ( out = provision.succeed("kanidm system oauth2 get service2") assert_lacks(out, "name: service2") + provision.succeed("kanidm logout -D idm_admin") + + with subtest("Test Provisioning - extraJsonFile"): + provision.succeed('${specialisations}/extraJsonFile/bin/switch-to-configuration test') + provision_login("${provisionIdmAdminPassword}") + out = provision.succeed("kanidm group get testgroup1") + assert_contains(out, "name: testgroup1") + out = provision.succeed("kanidm person get testuser1") + assert_contains(out, "name: testuser1") + out = provision.succeed("kanidm person get testuser2") + assert_contains(out, "name: testuser2") + out = provision.succeed("kanidm group get testgroup1") + assert_contains(out, "member: testuser1") + assert_contains(out, "member: testuser2") + provision.succeed("kanidm logout -D idm_admin") ''; }