feat(kanidm): add Kanidm service configuration and documentation

This commit is contained in:
Richie committed 2026-10-16 21:16:29 -04:00
1 parent 876683066c
commit d08915b541
4 files changed
+211 -1

No files matched your search

+96
View File
@@ -0,0 +1,96 @@
# Kanidm on Jeeves
Kanidm is served at **https://auth.tmmworkshop.com**. Portal terminates public
HTTPS and forwards requests over Tailscale to Jeeves on port 8443, verifying
Jeeves's TLS certificate. Only the Tailscale interface exposes that backend port.
The service module is automatically imported by `services/default.nix`.
`services/kanidm.nix` pins Kanidm to the 1.11 release series; review the
[upgrade guide](https://kanidm.github.io/kanidm/stable/server_updates.html)
before changing that series. The flake lock determines the patch release.
## Before deployment
1. Point `auth.tmmworkshop.com` at Portal's public address, using the same DNS
arrangement as `gitea.tmmworkshop.com`. Port 80 must reach Portal for ACME.
2. Enable [HTTPS certificates in Tailscale](https://tailscale.com/kb/1153/enabling-https).
Jeeves must be able to obtain a certificate for `jeeves.taile39c31.ts.net`.
3. Allow Portal to reach Jeeves on TCP 8443 in your Tailscale policy.
The trusted proxy address in `services/kanidm.nix` is Portal's current Tailscale
IPv4 address, `100.80.102.33`. Update it if Portal is re-enrolled with a new address.
HAProxy connects over IPv4 and replaces incoming `X-Forwarded-For` headers.
## Deploy
Ensure the new Nix file is tracked by Git before using this Git-backed flake.
From this repository, using the existing SSH aliases:
```sh
nixos-rebuild switch --flake .#jeeves --target-host jeeves --sudo
nixos-rebuild switch --flake .#portal-1 --target-host portal-1 --sudo
```
Deploy Jeeves first, then Portal. Portal manages the public Let's Encrypt
certificate. `kanidm-tls.service` on Jeeves obtains the backend certificate and
checks for renewal daily, restarting Kanidm only when the certificate changes.
Private keys are generated at runtime and never stored in this repository or
the Nix store.
On Jeeves, check startup:
```sh
sudo systemctl status kanidm-tls.service kanidm.service
sudo journalctl -u kanidm-tls.service -u kanidm.service -n 100
```
If the initial certificate request fails, fix the Tailscale HTTPS configuration
and run `sudo systemctl restart kanidm.service`; its dependency retries the
certificate request. A successful oneshot certificate service normally appears
as inactive after finishing.
After both hosts are deployed:
```sh
curl --fail https://auth.tmmworkshop.com/status
```
## First login
On Jeeves, recover the initial administrator passwords interactively:
```sh
sudo kanidmd recover-account admin
sudo kanidmd recover-account idm_admin
kanidm login --name idm_admin
kanidm person create richie "Richie"
kanidm person credential create-reset-token richie
```
Store the recovered passwords securely. Follow the reset URL to enroll your
personal account's passkey. `admin` manages the Kanidm server; `idm_admin`
manages people, groups, and application integrations. Creating `richie` does
not automatically grant administrator rights. The client is installed and
configured to use `https://auth.tmmworkshop.com`.
Register each website as its own OAuth2/OIDC client and each backend identity
as a service account, then grant application scopes through groups. This
deployment does not create application clients or change existing sites' login
settings. See the [OAuth2 guide](https://kanidm.github.io/kanidm/stable/integrations/oauth2.html)
and [service-account guide](https://kanidm.github.io/kanidm/stable/accounts/service_accounts.html).
## Storage and recovery
- Database: `/var/lib/kanidm/kanidm.db` on Jeeves's existing ZFS `/var` filesystem.
- Online backups: `/var/lib/kanidm/backups`, daily at 02:30, retaining 14.
- Backend certificate and key: `/var/lib/kanidm-tls`, readable by Kanidm.
- Server configuration: `/etc/kanidm/server.toml`.
The existing `root_pool/var` snapshot policy covers the database and backups.
Copy backups off Jeeves as part of disaster recovery; local snapshots do not
protect against loss of the host. Follow Kanidm's
[backup and restore instructions](https://kanidm.github.io/kanidm/stable/backup_and_restore.html).
The configured identity domain and origin are both `auth.tmmworkshop.com`.
Changing them after enrollment can affect passkeys and application integrations;
follow the [domain rename guide](https://kanidm.github.io/kanidm/stable/domain_rename.html).
+2
View File
@@ -8,6 +8,7 @@ let
grafana = 3000;
jellyfin_http = 8096;
jellyswarrm = 3100;
kanidm = 8443;
nix_binary_cache = 5000;
ollama = 11434;
open_webui = 8080;
@@ -28,6 +29,7 @@ in
ports.gems
ports.jellyfin_http
ports.jellyswarrm
ports.kanidm
ports.nix_binary_cache
ports.share
];
+101
View File
@@ -0,0 +1,101 @@
{ config, pkgs, ... }:
let
certificateDir = "/var/lib/kanidm-tls";
in
{
services.kanidm = {
# Pin the minor release: Kanidm upgrades require following its upgrade guide.
package = pkgs.kanidm_1_11;
server = {
enable = true;
settings = {
domain = "auth.tmmworkshop.com";
origin = "https://auth.tmmworkshop.com";
bindaddress = "0.0.0.0:8443";
db_fs_type = "zfs";
tls_chain = "${certificateDir}/certificate.pem";
tls_key = "${certificateDir}/private-key.pem";
# Only Portal may supply the original client address. HAProxy replaces
# X-Forwarded-For, so clients cannot supply a forged address themselves.
http_client_address_info.x-forward-for = [ "100.80.102.33" ];
# /var/lib/kanidm is on root_pool/var, which is already snapshotted.
online_backup = {
schedule = "30 2 * * *";
versions = 14;
};
};
};
client = {
enable = true;
settings.uri = "https://auth.tmmworkshop.com";
};
};
systemd = {
services = {
kanidm = {
requires = [ "kanidm-tls.service" ];
after = [ "kanidm-tls.service" ];
};
# Kanidm requires TLS even behind HAProxy. Use Jeeves's Tailscale
# certificate so Portal can verify both the CA and the backend hostname.
# HTTPS certificates must be enabled in the Tailscale admin console.
kanidm-tls = {
description = "Obtain and renew Kanidm's backend TLS certificate";
requires = [ "tailscaled.service" ];
wants = [ "tailscaled-autoconnect.service" ];
after = [
"tailscaled.service"
"tailscaled-autoconnect.service"
];
path = [
config.services.tailscale.package
pkgs.coreutils
pkgs.systemd
];
serviceConfig = {
Type = "oneshot";
User = "root";
Group = "kanidm";
StateDirectory = "kanidm-tls";
StateDirectoryMode = "0750";
RuntimeDirectory = "kanidm-tls";
RuntimeDirectoryMode = "0700";
UMask = "0077";
TimeoutStartSec = "2min";
};
script = ''
set -euo pipefail
tailscale cert --min-validity 720h \
--cert-file /run/kanidm-tls/certificate.pem \
--key-file /run/kanidm-tls/private-key.pem \
jeeves.taile39c31.ts.net
if ! cmp -s /run/kanidm-tls/certificate.pem ${certificateDir}/certificate.pem \
|| ! cmp -s /run/kanidm-tls/private-key.pem ${certificateDir}/private-key.pem; then
install -m 0640 -o root -g kanidm /run/kanidm-tls/certificate.pem ${certificateDir}/certificate.pem
install -m 0640 -o root -g kanidm /run/kanidm-tls/private-key.pem ${certificateDir}/private-key.pem
# The NixOS service bind-mounts the certificate files, so restart to
# pick up renewals. Do not wait on a service that depends on this one.
if systemctl is-active --quiet kanidm.service; then
systemctl --no-block try-restart kanidm.service
fi
fi
'';
};
};
timers.kanidm-tls = {
wantedBy = [ "timers.target" ];
timerConfig = {
OnCalendar = "daily";
Persistent = true;
RandomizedDelaySec = "1h";
};
};
};
}