machines/nix/systems/aarch64-linux/security.kaareskovgaard.net/kanidm.nix
Kaare Hoff Skovgaard cd8a0db1b6
Some checks failed
/ dev-shell (push) Successful in 1m8s
/ check (push) Failing after 1m29s
/ systems (push) Failing after 33s
/ rust-packages (push) Successful in 3m40s
/ terraform-providers (push) Successful in 5m13s
Begin reverting back to simple-nixos-mailserver
It appears I can get app passwords with kanidm and ldap
so just going to a more stable, probably supported setup,
should be good.
2025-07-28 12:02:24 +02:00

341 lines
11 KiB
Nix

{
config,
pkgs,
...
}:
let
domain = "login.kaareskovgaard.net";
bootstrapping = config.khscodes."security.kaareskovgaard.net".bootstrap.enable;
openbaoDomain = config.khscodes.infrastructure.openbao.domain;
openbaoAllowedRedirectUrls = [
"https://${openbaoDomain}/ui/vault/auth/kanidm/oidc/callback"
"https://${openbaoDomain}/kanidm/callback"
];
kanidm-reset-password = pkgs.writeShellApplication {
name = "kanidm-reset-password";
runtimeInputs = [
config.services.kanidm.package
pkgs.jq
pkgs.gnugrep
pkgs.expect
];
text = ''
username="''${1:-}"
if [[ "$username" == "" ]]; then
>&2 echo "Usage kanidm-reset-password <username>"
exit 1
fi
echo "Resetting password for idm_admin, please provide sudo password"
password="$(sudo -u kanidm kanidmd recover-account -c /etc/kanidm/server.toml -o json idm_admin 2> /dev/null | grep '^{' | jq --raw-output '.password')"
expect <<EOD
spawn kanidm login --url https://login.kaareskovgaard.net --name idm_admin
expect "Enter password"
send "$password\n"
expect eof
EOD
kanidm person credential create-reset-token --url https://login.kaareskovgaard.net "$username"
trap "kanidm logout --url https://login.kaareskovgaard.net" EXIT
'';
};
in
{
imports = [ ./kanidm_application.nix ];
config = {
khscodes.security.kanidm.applications = {
openbao = {
allowedRedirectUris = openbaoAllowedRedirectUrls;
landingUri = "https://${openbaoDomain}";
displayName = "OpenBAO";
scopeMaps = {
"openbao_admin" = [
"profile"
"email"
"openid"
];
};
claimMaps.groups = {
joinType = "array";
valuesByGroup = {
"openbao_admin" = [
"openbao_writer"
"openbao_cli_writer"
];
};
};
};
openbao-cli = {
terranixName = "openbao_cli";
allowedRedirectUris = [ "http://localhost:8250/oidc/callback" ];
landingUri = "http://localhost:8250";
displayName = "OpenBAO CLI";
scopeMaps = {
"openbao_admin" = [
"profile"
"email"
"openid"
];
};
claimMaps.groups = {
joinType = "array";
valuesByGroup = {
"openbao_admin" = [
"openbao_writer"
];
};
};
};
monitoring = {
allowedRedirectUris = [ "https://monitoring.kaareskovgaard.net/login/generic_oauth" ];
landingUri = "http://monitoring.kaareskovgaard.net";
displayName = "Monitoring";
scopeMaps = {
"openbao_admin" = [
"profile"
"email"
"openid"
];
};
};
forgejo = {
allowedRedirectUris = [ "https://kas.codes/user/oauth2/Kanidm/callback" ];
landingUri = "http://kas.codes";
displayName = "KAS: Codes";
scopeMaps = {
"forgejo_user" = [
"profile"
"email"
"openid"
];
};
claimMaps.groups = {
joinType = "array";
valuesByGroup = {
"forgejo_comitter" = [
"comitter"
];
"forgejo_admin" = [
"admin"
];
};
};
};
stalwart = {
allowedRedirectUris = [ "https://mail.kaareskovgaard.net/index.php/login/oauth" ];
landingUri = "https://mail.kaareskovgaard.net";
displayName = "Mail";
allowInsecureClientDisablePkce = true;
scopeMaps = {
"mail_user" = [
"profile"
"email"
"openid"
];
};
};
};
services.kanidm = {
enableServer = true;
# Verify upgrade will be successful by running `kanidmd domain upgrade-check` before bumping package version.
package = pkgs.kanidmWithSecretProvisioning_1_6;
serverSettings = {
inherit domain;
tls_chain = "${config.security.acme.certs.${domain}.directory}/fullchain.pem";
tls_key = "${config.security.acme.certs.${domain}.directory}/key.pem";
origin = "https://${domain}";
trust_x_forward_for = true;
};
provision = {
enable = true;
idmAdminPasswordFile = null;
adminPasswordFile = null;
acceptInvalidCerts = true;
persons.khs = {
present = true;
mailAddresses = [
"kaare@kaareskovgaard.net"
"kaare@agerlin-skovgaard.dk"
"kaare@agerlinskovgaard.dk"
];
legalName = "Kaare Skovgaard";
displayName = "khs";
};
groups.openbao_admin = {
present = true;
members = [ "khs" ];
};
groups.forgejo_user = {
present = true;
members = [ "khs" ];
};
groups.forgejo_comitter = {
present = true;
members = [ "khs" ];
};
groups.forgejo_admin = {
present = true;
members = [ "khs" ];
};
groups.mail_user = {
present = true;
members = [ "khs" ];
};
};
};
khscodes.infrastructure.provisioning.post.modules = [
(
{ ... }:
{
khscodes.vault = {
enable = true;
mount.kanidm = {
path = "kanidm";
type = "kv";
options = {
version = "2";
};
description = "Secrets used for kanidm";
};
};
}
)
# Sets up OIDC authentication within OpenBAO.
# OpenBAO queries the openid url for its configuration when adding it, so it is not possible,
# to add this before
(
if bootstrapping then
{ }
else
{
resource.vault_jwt_auth_backend.kanidm_cli = {
description = "Kanidm cli auth backend";
path = "oidc";
type = "oidc";
oidc_discovery_url = "https://${domain}/oauth2/openid/openbao-cli";
oidc_client_id = "openbao-cli";
oidc_client_secret = "\${ resource.random_password.openbao_cli_secret.result }";
default_role = "kanidm_cli_writer";
jwt_supported_algs = [
"ES256"
];
tune = [
{
listing_visibility = "hidden";
default_lease_ttl = "2h";
max_lease_ttl = "2h";
token_type = "default-service";
passthrough_request_headers = [ ];
allowed_response_headers = [ ];
audit_non_hmac_request_keys = [ ];
audit_non_hmac_response_keys = [ ];
}
];
};
resource.vault_jwt_auth_backend.kanidm = {
description = "Kanidm auth backend";
path = "kanidm";
type = "oidc";
oidc_discovery_url = "https://${domain}/oauth2/openid/openbao";
oidc_client_id = "openbao";
oidc_client_secret = "\${ resource.random_password.openbao_secret.result }";
default_role = "kanidm_writer";
jwt_supported_algs = [
"ES256"
];
tune = [
{
listing_visibility = "unauth";
default_lease_ttl = "2h";
max_lease_ttl = "2h";
token_type = "default-service";
passthrough_request_headers = [ ];
allowed_response_headers = [ ];
audit_non_hmac_request_keys = [ ];
audit_non_hmac_response_keys = [ ];
}
];
};
resource.vault_jwt_auth_backend_role.kanidm_cli_writer = {
backend = "\${ resource.vault_jwt_auth_backend.kanidm_cli.path }";
role_name = "kanidm_cli_writer";
bound_audiences = [ "openbao-cli" ];
allowed_redirect_uris = [ "http://localhost:8250/oidc/callback" ];
user_claim = "sub";
token_policies = [ "writer" ];
groups_claim = "groups";
oidc_scopes = [
"openid"
"profile"
"email"
];
};
resource.vault_jwt_auth_backend_role.kanidm_writer = {
backend = "\${ resource.vault_jwt_auth_backend.kanidm.path }";
role_name = "kanidm_writer";
bound_audiences = [ "openbao" ];
allowed_redirect_uris = openbaoAllowedRedirectUrls;
user_claim = "sub";
token_policies = [ "writer" ];
groups_claim = "groups";
oidc_scopes = [
"openid"
"profile"
"email"
];
};
resource.vault_identity_group.kanidm_writer = {
name = "kanidm_writer";
policies = [ "writer" ];
type = "external";
};
resource.vault_identity_group.kanidm_cli_writer = {
name = "kanidm_cli_writer";
policies = [ "writer" ];
type = "external";
};
resource.vault_identity_group_alias.oidc_writer = {
name = "openbao_cli_writer";
mount_accessor = "\${ vault_jwt_auth_backend.kanidm_cli.accessor }";
canonical_id = "\${ vault_identity_group.kanidm_cli_writer.id }";
};
resource.vault_identity_group_alias.kanidm_writer = {
name = "openbao_writer";
mount_accessor = "\${ vault_jwt_auth_backend.kanidm.accessor }";
canonical_id = "\${ vault_identity_group.kanidm_writer.id }";
};
resource.vault_policy.writer = {
name = "writer";
policy = ''
path "*" {
capabilities = ["create", "update", "patch", "read", "delete", "list"]
}
'';
};
}
)
];
security.acme.certs.${domain}.reloadServices = [ "kanidm.service" ];
# Allow kanidm to read the certificate file
users.groups.nginx.members = [ "kanidm" ];
khscodes.services.nginx.virtualHosts.${domain} = {
locations."/" = {
proxyPass = "https://${config.services.kanidm.serverSettings.bindaddress}/";
recommendedProxySettings = true;
};
};
environment.systemPackages = [
kanidm-reset-password
];
};
}