Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08915b541 |
No files matched your search
Generated
+18
-18
@@ -45,11 +45,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792098797,
|
"lastModified": 1791293500,
|
||||||
"narHash": "sha256-u4DIIZgznEGzByxXrxWCXK19VcMtTBmi/M9gZ7Jk9bM=",
|
"narHash": "sha256-3hwJd2/vG/VYV1P/g7oDgdbNsWODhN5F//Od+hPMm2k=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "640725978b99076e18e069f6e7d2959124584f8f",
|
"rev": "0e11b7bcba1d74ef8ea10bfdfb1e1a5811a21087",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -67,11 +67,11 @@
|
|||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1791640927,
|
"lastModified": 1791127101,
|
||||||
"narHash": "sha256-7eCfJ8ZUodiap2b/JE15C4MTBP1sp647CUhF7CuWB1s=",
|
"narHash": "sha256-SmrBBhcv9iNHA2vlafi7Zpe8bkcsMjVNSmCCVf5/jrQ=",
|
||||||
"owner": "LLukas22",
|
"owner": "LLukas22",
|
||||||
"repo": "Jellyswarrm",
|
"repo": "Jellyswarrm",
|
||||||
"rev": "cf1a1851d873bcd4d2198a50a1f9d2c7c5e5f99c",
|
"rev": "0b4301c6c74efff9c3634296913032579c98faf6",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -85,11 +85,11 @@
|
|||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1791721963,
|
"lastModified": 1791128201,
|
||||||
"narHash": "sha256-BYhx89XJltnRyQeuDKHVL5eSqbbfAcOKuDts7QRsrxQ=",
|
"narHash": "sha256-rlsYY/Dg/LhbIj51Aa6TLvRpZeb3KZgUTd7llQ5XcJE=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixos-hardware",
|
"repo": "nixos-hardware",
|
||||||
"rev": "60a624466c08787ee8303393b7d4752d1826f14f",
|
"rev": "31cc5f4d9b9ba601071e8b8504601b9b176e2756",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -114,11 +114,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-master": {
|
"nixpkgs-master": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792194661,
|
"lastModified": 1791296764,
|
||||||
"narHash": "sha256-hlvx30U4H8lYBuoycARFOicWC+yu2tCNdSpojTWLi+E=",
|
"narHash": "sha256-N2b6Uxdr1MFhscEPqDyntTTtGZASgq3XYE0ujRzfeNI=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e7fccb1ad3ed343190cf4409aa1ec21543b40090",
|
"rev": "a15afac8691bd24f746255ffbd76306693e4cf7a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -146,11 +146,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1791956154,
|
"lastModified": 1791260994,
|
||||||
"narHash": "sha256-jBYGPi4udQ9uo4KQp9m2J0546nJVQitG15gvZSaVVAs=",
|
"narHash": "sha256-Miqqk/ammqnTUxaoCyvtwoPeLbI1ksFyLxi/CazZpWY=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "55bfb4d7437bd4e09ab7cba58a43bd68eb310044",
|
"rev": "151fa4e8ddfdd8dd25d945ad94ed54a13de9f6e4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -201,11 +201,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1792056300,
|
"lastModified": 1791103873,
|
||||||
"narHash": "sha256-YU1LjDe1o2Ymg5zEiCHbmMXLkDCtxHQRiVqZ3nC5GGk=",
|
"narHash": "sha256-nFxM+pKoZ8LJAEnUXARyCaOAloWgaW9kZQOSjWzKcTE=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "44931708c62937c2b1bf7953902989fa5bb9ee1b",
|
"rev": "dcd241ba97088c22569d1573286e1b9daad340c0",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -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;
|
grafana = 3000;
|
||||||
jellyfin_http = 8096;
|
jellyfin_http = 8096;
|
||||||
jellyswarrm = 3100;
|
jellyswarrm = 3100;
|
||||||
|
kanidm = 8443;
|
||||||
nix_binary_cache = 5000;
|
nix_binary_cache = 5000;
|
||||||
ollama = 11434;
|
ollama = 11434;
|
||||||
open_webui = 8080;
|
open_webui = 8080;
|
||||||
@@ -28,6 +29,7 @@ in
|
|||||||
ports.gems
|
ports.gems
|
||||||
ports.jellyfin_http
|
ports.jellyfin_http
|
||||||
ports.jellyswarrm
|
ports.jellyswarrm
|
||||||
|
ports.kanidm
|
||||||
ports.nix_binary_cache
|
ports.nix_binary_cache
|
||||||
ports.share
|
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
|
#Application Setup
|
||||||
frontend ContentSwitching
|
frontend ContentSwitching
|
||||||
bind [::]:80 v4v6
|
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
|
mode http
|
||||||
|
|
||||||
# ACME challenge routing (must be first)
|
# 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)
|
# Host ACLs (defined early so rate-limiting can scope to a single vhost)
|
||||||
acl host_audiobookshelf hdr(host) -i audiobookshelf.tmmworkshop.com
|
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_cache hdr(host) -i cache.tmmworkshop.com
|
||||||
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
|
acl host_jellyfin hdr(host) -i jellyfin.tmmworkshop.com
|
||||||
acl host_share hdr(host) -i share.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 acme_challenge if is_acme
|
||||||
use_backend audiobookshelf_nodes if host_audiobookshelf
|
use_backend audiobookshelf_nodes if host_audiobookshelf
|
||||||
|
use_backend kanidm if host_auth
|
||||||
use_backend cache_nodes if host_cache
|
use_backend cache_nodes if host_cache
|
||||||
use_backend jellyfin if host_jellyfin
|
use_backend jellyfin if host_jellyfin
|
||||||
use_backend share_nodes if host_share
|
use_backend share_nodes if host_share
|
||||||
@@ -141,6 +143,15 @@ backend audiobookshelf_nodes
|
|||||||
mode http
|
mode http
|
||||||
server server jeeves:8000
|
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
|
backend cache_nodes
|
||||||
mode http
|
mode http
|
||||||
server server jeeves:5000
|
server server jeeves:5000
|
||||||
|
|||||||
Reference in new issue
Block a user