285 lines
8.6 KiB
Nix
285 lines
8.6 KiB
Nix
{
|
|
pkgs,
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
inherit (lib)
|
|
mkEnableOption
|
|
mkIf
|
|
mkOption
|
|
converge
|
|
mkMerge
|
|
filterAttrsRecursive
|
|
literalExpression
|
|
mapAttrsToList
|
|
concatStringsSep
|
|
attrNames
|
|
;
|
|
inherit (lib.types)
|
|
str
|
|
nullOr
|
|
pathWith
|
|
submodule
|
|
listOf
|
|
functionTo
|
|
attrsOf
|
|
lines
|
|
;
|
|
absPath = pathWith {
|
|
inStore = false;
|
|
absolute = true;
|
|
};
|
|
cfg = config.xyno.services.kanidm;
|
|
tlsDir = "/run/generated/kanidm-tls";
|
|
package = pkgs.kanidmWithSecretProvisioning_1_8.overrideAttrs (old: {
|
|
doCheck = false;
|
|
patches = old.patches ++ [
|
|
(pkgs.writeText "patch-kanidm-name" ''
|
|
diff --git a/server/lib/src/value.rs b/server/lib/src/value.rs
|
|
index 86b5a74c1..c83b2f93d 100644
|
|
--- a/server/lib/src/value.rs
|
|
+++ b/server/lib/src/value.rs
|
|
@@ -64,7 +64,7 @@ lazy_static! {
|
|
/// Only lowercase+numbers, with limited chars.
|
|
pub static ref INAME_RE: Regex = {
|
|
#[allow(clippy::expect_used)]
|
|
- Regex::new("^[a-z][a-z0-9-_\\.]{0,63}$").expect("Invalid Iname regex found")
|
|
+ Regex::new("^[a-z0-9-_\\.]{0,64}$").expect("Invalid Iname regex found")
|
|
};
|
|
|
|
/// Only alpha-numeric with limited special chars and space
|
|
'')
|
|
];
|
|
});
|
|
templatePlaceholders = {
|
|
clientId = ''\($get.attrs.name[0])'';
|
|
basicSecret = ''\($secret.secret)'';
|
|
env = v: ''\(env.${v})'';
|
|
};
|
|
in
|
|
{
|
|
options.xyno.services.kanidm.enable = mkEnableOption "enables kanidm";
|
|
options.xyno.services.kanidm.domain = mkOption {
|
|
default = "idm.xyno.systems";
|
|
type = str;
|
|
};
|
|
options.xyno.services.kanidm.isReplica = mkEnableOption "replica"; # TODO
|
|
options.xyno.services.kanidm.setupTraefik = mkEnableOption "traefik";
|
|
options.xyno.services.kanidm.templates = mkOption {
|
|
type = attrsOf (
|
|
submodule (
|
|
{ name, ... }:
|
|
{
|
|
options = {
|
|
path = mkOption {
|
|
type = absPath;
|
|
default = "/run/generated/kanidmTemplates/${name}";
|
|
};
|
|
user = mkOption {
|
|
type = str;
|
|
default = "root";
|
|
};
|
|
group = mkOption {
|
|
type = str;
|
|
default = "kanidm";
|
|
};
|
|
chmod = mkOption {
|
|
type = str;
|
|
default = "440";
|
|
};
|
|
wantedBy = mkOption {
|
|
type = listOf str;
|
|
default = [ ];
|
|
example = [ "traccar.service" ];
|
|
};
|
|
text = mkOption {
|
|
type = functionTo lines;
|
|
description = ''
|
|
jq templated string
|
|
|
|
current placeholders: ${concatStringsSep ", " (attrNames templatePlaceholders)}
|
|
'';
|
|
example = literalExpression ''
|
|
p: ${"''"}
|
|
OAUTH2_PROXY_CLIENT_ID=''${p.clientId}
|
|
OAUTH2_PROXY_CLIENT_SECRET=''${p.clientSecret}
|
|
${"''"}
|
|
'';
|
|
};
|
|
environmentFiles = mkOption {
|
|
type = listOf absPath;
|
|
default = [ ];
|
|
description = ''
|
|
add environment variables to the template file.
|
|
the environment variable BANANA would be accessible as
|
|
```
|
|
COOKIE_SECRET=''${p.env "BANANA"}
|
|
```
|
|
in the template
|
|
'';
|
|
};
|
|
|
|
};
|
|
|
|
}
|
|
)
|
|
);
|
|
|
|
example = {
|
|
traccar.text = p: ''
|
|
OPENID_CLIENTID=${p.clientId}
|
|
OPENID_SECRET=${p.basicSecret}
|
|
'';
|
|
};
|
|
};
|
|
|
|
options.xyno.services.kanidm.tls = {
|
|
keyPem = mkOption {
|
|
type = nullOr absPath;
|
|
default = null;
|
|
description = "autogenerated if unset";
|
|
};
|
|
certPem = mkOption {
|
|
default = "${tlsDir}/cert.pem";
|
|
type = absPath;
|
|
};
|
|
};
|
|
config = mkMerge [
|
|
(mkIf cfg.enable {
|
|
services.kanidm = {
|
|
enableServer = true;
|
|
enableClient = true;
|
|
inherit package;
|
|
clientSettings.uri = "https://${cfg.domain}";
|
|
|
|
provision = {
|
|
enable = true;
|
|
adminPasswordFile = config.sops.secrets."kanidm/adminPassword".path;
|
|
idmAdminPasswordFile = config.sops.secrets."kanidm/idmAdminPassword".path;
|
|
instanceUrl = "https://127.0.0.3:8443";
|
|
acceptInvalidCerts = true;
|
|
autoRemove = true;
|
|
groups.application_admins = {};
|
|
};
|
|
serverSettings = {
|
|
trust_x_forward_for = true;
|
|
tls_key = if cfg.tls.keyPem != null then cfg.tls.keyPem else "${tlsDir}/key.pem";
|
|
tls_chain = cfg.tls.certPem;
|
|
bindaddress = "127.0.0.3:8443";
|
|
origin = "https://${cfg.domain}";
|
|
domain = cfg.domain;
|
|
};
|
|
};
|
|
systemd.tmpfiles.rules = [ "d /run/generated/kanidmTemplates 1755 root kanidm -" ];
|
|
systemd.services = mkMerge (
|
|
(mapAttrsToList (n: v: {
|
|
"generate-kanidm-template-${n}" = {
|
|
serviceConfig = {
|
|
User = "root";
|
|
Group = "kanidm";
|
|
Type = "oneshot";
|
|
PrivateTmp = true;
|
|
EnvironmentFile = v.environmentFiles;
|
|
};
|
|
requires = [ "kanidm.service" ] ++ (lib.optional cfg.setupTraefik "traefik.service");
|
|
after = [
|
|
"kanidm.service"
|
|
"systemd-tmpfiles-setup.service"
|
|
]
|
|
++ (lib.optional cfg.setupTraefik "traefik.service");
|
|
before = v.wantedBy;
|
|
partOf = v.wantedBy;
|
|
wantedBy = if (builtins.length v.wantedBy) == 0 then [ "multi-user.target" ] else v.wantedBy;
|
|
enableStrictShellChecks = true;
|
|
path = [
|
|
package
|
|
pkgs.jq
|
|
];
|
|
environment.KANIDM_TOKEN_CACHE_PATH = "/tmp/kanidm-token-cache";
|
|
script =
|
|
let
|
|
templateText = v.text templatePlaceholders;
|
|
in
|
|
''
|
|
KANIDM_PASSWORD=$(cat "${
|
|
config.sops.secrets."kanidm/idmAdminPassword".path
|
|
}") kanidm login -D idm_admin
|
|
jq -r -s \
|
|
-f "${pkgs.writeText "kanidm-template-${n}" ''"${templateText}"''}" \
|
|
--argjson get "$(kanidm system oauth2 get --output json "${n}")" \
|
|
--argjson secret "$(kanidm system oauth2 show-basic-secret --output json "${n}")" \
|
|
> "${v.path}"
|
|
chown "${v.user}:${v.group}" "${v.path}"
|
|
chmod "${v.chmod}" "${v.path}"
|
|
'';
|
|
};
|
|
}) cfg.templates)
|
|
++ [
|
|
(mkIf (cfg.tls.keyPem == null) {
|
|
|
|
generate-kanidm-tls =
|
|
let
|
|
units = [
|
|
"kanidm.service"
|
|
]
|
|
++ (lib.optional cfg.setupTraefik "traefik.service");
|
|
in
|
|
{
|
|
serviceConfig = {
|
|
User = "root";
|
|
Group = "kanidm";
|
|
Type = "oneshot";
|
|
};
|
|
wantedBy = units;
|
|
before = units;
|
|
script = ''
|
|
mkdir -p ${tlsDir}
|
|
cd ${tlsDir}
|
|
${config.services.kanidm.package}/bin/kanidmd cert-generate -c ${
|
|
let
|
|
toml = pkgs.formats.toml { };
|
|
filterConfig = converge (filterAttrsRecursive (_: v: v != null));
|
|
in
|
|
toml.generate "kanidm-tls.conf" (filterConfig (config.services.kanidm.serverSettings))
|
|
}
|
|
chmod +g ${tlsDir}/*
|
|
'';
|
|
};
|
|
})
|
|
]
|
|
);
|
|
sops.secrets."kanidm/adminPassword" = {
|
|
sopsFile = ../../instances/${config.networking.hostName}/secrets/kanidm.yaml;
|
|
reloadUnits = [ "kanidm.service" ];
|
|
owner = "kanidm";
|
|
};
|
|
sops.secrets."kanidm/idmAdminPassword" = {
|
|
sopsFile = ../../instances/${config.networking.hostName}/secrets/kanidm.yaml;
|
|
reloadUnits = [ "kanidm.service" ];
|
|
owner = "kanidm";
|
|
};
|
|
|
|
xyno.impermanence.directories = [ "/var/lib/kanidm" ];
|
|
})
|
|
(mkIf (cfg.enable && cfg.setupTraefik) {
|
|
|
|
xyno.services.traefik.simpleProxy.kanidm = {
|
|
host = cfg.domain;
|
|
internal = "https://127.0.0.3:8443";
|
|
transport = "kanidm-https";
|
|
};
|
|
services.traefik.dynamicConfigOptions.http = mkIf (cfg.tls.keyPem == null) {
|
|
serversTransports."kanidm-https" = {
|
|
serverName = cfg.domain;
|
|
rootcas = [
|
|
"${tlsDir}/ca.pem"
|
|
];
|
|
};
|
|
};
|
|
|
|
})
|
|
];
|
|
|
|
}
|