Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08915b541 |
No files matched your search
@@ -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).
|
||||
@@ -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
|
||||
];
|
||||
|
||||
@@ -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";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -32,7 +32,7 @@ defaults
|
||||
#Application Setup
|
||||
frontend ContentSwitching
|
||||
bind [::]:80 v4v6
|
||||
bind [::]:443 v4v6 ssl crt /var/lib/acme/audiobookshelf.tmmworkshop.com/full.pem crt /var/lib/acme/cache.tmmworkshop.com/full.pem crt /var/lib/acme/gems.tmmworkshop.com/full.pem crt /var/lib/acme/jellyfin.tmmworkshop.com/full.pem crt /var/lib/acme/share.tmmworkshop.com/full.pem crt /var/lib/acme/gitea.tmmworkshop.com/full.pem crt
|
||||
bind [::]:443 v4v6 ssl crt /var/lib/acme/audiobookshelf.tmmworkshop.com/full.pem crt /var/lib/acme/auth.tmmworkshop.com/full.pem crt /var/lib/acme/cache.tmmworkshop.com/full.pem crt /var/lib/acme/gems.tmmworkshop.com/full.pem crt /var/lib/acme/jellyfin.tmmworkshop.com/full.pem crt /var/lib/acme/share.tmmworkshop.com/full.pem crt /var/lib/acme/gitea.tmmworkshop.com/full.pem
|
||||
mode http
|
||||
|
||||
# ACME challenge routing (must be first)
|
||||
@@ -40,6 +40,7 @@ frontend ContentSwitching
|
||||
|
||||
# Host ACLs (defined early so rate-limiting can scope to a single vhost)
|
||||
acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com
|
||||
acl host_auth hdr(host) -i auth.tmmworkshop.com
|
||||
acl host_cache hdr(host) -i cache.tmmworkshop.com
|
||||
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
|
||||
acl host_share hdr(host) -i share.tmmworkshop.com
|
||||
@@ -111,6 +112,7 @@ frontend ContentSwitching
|
||||
|
||||
use_backend acme_challenge if is_acme
|
||||
use_backend audiobookshelf_nodes if host_audiobookshelf
|
||||
use_backend kanidm if host_auth
|
||||
use_backend cache_nodes if host_cache
|
||||
use_backend jellyfin if host_jellyfin
|
||||
use_backend share_nodes if host_share
|
||||
@@ -141,6 +143,15 @@ backend audiobookshelf_nodes
|
||||
mode http
|
||||
server server jeeves:8000
|
||||
|
||||
backend kanidm
|
||||
mode http
|
||||
http-request set-header X-Forwarded-For %[src]
|
||||
http-request set-header X-Forwarded-Proto https
|
||||
option httpchk
|
||||
http-check send meth GET uri /status ver HTTP/1.1 hdr Host auth.tmmworkshop.com
|
||||
http-check expect status 200
|
||||
server kanidm jeeves.taile39c31.ts.net:8443 ssl verify required ca-file /etc/ssl/certs/ca-certificates.crt verifyhost jeeves.taile39c31.ts.net sni str(jeeves.taile39c31.ts.net) check check-sni jeeves.taile39c31.ts.net resolve-prefer ipv4
|
||||
|
||||
backend cache_nodes
|
||||
mode http
|
||||
server server jeeves:5000
|
||||
|
||||
Reference in new issue
Block a user