- bootstrap.sh / kanidm-setup.sh: docker compose -> podman compose; run rootful (as root) so Caddy can bind 80/443 and Kanidm sees a stable source IP - compose.yml: remove the in-compose act_runner (it mounted docker.sock) — the host gitea-runner already covers it; pin the project network to 172.18.0.0/16 so Kanidm's X-Forwarded-For trust (172.16/12) stays valid under Podman, whose default pool hands out unmatched 10.89.x addresses - README / deploy.yml: podman + host-runner notes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
3.5 KiB
Tomter Vel — infrastructure
Everything the vel's own site needs on one host, as containers: Caddy for TLS, NATS with JetStream for the records, Kanidm for who is who, and portal, the site itself. The content (pages, forms, desks) lives in tomtervel/questions and is fetched from there; this repo owns the host.
Internet ──► Caddy (Let's Encrypt)
├── PORTAL_HOST ──► portal:3000 ──► NATS (records) + Kanidm (login)
└── ID_HOST ─────► kanidm:8443 (internal TLS)
Today the vel's draft site runs on Klingenberg Bygg's host as vel.klingenbergbygg.no, sharing that host's Kanidm and NATS. This repo is the same site on a host of the vel's own, with a Kanidm of its own, so nothing about the vel's members or records depends on anyone else.
First start
On a fresh Linux host with podman + podman-compose and openssl (run rootful, i.e. as root, so Caddy can bind 80/443 and Kanidm sees a stable source IP):
git clone https://prosjekt.klingenbergbygg.no/tomtervel/infrastructure /srv/tomtervel/infrastructure
cd /srv/tomtervel/infrastructure
cp .env.example .env # set PORTAL_HOST and ID_HOST; DNS must point here
sudo sh bootstrap.sh # renders configs, makes the internal cert, starts, recovers Kanidm admin
sh kanidm-setup.sh # logs in, creates the portal client and desk groups, restarts portal
bootstrap.sh prints the admin and idm_admin passwords once;
write them down. After kanidm-setup.sh, https://PORTAL_HOST serves the
site and https://ID_HOST is the login.
People and desks
Each group in the content (qualifies: under questions/) is a
Kanidm group tomtervel_<group>, all members of tomtervel_members.
Someone in tomtervel_styret logs in and sees the board's desk.
kanidm -D idm_admin -H https://ID_HOST person create kari "Kari Lien"
kanidm -D idm_admin -H https://ID_HOST person update kari --mail kari@example.no
kanidm -D idm_admin -H https://ID_HOST person credential create-reset-token kari
kanidm -D idm_admin -H https://ID_HOST group add-members tomtervel_styret kari
Membership is read at login; someone added while logged in logs out and in again.
Content changes
A push to tomtervel/questions is linted on push and tells the portal
to reload over NATS. That reload reaches the host it runs on: today
Klingenberg Bygg's. On this host the runner is a host service
(pacman -S gitea-runner, registered against prosjekt.klingenbergbygg.no) —
not an in-compose container — so it reaches NATS on the published
127.0.0.1:4222. Give the content repo's lint-and-reload.yml a reload job
whose runs-on matches that runner's host label, running
nats --server nats://portal:$NATS_PASSWORD@127.0.0.1:4222 pub portal.content.reload "".
Until then, podman compose restart portal picks up new content.
Upgrading portal
Bump PORTAL_RELEASE in .env (a tag of uhhm/portal) and
podman compose up -d --build portal. Keep IRIS_RELEASE in the
content repo's workflow matched to it.
Backups
- Kanidm writes a nightly backup into its volume (
/data/backups, seven kept); copy that directory off the host. - NATS JetStream data is the
nats_datavolume: every record ever submitted and every state change. Snapshot the volume. - Caddy's certificates regenerate; nothing to keep.
What is not here
Mail (a person's reset link is a token you hand them), monitoring, and the vel's current website at tomtervel.no, which stays where it is until the vel points the apex at PORTAL_HOST.