mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-17 11:10:03 +00:00
nixos/kanidm: inherit lib, nixfmt
This commit is contained in:
parent
e87eca7eb7
commit
8f18393d38
|
@ -6,10 +6,42 @@
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
|
inherit (lib)
|
||||||
|
any
|
||||||
|
attrNames
|
||||||
|
attrValues
|
||||||
|
concatLines
|
||||||
|
concatLists
|
||||||
|
converge
|
||||||
|
filter
|
||||||
|
filterAttrs
|
||||||
|
filterAttrsRecursive
|
||||||
|
flip
|
||||||
|
foldl'
|
||||||
|
getExe
|
||||||
|
hasInfix
|
||||||
|
hasPrefix
|
||||||
|
isStorePath
|
||||||
|
last
|
||||||
|
mapAttrsToList
|
||||||
|
mkEnableOption
|
||||||
|
mkForce
|
||||||
|
mkIf
|
||||||
|
mkMerge
|
||||||
|
mkOption
|
||||||
|
mkPackageOption
|
||||||
|
optional
|
||||||
|
optionalString
|
||||||
|
splitString
|
||||||
|
subtractLists
|
||||||
|
types
|
||||||
|
unique
|
||||||
|
;
|
||||||
|
|
||||||
cfg = config.services.kanidm;
|
cfg = config.services.kanidm;
|
||||||
settingsFormat = pkgs.formats.toml { };
|
settingsFormat = pkgs.formats.toml { };
|
||||||
# Remove null values, so we can document optional values that don't end up in the generated TOML file.
|
# Remove null values, so we can document optional values that don't end up in the generated TOML file.
|
||||||
filterConfig = lib.converge (lib.filterAttrsRecursive (_: v: v != null));
|
filterConfig = converge (filterAttrsRecursive (_: v: v != null));
|
||||||
serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings);
|
serverConfigFile = settingsFormat.generate "server.toml" (filterConfig cfg.serverSettings);
|
||||||
clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings);
|
clientConfigFile = settingsFormat.generate "kanidm-config.toml" (filterConfig cfg.clientSettings);
|
||||||
unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings);
|
unixConfigFile = settingsFormat.generate "kanidm-unixd.toml" (filterConfig cfg.unixSettings);
|
||||||
|
@ -22,17 +54,14 @@ let
|
||||||
# This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount
|
# This makes sure that if e.g. the tls_chain is in the nix store and /nix/store is already in the mount
|
||||||
# paths, no new bind mount is added. Adding subpaths caused problems on ofborg.
|
# paths, no new bind mount is added. Adding subpaths caused problems on ofborg.
|
||||||
hasPrefixInList =
|
hasPrefixInList =
|
||||||
list: newPath:
|
list: newPath: any (path: hasPrefix (builtins.toString path) (builtins.toString newPath)) list;
|
||||||
lib.any (path: lib.hasPrefix (builtins.toString path) (builtins.toString newPath)) list;
|
mergePaths = foldl' (
|
||||||
mergePaths = lib.foldl' (
|
|
||||||
merged: newPath:
|
merged: newPath:
|
||||||
let
|
let
|
||||||
# If the new path is a prefix to some existing path, we need to filter it out
|
# If the new path is a prefix to some existing path, we need to filter it out
|
||||||
filteredPaths = lib.filter (
|
filteredPaths = filter (p: !hasPrefix (builtins.toString newPath) (builtins.toString p)) merged;
|
||||||
p: !lib.hasPrefix (builtins.toString newPath) (builtins.toString p)
|
|
||||||
) merged;
|
|
||||||
# If a prefix of the new path is already in the list, do not add it
|
# If a prefix of the new path is already in the list, do not add it
|
||||||
filteredNew = lib.optional (!hasPrefixInList filteredPaths newPath) newPath;
|
filteredNew = optional (!hasPrefixInList filteredPaths newPath) newPath;
|
||||||
in
|
in
|
||||||
filteredPaths ++ filteredNew
|
filteredPaths ++ filteredNew
|
||||||
) [ ];
|
) [ ];
|
||||||
|
@ -84,13 +113,13 @@ let
|
||||||
|
|
||||||
mkPresentOption =
|
mkPresentOption =
|
||||||
what:
|
what:
|
||||||
lib.mkOption {
|
mkOption {
|
||||||
description = "Whether to ensure that this ${what} is present or absent.";
|
description = "Whether to ensure that this ${what} is present or absent.";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
filterPresent = lib.filterAttrs (_: v: v.present);
|
filterPresent = filterAttrs (_: v: v.present);
|
||||||
|
|
||||||
provisionStateJson = pkgs.writeText "provision-state.json" (
|
provisionStateJson = pkgs.writeText "provision-state.json" (
|
||||||
builtins.toJSON { inherit (cfg.provision) groups persons systems; }
|
builtins.toJSON { inherit (cfg.provision) groups persons systems; }
|
||||||
|
@ -98,7 +127,7 @@ let
|
||||||
|
|
||||||
# Only recover the admin account if a password should explicitly be provisioned
|
# Only recover the admin account if a password should explicitly be provisioned
|
||||||
# for the account. Otherwise it is not needed for provisioning.
|
# for the account. Otherwise it is not needed for provisioning.
|
||||||
maybeRecoverAdmin = lib.optionalString (cfg.provision.adminPasswordFile != null) ''
|
maybeRecoverAdmin = optionalString (cfg.provision.adminPasswordFile != null) ''
|
||||||
KANIDM_ADMIN_PASSWORD=$(< ${cfg.provision.adminPasswordFile})
|
KANIDM_ADMIN_PASSWORD=$(< ${cfg.provision.adminPasswordFile})
|
||||||
# We always reset the admin account password if a desired password was specified.
|
# We always reset the admin account password if a desired password was specified.
|
||||||
if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} admin --from-environment >/dev/null; then
|
if ! KANIDM_RECOVER_ACCOUNT_PASSWORD=$KANIDM_ADMIN_PASSWORD ${cfg.package}/bin/kanidmd recover-account -c ${serverConfigFile} admin --from-environment >/dev/null; then
|
||||||
|
@ -128,7 +157,7 @@ let
|
||||||
echo "kanidm provision: Failed to recover admin account" >&2
|
echo "kanidm provision: Failed to recover admin account" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
if ! KANIDM_IDM_ADMIN_PASSWORD=$(grep '{"password' <<< "$recover_out" | ${lib.getExe pkgs.jq} -r .password); then
|
if ! KANIDM_IDM_ADMIN_PASSWORD=$(grep '{"password' <<< "$recover_out" | ${getExe pkgs.jq} -r .password); then
|
||||||
echo "$recover_out" >&2
|
echo "$recover_out" >&2
|
||||||
echo "kanidm provision: Failed to parse password for idm_admin account" >&2
|
echo "kanidm provision: Failed to parse password for idm_admin account" >&2
|
||||||
exit 1
|
exit 1
|
||||||
|
@ -140,8 +169,8 @@ let
|
||||||
|
|
||||||
# Wait for the kanidm server to come online
|
# Wait for the kanidm server to come online
|
||||||
count=0
|
count=0
|
||||||
while ! ${lib.getExe pkgs.curl} -L --silent --max-time 1 --connect-timeout 1 --fail \
|
while ! ${getExe pkgs.curl} -L --silent --max-time 1 --connect-timeout 1 --fail \
|
||||||
${lib.optionalString cfg.provision.acceptInvalidCerts "--insecure"} \
|
${optionalString cfg.provision.acceptInvalidCerts "--insecure"} \
|
||||||
${cfg.provision.instanceUrl} >/dev/null
|
${cfg.provision.instanceUrl} >/dev/null
|
||||||
do
|
do
|
||||||
sleep 1
|
sleep 1
|
||||||
|
@ -156,58 +185,58 @@ let
|
||||||
${maybeRecoverAdmin}
|
${maybeRecoverAdmin}
|
||||||
|
|
||||||
KANIDM_PROVISION_IDM_ADMIN_TOKEN=$KANIDM_IDM_ADMIN_PASSWORD \
|
KANIDM_PROVISION_IDM_ADMIN_TOKEN=$KANIDM_IDM_ADMIN_PASSWORD \
|
||||||
${lib.getExe pkgs.kanidm-provision} \
|
${getExe pkgs.kanidm-provision} \
|
||||||
${lib.optionalString (!cfg.provision.autoRemove) "--no-auto-remove"} \
|
${optionalString (!cfg.provision.autoRemove) "--no-auto-remove"} \
|
||||||
${lib.optionalString cfg.provision.acceptInvalidCerts "--accept-invalid-certs"} \
|
${optionalString cfg.provision.acceptInvalidCerts "--accept-invalid-certs"} \
|
||||||
--url "${cfg.provision.instanceUrl}" \
|
--url "${cfg.provision.instanceUrl}" \
|
||||||
--state ${provisionStateJson}
|
--state ${provisionStateJson}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
serverPort =
|
serverPort =
|
||||||
# ipv6:
|
# ipv6:
|
||||||
if lib.hasInfix "]:" cfg.serverSettings.bindaddress then
|
if hasInfix "]:" cfg.serverSettings.bindaddress then
|
||||||
lib.last (lib.splitString "]:" cfg.serverSettings.bindaddress)
|
last (splitString "]:" cfg.serverSettings.bindaddress)
|
||||||
else
|
else
|
||||||
# ipv4:
|
# ipv4:
|
||||||
if lib.hasInfix "." cfg.serverSettings.bindaddress then
|
if hasInfix "." cfg.serverSettings.bindaddress then
|
||||||
lib.last (lib.splitString ":" cfg.serverSettings.bindaddress)
|
last (splitString ":" cfg.serverSettings.bindaddress)
|
||||||
# default is 8443
|
# default is 8443
|
||||||
else
|
else
|
||||||
"8443";
|
"8443";
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services.kanidm = {
|
options.services.kanidm = {
|
||||||
enableClient = lib.mkEnableOption "the Kanidm client";
|
enableClient = mkEnableOption "the Kanidm client";
|
||||||
enableServer = lib.mkEnableOption "the Kanidm server";
|
enableServer = mkEnableOption "the Kanidm server";
|
||||||
enablePam = lib.mkEnableOption "the Kanidm PAM and NSS integration";
|
enablePam = mkEnableOption "the Kanidm PAM and NSS integration";
|
||||||
|
|
||||||
package = lib.mkPackageOption pkgs "kanidm" { };
|
package = mkPackageOption pkgs "kanidm" { };
|
||||||
|
|
||||||
serverSettings = lib.mkOption {
|
serverSettings = mkOption {
|
||||||
type = lib.types.submodule {
|
type = types.submodule {
|
||||||
freeformType = settingsFormat.type;
|
freeformType = settingsFormat.type;
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
bindaddress = lib.mkOption {
|
bindaddress = mkOption {
|
||||||
description = "Address/port combination the webserver binds to.";
|
description = "Address/port combination the webserver binds to.";
|
||||||
example = "[::1]:8443";
|
example = "[::1]:8443";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
# Should be optional but toml does not accept null
|
# Should be optional but toml does not accept null
|
||||||
ldapbindaddress = lib.mkOption {
|
ldapbindaddress = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Address and port the LDAP server is bound to. Setting this to `null` disables the LDAP interface.
|
Address and port the LDAP server is bound to. Setting this to `null` disables the LDAP interface.
|
||||||
'';
|
'';
|
||||||
example = "[::1]:636";
|
example = "[::1]:636";
|
||||||
default = null;
|
default = null;
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = types.nullOr types.str;
|
||||||
};
|
};
|
||||||
origin = lib.mkOption {
|
origin = mkOption {
|
||||||
description = "The origin of your Kanidm instance. Must have https as protocol.";
|
description = "The origin of your Kanidm instance. Must have https as protocol.";
|
||||||
example = "https://idm.example.org";
|
example = "https://idm.example.org";
|
||||||
type = lib.types.strMatching "^https://.*";
|
type = types.strMatching "^https://.*";
|
||||||
};
|
};
|
||||||
domain = lib.mkOption {
|
domain = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
The `domain` that Kanidm manages. Must be below or equal to the domain
|
The `domain` that Kanidm manages. Must be below or equal to the domain
|
||||||
specified in `serverSettings.origin`.
|
specified in `serverSettings.origin`.
|
||||||
|
@ -218,58 +247,58 @@ in
|
||||||
'';
|
'';
|
||||||
example = "example.org";
|
example = "example.org";
|
||||||
default = null;
|
default = null;
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = types.nullOr types.str;
|
||||||
};
|
};
|
||||||
db_path = lib.mkOption {
|
db_path = mkOption {
|
||||||
description = "Path to Kanidm database.";
|
description = "Path to Kanidm database.";
|
||||||
default = "/var/lib/kanidm/kanidm.db";
|
default = "/var/lib/kanidm/kanidm.db";
|
||||||
readOnly = true;
|
readOnly = true;
|
||||||
type = lib.types.path;
|
type = types.path;
|
||||||
};
|
};
|
||||||
tls_chain = lib.mkOption {
|
tls_chain = mkOption {
|
||||||
description = "TLS chain in pem format.";
|
description = "TLS chain in pem format.";
|
||||||
type = lib.types.path;
|
type = types.path;
|
||||||
};
|
};
|
||||||
tls_key = lib.mkOption {
|
tls_key = mkOption {
|
||||||
description = "TLS key in pem format.";
|
description = "TLS key in pem format.";
|
||||||
type = lib.types.path;
|
type = types.path;
|
||||||
};
|
};
|
||||||
log_level = lib.mkOption {
|
log_level = mkOption {
|
||||||
description = "Log level of the server.";
|
description = "Log level of the server.";
|
||||||
default = "info";
|
default = "info";
|
||||||
type = lib.types.enum [
|
type = types.enum [
|
||||||
"info"
|
"info"
|
||||||
"debug"
|
"debug"
|
||||||
"trace"
|
"trace"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
role = lib.mkOption {
|
role = mkOption {
|
||||||
description = "The role of this server. This affects the replication relationship and thereby available features.";
|
description = "The role of this server. This affects the replication relationship and thereby available features.";
|
||||||
default = "WriteReplica";
|
default = "WriteReplica";
|
||||||
type = lib.types.enum [
|
type = types.enum [
|
||||||
"WriteReplica"
|
"WriteReplica"
|
||||||
"WriteReplicaNoUI"
|
"WriteReplicaNoUI"
|
||||||
"ReadOnlyReplica"
|
"ReadOnlyReplica"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
online_backup = {
|
online_backup = {
|
||||||
path = lib.mkOption {
|
path = mkOption {
|
||||||
description = "Path to the output directory for backups.";
|
description = "Path to the output directory for backups.";
|
||||||
type = lib.types.path;
|
type = types.path;
|
||||||
default = "/var/lib/kanidm/backups";
|
default = "/var/lib/kanidm/backups";
|
||||||
};
|
};
|
||||||
schedule = lib.mkOption {
|
schedule = mkOption {
|
||||||
description = "The schedule for backups in cron format.";
|
description = "The schedule for backups in cron format.";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
default = "00 22 * * *";
|
default = "00 22 * * *";
|
||||||
};
|
};
|
||||||
versions = lib.mkOption {
|
versions = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Number of backups to keep.
|
Number of backups to keep.
|
||||||
|
|
||||||
The default is set to `0`, in order to disable backups by default.
|
The default is set to `0`, in order to disable backups by default.
|
||||||
'';
|
'';
|
||||||
type = lib.types.ints.unsigned;
|
type = types.ints.unsigned;
|
||||||
default = 0;
|
default = 0;
|
||||||
example = 7;
|
example = 7;
|
||||||
};
|
};
|
||||||
|
@ -285,14 +314,14 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
clientSettings = lib.mkOption {
|
clientSettings = mkOption {
|
||||||
type = lib.types.submodule {
|
type = types.submodule {
|
||||||
freeformType = settingsFormat.type;
|
freeformType = settingsFormat.type;
|
||||||
|
|
||||||
options.uri = lib.mkOption {
|
options.uri = mkOption {
|
||||||
description = "Address of the Kanidm server.";
|
description = "Address of the Kanidm server.";
|
||||||
example = "http://127.0.0.1:8080";
|
example = "http://127.0.0.1:8080";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
description = ''
|
description = ''
|
||||||
|
@ -303,20 +332,20 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
unixSettings = lib.mkOption {
|
unixSettings = mkOption {
|
||||||
type = lib.types.submodule {
|
type = types.submodule {
|
||||||
freeformType = settingsFormat.type;
|
freeformType = settingsFormat.type;
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
pam_allowed_login_groups = lib.mkOption {
|
pam_allowed_login_groups = mkOption {
|
||||||
description = "Kanidm groups that are allowed to login using PAM.";
|
description = "Kanidm groups that are allowed to login using PAM.";
|
||||||
example = "my_pam_group";
|
example = "my_pam_group";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = types.listOf types.str;
|
||||||
};
|
};
|
||||||
hsm_pin_path = lib.mkOption {
|
hsm_pin_path = mkOption {
|
||||||
description = "Path to a HSM pin.";
|
description = "Path to a HSM pin.";
|
||||||
default = "/var/cache/kanidm-unixd/hsm-pin";
|
default = "/var/cache/kanidm-unixd/hsm-pin";
|
||||||
type = lib.types.path;
|
type = types.path;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -329,73 +358,73 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
provision = {
|
provision = {
|
||||||
enable = lib.mkEnableOption "provisioning of groups, users and oauth2 resource servers";
|
enable = mkEnableOption "provisioning of groups, users and oauth2 resource servers";
|
||||||
|
|
||||||
instanceUrl = lib.mkOption {
|
instanceUrl = mkOption {
|
||||||
description = "The instance url to which the provisioning tool should connect.";
|
description = "The instance url to which the provisioning tool should connect.";
|
||||||
default = "https://localhost:${serverPort}";
|
default = "https://localhost:${serverPort}";
|
||||||
defaultText = ''"https://localhost:<port from serverSettings.bindaddress>"'';
|
defaultText = ''"https://localhost:<port from serverSettings.bindaddress>"'';
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
};
|
};
|
||||||
|
|
||||||
acceptInvalidCerts = lib.mkOption {
|
acceptInvalidCerts = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Whether to allow invalid certificates when provisioning the target instance.
|
Whether to allow invalid certificates when provisioning the target instance.
|
||||||
By default this is only allowed when the instanceUrl is localhost. This is
|
By default this is only allowed when the instanceUrl is localhost. This is
|
||||||
dangerous when used with an external URL.
|
dangerous when used with an external URL.
|
||||||
'';
|
'';
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = lib.hasPrefix "https://localhost:" cfg.provision.instanceUrl;
|
default = hasPrefix "https://localhost:" cfg.provision.instanceUrl;
|
||||||
defaultText = ''lib.hasPrefix "https://localhost:" cfg.provision.instanceUrl'';
|
defaultText = ''hasPrefix "https://localhost:" cfg.provision.instanceUrl'';
|
||||||
};
|
};
|
||||||
|
|
||||||
adminPasswordFile = lib.mkOption {
|
adminPasswordFile = mkOption {
|
||||||
description = "Path to a file containing the admin password for kanidm. Do NOT use a file from the nix store here!";
|
description = "Path to a file containing the admin password for kanidm. Do NOT use a file from the nix store here!";
|
||||||
example = "/run/secrets/kanidm-admin-password";
|
example = "/run/secrets/kanidm-admin-password";
|
||||||
default = null;
|
default = null;
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = types.nullOr types.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
idmAdminPasswordFile = lib.mkOption {
|
idmAdminPasswordFile = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Path to a file containing the idm admin password for kanidm. Do NOT use a file from the nix store here!
|
Path to a file containing the idm admin password for kanidm. Do NOT use a file from the nix store here!
|
||||||
If this is not given but provisioning is enabled, the idm_admin password will be reset on each restart.
|
If this is not given but provisioning is enabled, the idm_admin password will be reset on each restart.
|
||||||
'';
|
'';
|
||||||
example = "/run/secrets/kanidm-idm-admin-password";
|
example = "/run/secrets/kanidm-idm-admin-password";
|
||||||
default = null;
|
default = null;
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = types.nullOr types.path;
|
||||||
};
|
};
|
||||||
|
|
||||||
autoRemove = lib.mkOption {
|
autoRemove = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Determines whether deleting an entity in this provisioning config should automatically
|
Determines whether deleting an entity in this provisioning config should automatically
|
||||||
cause them to be removed from kanidm, too. This works because the provisioning tool tracks
|
cause them to be removed from kanidm, too. This works because the provisioning tool tracks
|
||||||
all entities it has ever created. If this is set to false, you need to explicitly specify
|
all entities it has ever created. If this is set to false, you need to explicitly specify
|
||||||
`present = false` to delete an entity.
|
`present = false` to delete an entity.
|
||||||
'';
|
'';
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
groups = lib.mkOption {
|
groups = mkOption {
|
||||||
description = "Provisioning of kanidm groups";
|
description = "Provisioning of kanidm groups";
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = types.attrsOf (
|
||||||
lib.types.submodule (groupSubmod: {
|
types.submodule (groupSubmod: {
|
||||||
options = {
|
options = {
|
||||||
present = mkPresentOption "group";
|
present = mkPresentOption "group";
|
||||||
|
|
||||||
members = lib.mkOption {
|
members = mkOption {
|
||||||
description = "List of kanidm entities (persons, groups, ...) which are part of this group.";
|
description = "List of kanidm entities (persons, groups, ...) which are part of this group.";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = types.listOf types.str;
|
||||||
apply = lib.unique;
|
apply = unique;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
config.members = lib.concatLists (
|
config.members = concatLists (
|
||||||
lib.flip lib.mapAttrsToList cfg.provision.persons (
|
flip mapAttrsToList cfg.provision.persons (
|
||||||
person: personCfg:
|
person: personCfg:
|
||||||
lib.optional (
|
optional (
|
||||||
personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups
|
personCfg.present && builtins.elem groupSubmod.config._module.args.name personCfg.groups
|
||||||
) person
|
) person
|
||||||
)
|
)
|
||||||
|
@ -404,38 +433,38 @@ in
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
persons = lib.mkOption {
|
persons = mkOption {
|
||||||
description = "Provisioning of kanidm persons";
|
description = "Provisioning of kanidm persons";
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = types.attrsOf (
|
||||||
lib.types.submodule {
|
types.submodule {
|
||||||
options = {
|
options = {
|
||||||
present = mkPresentOption "person";
|
present = mkPresentOption "person";
|
||||||
|
|
||||||
displayName = lib.mkOption {
|
displayName = mkOption {
|
||||||
description = "Display name";
|
description = "Display name";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
example = "My User";
|
example = "My User";
|
||||||
};
|
};
|
||||||
|
|
||||||
legalName = lib.mkOption {
|
legalName = mkOption {
|
||||||
description = "Full legal name";
|
description = "Full legal name";
|
||||||
type = lib.types.nullOr lib.types.str;
|
type = types.nullOr types.str;
|
||||||
example = "Jane Doe";
|
example = "Jane Doe";
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
mailAddresses = lib.mkOption {
|
mailAddresses = mkOption {
|
||||||
description = "Mail addresses. First given address is considered the primary address.";
|
description = "Mail addresses. First given address is considered the primary address.";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = types.listOf types.str;
|
||||||
example = [ "jane.doe@example.com" ];
|
example = [ "jane.doe@example.com" ];
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
|
|
||||||
groups = lib.mkOption {
|
groups = mkOption {
|
||||||
description = "List of groups this person should belong to.";
|
description = "List of groups this person should belong to.";
|
||||||
type = lib.types.listOf lib.types.str;
|
type = types.listOf types.str;
|
||||||
apply = lib.unique;
|
apply = unique;
|
||||||
default = [ ];
|
default = [ ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -443,119 +472,119 @@ in
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
systems.oauth2 = lib.mkOption {
|
systems.oauth2 = mkOption {
|
||||||
description = "Provisioning of oauth2 resource servers";
|
description = "Provisioning of oauth2 resource servers";
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = types.attrsOf (
|
||||||
lib.types.submodule {
|
types.submodule {
|
||||||
options = {
|
options = {
|
||||||
present = mkPresentOption "oauth2 resource server";
|
present = mkPresentOption "oauth2 resource server";
|
||||||
|
|
||||||
public = lib.mkOption {
|
public = mkOption {
|
||||||
description = "Whether this is a public client (enforces PKCE, doesn't use a basic secret)";
|
description = "Whether this is a public client (enforces PKCE, doesn't use a basic secret)";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
displayName = lib.mkOption {
|
displayName = mkOption {
|
||||||
description = "Display name";
|
description = "Display name";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
example = "Some Service";
|
example = "Some Service";
|
||||||
};
|
};
|
||||||
|
|
||||||
originUrl = lib.mkOption {
|
originUrl = mkOption {
|
||||||
description = "The origin URL of the service. OAuth2 redirects will only be allowed to sites under this origin. Must end with a slash.";
|
description = "The origin URL of the service. OAuth2 redirects will only be allowed to sites under this origin. Must end with a slash.";
|
||||||
type =
|
type =
|
||||||
let
|
let
|
||||||
originStrType = lib.types.strMatching ".*://.*/$";
|
originStrType = types.strMatching ".*://.*/$";
|
||||||
in
|
in
|
||||||
lib.types.either originStrType (lib.types.nonEmptyListOf originStrType);
|
types.either originStrType (types.nonEmptyListOf originStrType);
|
||||||
example = "https://someservice.example.com/";
|
example = "https://someservice.example.com/";
|
||||||
};
|
};
|
||||||
|
|
||||||
originLanding = lib.mkOption {
|
originLanding = mkOption {
|
||||||
description = "When redirecting from the Kanidm Apps Listing page, some linked applications may need to land on a specific page to trigger oauth2/oidc interactions.";
|
description = "When redirecting from the Kanidm Apps Listing page, some linked applications may need to land on a specific page to trigger oauth2/oidc interactions.";
|
||||||
type = lib.types.str;
|
type = types.str;
|
||||||
example = "https://someservice.example.com/home";
|
example = "https://someservice.example.com/home";
|
||||||
};
|
};
|
||||||
|
|
||||||
basicSecretFile = lib.mkOption {
|
basicSecretFile = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
The basic secret to use for this service. If null, the random secret generated
|
The basic secret to use for this service. If null, the random secret generated
|
||||||
by kanidm will not be touched. Do NOT use a path from the nix store here!
|
by kanidm will not be touched. Do NOT use a path from the nix store here!
|
||||||
'';
|
'';
|
||||||
type = lib.types.nullOr lib.types.path;
|
type = types.nullOr types.path;
|
||||||
example = "/run/secrets/some-oauth2-basic-secret";
|
example = "/run/secrets/some-oauth2-basic-secret";
|
||||||
default = null;
|
default = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
enableLocalhostRedirects = lib.mkOption {
|
enableLocalhostRedirects = mkOption {
|
||||||
description = "Allow localhost redirects. Only for public clients.";
|
description = "Allow localhost redirects. Only for public clients.";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
enableLegacyCrypto = lib.mkOption {
|
enableLegacyCrypto = mkOption {
|
||||||
description = "Enable legacy crypto on this client. Allows JWT signing algorthms like RS256.";
|
description = "Enable legacy crypto on this client. Allows JWT signing algorthms like RS256.";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
allowInsecureClientDisablePkce = lib.mkOption {
|
allowInsecureClientDisablePkce = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Disable PKCE on this oauth2 resource server to work around insecure clients
|
Disable PKCE on this oauth2 resource server to work around insecure clients
|
||||||
that may not support it. You should request the client to enable PKCE!
|
that may not support it. You should request the client to enable PKCE!
|
||||||
Only for non-public clients.
|
Only for non-public clients.
|
||||||
'';
|
'';
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
preferShortUsername = lib.mkOption {
|
preferShortUsername = mkOption {
|
||||||
description = "Use 'name' instead of 'spn' in the preferred_username claim";
|
description = "Use 'name' instead of 'spn' in the preferred_username claim";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = false;
|
default = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
scopeMaps = lib.mkOption {
|
scopeMaps = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Maps kanidm groups to returned oauth scopes.
|
Maps kanidm groups to returned oauth scopes.
|
||||||
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
||||||
'';
|
'';
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
type = types.attrsOf (types.listOf types.str);
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
supplementaryScopeMaps = lib.mkOption {
|
supplementaryScopeMaps = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Maps kanidm groups to additionally returned oauth scopes.
|
Maps kanidm groups to additionally returned oauth scopes.
|
||||||
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
See [Scope Relations](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html#scope-relationships) for more information.
|
||||||
'';
|
'';
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
type = types.attrsOf (types.listOf types.str);
|
||||||
default = { };
|
default = { };
|
||||||
};
|
};
|
||||||
|
|
||||||
removeOrphanedClaimMaps = lib.mkOption {
|
removeOrphanedClaimMaps = mkOption {
|
||||||
description = "Whether claim maps not specified here but present in kanidm should be removed from kanidm.";
|
description = "Whether claim maps not specified here but present in kanidm should be removed from kanidm.";
|
||||||
type = lib.types.bool;
|
type = types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
claimMaps = lib.mkOption {
|
claimMaps = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Adds additional claims (and values) based on which kanidm groups an authenticating party belongs to.
|
Adds additional claims (and values) based on which kanidm groups an authenticating party belongs to.
|
||||||
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
||||||
'';
|
'';
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (
|
type = types.attrsOf (
|
||||||
lib.types.submodule {
|
types.submodule {
|
||||||
options = {
|
options = {
|
||||||
joinType = lib.mkOption {
|
joinType = mkOption {
|
||||||
description = ''
|
description = ''
|
||||||
Determines how multiple values are joined to create the claim value.
|
Determines how multiple values are joined to create the claim value.
|
||||||
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
See [Claim Maps](https://kanidm.github.io/kanidm/master/integrations/oauth2.html#custom-claim-maps) for more information.
|
||||||
'';
|
'';
|
||||||
type = lib.types.enum [
|
type = types.enum [
|
||||||
"array"
|
"array"
|
||||||
"csv"
|
"csv"
|
||||||
"ssv"
|
"ssv"
|
||||||
|
@ -563,10 +592,10 @@ in
|
||||||
default = "array";
|
default = "array";
|
||||||
};
|
};
|
||||||
|
|
||||||
valuesByGroup = lib.mkOption {
|
valuesByGroup = mkOption {
|
||||||
description = "Maps kanidm groups to values for the claim.";
|
description = "Maps kanidm groups to values for the claim.";
|
||||||
default = { };
|
default = { };
|
||||||
type = lib.types.attrsOf (lib.types.listOf lib.types.str);
|
type = types.attrsOf (types.listOf types.str);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -579,26 +608,26 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) {
|
config = mkIf (cfg.enableClient || cfg.enableServer || cfg.enablePam) {
|
||||||
assertions =
|
assertions =
|
||||||
let
|
let
|
||||||
entityList =
|
entityList =
|
||||||
type: attrs: lib.flip lib.mapAttrsToList (filterPresent attrs) (name: _: { inherit type name; });
|
type: attrs: flip mapAttrsToList (filterPresent attrs) (name: _: { inherit type name; });
|
||||||
entities =
|
entities =
|
||||||
entityList "group" cfg.provision.groups
|
entityList "group" cfg.provision.groups
|
||||||
++ entityList "person" cfg.provision.persons
|
++ entityList "person" cfg.provision.persons
|
||||||
++ entityList "oauth2" cfg.provision.systems.oauth2;
|
++ entityList "oauth2" cfg.provision.systems.oauth2;
|
||||||
|
|
||||||
# Accumulate entities by name. Track corresponding entity types for later duplicate check.
|
# Accumulate entities by name. Track corresponding entity types for later duplicate check.
|
||||||
entitiesByName = lib.foldl' (
|
entitiesByName = foldl' (
|
||||||
acc: { type, name }: acc // { ${name} = (acc.${name} or [ ]) ++ [ type ]; }
|
acc: { type, name }: acc // { ${name} = (acc.${name} or [ ]) ++ [ type ]; }
|
||||||
) { } entities;
|
) { } entities;
|
||||||
|
|
||||||
assertGroupsKnown =
|
assertGroupsKnown =
|
||||||
opt: groups:
|
opt: groups:
|
||||||
let
|
let
|
||||||
knownGroups = lib.attrNames (filterPresent cfg.provision.groups);
|
knownGroups = attrNames (filterPresent cfg.provision.groups);
|
||||||
unknownGroups = lib.subtractLists knownGroups groups;
|
unknownGroups = subtractLists knownGroups groups;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownGroups == [ ];
|
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownGroups == [ ];
|
||||||
|
@ -608,7 +637,7 @@ in
|
||||||
assertEntitiesKnown =
|
assertEntitiesKnown =
|
||||||
opt: entities:
|
opt: entities:
|
||||||
let
|
let
|
||||||
unknownEntities = lib.subtractLists (lib.attrNames entitiesByName) entities;
|
unknownEntities = subtractLists (attrNames entitiesByName) entities;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownEntities == [ ];
|
assertion = (cfg.enableServer && cfg.provision.enable) -> unknownEntities == [ ];
|
||||||
|
@ -620,7 +649,7 @@ in
|
||||||
assertion =
|
assertion =
|
||||||
!cfg.enableServer
|
!cfg.enableServer
|
||||||
|| ((cfg.serverSettings.tls_chain or null) == null)
|
|| ((cfg.serverSettings.tls_chain or null) == null)
|
||||||
|| (!lib.isStorePath cfg.serverSettings.tls_chain);
|
|| (!isStorePath cfg.serverSettings.tls_chain);
|
||||||
message = ''
|
message = ''
|
||||||
<option>services.kanidm.serverSettings.tls_chain</option> points to
|
<option>services.kanidm.serverSettings.tls_chain</option> points to
|
||||||
a file in the Nix store. You should use a quoted absolute path to
|
a file in the Nix store. You should use a quoted absolute path to
|
||||||
|
@ -631,7 +660,7 @@ in
|
||||||
assertion =
|
assertion =
|
||||||
!cfg.enableServer
|
!cfg.enableServer
|
||||||
|| ((cfg.serverSettings.tls_key or null) == null)
|
|| ((cfg.serverSettings.tls_key or null) == null)
|
||||||
|| (!lib.isStorePath cfg.serverSettings.tls_key);
|
|| (!isStorePath cfg.serverSettings.tls_key);
|
||||||
message = ''
|
message = ''
|
||||||
<option>services.kanidm.serverSettings.tls_key</option> points to
|
<option>services.kanidm.serverSettings.tls_key</option> points to
|
||||||
a file in the Nix store. You should use a quoted absolute path to
|
a file in the Nix store. You should use a quoted absolute path to
|
||||||
|
@ -677,9 +706,7 @@ in
|
||||||
&& (
|
&& (
|
||||||
cfg.provision.adminPasswordFile != null
|
cfg.provision.adminPasswordFile != null
|
||||||
|| cfg.provision.idmAdminPasswordFile != null
|
|| cfg.provision.idmAdminPasswordFile != null
|
||||||
|| lib.any (x: x.basicSecretFile != null) (
|
|| any (x: x.basicSecretFile != null) (attrValues (filterPresent cfg.provision.systems.oauth2))
|
||||||
lib.attrValues (filterPresent cfg.provision.systems.oauth2)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
-> cfg.package.enableSecretProvisioning;
|
-> cfg.package.enableSecretProvisioning;
|
||||||
|
@ -692,48 +719,48 @@ in
|
||||||
(
|
(
|
||||||
let
|
let
|
||||||
# Filter all names that occurred in more than one entity type.
|
# Filter all names that occurred in more than one entity type.
|
||||||
duplicateNames = lib.filterAttrs (_: v: builtins.length v > 1) entitiesByName;
|
duplicateNames = filterAttrs (_: v: builtins.length v > 1) entitiesByName;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
assertion = cfg.provision.enable -> duplicateNames == { };
|
assertion = cfg.provision.enable -> duplicateNames == { };
|
||||||
message = ''
|
message = ''
|
||||||
services.kanidm.provision requires all entity names (group, person, oauth2, ...) to be unique!
|
services.kanidm.provision requires all entity names (group, person, oauth2, ...) to be unique!
|
||||||
${lib.concatLines (
|
${concatLines (
|
||||||
lib.mapAttrsToList (name: xs: " - '${name}' used as: ${toString xs}") duplicateNames
|
mapAttrsToList (name: xs: " - '${name}' used as: ${toString xs}") duplicateNames
|
||||||
)}'';
|
)}'';
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
++ lib.flip lib.mapAttrsToList (filterPresent cfg.provision.persons) (
|
++ flip mapAttrsToList (filterPresent cfg.provision.persons) (
|
||||||
person: personCfg:
|
person: personCfg:
|
||||||
assertGroupsKnown "services.kanidm.provision.persons.${person}.groups" personCfg.groups
|
assertGroupsKnown "services.kanidm.provision.persons.${person}.groups" personCfg.groups
|
||||||
)
|
)
|
||||||
++ lib.flip lib.mapAttrsToList (filterPresent cfg.provision.groups) (
|
++ flip mapAttrsToList (filterPresent cfg.provision.groups) (
|
||||||
group: groupCfg:
|
group: groupCfg:
|
||||||
assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members
|
assertEntitiesKnown "services.kanidm.provision.groups.${group}.members" groupCfg.members
|
||||||
)
|
)
|
||||||
++ lib.concatLists (
|
++ concatLists (
|
||||||
lib.flip lib.mapAttrsToList (filterPresent cfg.provision.systems.oauth2) (
|
flip mapAttrsToList (filterPresent cfg.provision.systems.oauth2) (
|
||||||
oauth2: oauth2Cfg:
|
oauth2: oauth2Cfg:
|
||||||
[
|
[
|
||||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (
|
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.scopeMaps" (
|
||||||
lib.attrNames oauth2Cfg.scopeMaps
|
attrNames oauth2Cfg.scopeMaps
|
||||||
))
|
))
|
||||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (
|
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.supplementaryScopeMaps" (
|
||||||
lib.attrNames oauth2Cfg.supplementaryScopeMaps
|
attrNames oauth2Cfg.supplementaryScopeMaps
|
||||||
))
|
))
|
||||||
]
|
]
|
||||||
++ lib.concatLists (
|
++ concatLists (
|
||||||
lib.flip lib.mapAttrsToList oauth2Cfg.claimMaps (
|
flip mapAttrsToList oauth2Cfg.claimMaps (
|
||||||
claim: claimCfg: [
|
claim: claimCfg: [
|
||||||
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim}.valuesByGroup" (
|
(assertGroupsKnown "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim}.valuesByGroup" (
|
||||||
lib.attrNames claimCfg.valuesByGroup
|
attrNames claimCfg.valuesByGroup
|
||||||
))
|
))
|
||||||
# At least one group must map to a value in each claim map
|
# At least one group must map to a value in each claim map
|
||||||
{
|
{
|
||||||
assertion =
|
assertion =
|
||||||
(cfg.provision.enable && cfg.enableServer)
|
(cfg.provision.enable && cfg.enableServer)
|
||||||
-> lib.any (xs: xs != [ ]) (lib.attrValues claimCfg.valuesByGroup);
|
-> any (xs: xs != [ ]) (attrValues claimCfg.valuesByGroup);
|
||||||
message = "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim} does not specify any values for any group";
|
message = "services.kanidm.provision.systems.oauth2.${oauth2}.claimMaps.${claim} does not specify any values for any group";
|
||||||
}
|
}
|
||||||
# Public clients cannot define a basic secret
|
# Public clients cannot define a basic secret
|
||||||
|
@ -762,7 +789,7 @@ in
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
environment.systemPackages = lib.mkIf cfg.enableClient [ cfg.package ];
|
environment.systemPackages = mkIf cfg.enableClient [ cfg.package ];
|
||||||
|
|
||||||
systemd.tmpfiles.settings."10-kanidm" = {
|
systemd.tmpfiles.settings."10-kanidm" = {
|
||||||
${cfg.serverSettings.online_backup.path}.d = {
|
${cfg.serverSettings.online_backup.path}.d = {
|
||||||
|
@ -772,11 +799,11 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.kanidm = lib.mkIf cfg.enableServer {
|
systemd.services.kanidm = mkIf cfg.enableServer {
|
||||||
description = "kanidm identity management daemon";
|
description = "kanidm identity management daemon";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
serviceConfig = lib.mkMerge [
|
serviceConfig = mkMerge [
|
||||||
# Merge paths and ignore existing prefixes needs to sidestep mkMerge
|
# Merge paths and ignore existing prefixes needs to sidestep mkMerge
|
||||||
(
|
(
|
||||||
defaultServiceConfig
|
defaultServiceConfig
|
||||||
|
@ -789,7 +816,7 @@ in
|
||||||
StateDirectoryMode = "0700";
|
StateDirectoryMode = "0700";
|
||||||
RuntimeDirectory = "kanidmd";
|
RuntimeDirectory = "kanidmd";
|
||||||
ExecStart = "${cfg.package}/bin/kanidmd server -c ${serverConfigFile}";
|
ExecStart = "${cfg.package}/bin/kanidmd server -c ${serverConfigFile}";
|
||||||
ExecStartPost = lib.mkIf cfg.provision.enable postStartScript;
|
ExecStartPost = mkIf cfg.provision.enable postStartScript;
|
||||||
User = "kanidm";
|
User = "kanidm";
|
||||||
Group = "kanidm";
|
Group = "kanidm";
|
||||||
|
|
||||||
|
@ -803,9 +830,9 @@ in
|
||||||
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
AmbientCapabilities = [ "CAP_NET_BIND_SERVICE" ];
|
||||||
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
CapabilityBoundingSet = [ "CAP_NET_BIND_SERVICE" ];
|
||||||
# This would otherwise override the CAP_NET_BIND_SERVICE capability.
|
# This would otherwise override the CAP_NET_BIND_SERVICE capability.
|
||||||
PrivateUsers = lib.mkForce false;
|
PrivateUsers = mkForce false;
|
||||||
# Port needs to be exposed to the host network
|
# Port needs to be exposed to the host network
|
||||||
PrivateNetwork = lib.mkForce false;
|
PrivateNetwork = mkForce false;
|
||||||
RestrictAddressFamilies = [
|
RestrictAddressFamilies = [
|
||||||
"AF_INET"
|
"AF_INET"
|
||||||
"AF_INET6"
|
"AF_INET6"
|
||||||
|
@ -817,7 +844,7 @@ in
|
||||||
environment.RUST_LOG = "info";
|
environment.RUST_LOG = "info";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.kanidm-unixd = lib.mkIf cfg.enablePam {
|
systemd.services.kanidm-unixd = mkIf cfg.enablePam {
|
||||||
description = "Kanidm PAM daemon";
|
description = "Kanidm PAM daemon";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network.target" ];
|
after = [ "network.target" ];
|
||||||
|
@ -825,7 +852,7 @@ in
|
||||||
unixConfigFile
|
unixConfigFile
|
||||||
clientConfigFile
|
clientConfigFile
|
||||||
];
|
];
|
||||||
serviceConfig = lib.mkMerge [
|
serviceConfig = mkMerge [
|
||||||
defaultServiceConfig
|
defaultServiceConfig
|
||||||
{
|
{
|
||||||
CacheDirectory = "kanidm-unixd";
|
CacheDirectory = "kanidm-unixd";
|
||||||
|
@ -848,7 +875,7 @@ in
|
||||||
"/run/kanidm-unixd:/var/run/kanidm-unixd"
|
"/run/kanidm-unixd:/var/run/kanidm-unixd"
|
||||||
];
|
];
|
||||||
# Needs to connect to kanidmd
|
# Needs to connect to kanidmd
|
||||||
PrivateNetwork = lib.mkForce false;
|
PrivateNetwork = mkForce false;
|
||||||
RestrictAddressFamilies = [
|
RestrictAddressFamilies = [
|
||||||
"AF_INET"
|
"AF_INET"
|
||||||
"AF_INET6"
|
"AF_INET6"
|
||||||
|
@ -860,7 +887,7 @@ in
|
||||||
environment.RUST_LOG = "info";
|
environment.RUST_LOG = "info";
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.services.kanidm-unixd-tasks = lib.mkIf cfg.enablePam {
|
systemd.services.kanidm-unixd-tasks = mkIf cfg.enablePam {
|
||||||
description = "Kanidm PAM home management daemon";
|
description = "Kanidm PAM home management daemon";
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [
|
after = [
|
||||||
|
@ -910,25 +937,25 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
# These paths are hardcoded
|
# These paths are hardcoded
|
||||||
environment.etc = lib.mkMerge [
|
environment.etc = mkMerge [
|
||||||
(lib.mkIf cfg.enableServer { "kanidm/server.toml".source = serverConfigFile; })
|
(mkIf cfg.enableServer { "kanidm/server.toml".source = serverConfigFile; })
|
||||||
(lib.mkIf options.services.kanidm.clientSettings.isDefined {
|
(mkIf options.services.kanidm.clientSettings.isDefined {
|
||||||
"kanidm/config".source = clientConfigFile;
|
"kanidm/config".source = clientConfigFile;
|
||||||
})
|
})
|
||||||
(lib.mkIf cfg.enablePam { "kanidm/unixd".source = unixConfigFile; })
|
(mkIf cfg.enablePam { "kanidm/unixd".source = unixConfigFile; })
|
||||||
];
|
];
|
||||||
|
|
||||||
system.nssModules = lib.mkIf cfg.enablePam [ cfg.package ];
|
system.nssModules = mkIf cfg.enablePam [ cfg.package ];
|
||||||
|
|
||||||
system.nssDatabases.group = lib.optional cfg.enablePam "kanidm";
|
system.nssDatabases.group = optional cfg.enablePam "kanidm";
|
||||||
system.nssDatabases.passwd = lib.optional cfg.enablePam "kanidm";
|
system.nssDatabases.passwd = optional cfg.enablePam "kanidm";
|
||||||
|
|
||||||
users.groups = lib.mkMerge [
|
users.groups = mkMerge [
|
||||||
(lib.mkIf cfg.enableServer { kanidm = { }; })
|
(mkIf cfg.enableServer { kanidm = { }; })
|
||||||
(lib.mkIf cfg.enablePam { kanidm-unixd = { }; })
|
(mkIf cfg.enablePam { kanidm-unixd = { }; })
|
||||||
];
|
];
|
||||||
users.users = lib.mkMerge [
|
users.users = mkMerge [
|
||||||
(lib.mkIf cfg.enableServer {
|
(mkIf cfg.enableServer {
|
||||||
kanidm = {
|
kanidm = {
|
||||||
description = "Kanidm server";
|
description = "Kanidm server";
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
|
@ -936,7 +963,7 @@ in
|
||||||
packages = [ cfg.package ];
|
packages = [ cfg.package ];
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
(lib.mkIf cfg.enablePam {
|
(mkIf cfg.enablePam {
|
||||||
kanidm-unixd = {
|
kanidm-unixd = {
|
||||||
description = "Kanidm PAM daemon";
|
description = "Kanidm PAM daemon";
|
||||||
isSystemUser = true;
|
isSystemUser = true;
|
||||||
|
|
Loading…
Reference in a new issue