Tomter Vel's own platform: Caddy, NATS, Kanidm and portal as containers
deploy / deploy (push) Canceled after 0s
deploy / deploy (push) Canceled after 0s
One host, four containers, content fetched from tomtervel/questions. bootstrap.sh renders configs from .env and recovers the Kanidm admin; kanidm-setup.sh creates the portal client and the desk groups. An optional runner profile lets the content repo's reload reach this host. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,28 @@
|
|||||||
|
# Copy to .env and fill in. Everything rendered from this (nats.conf,
|
||||||
|
# kanidm/server.toml, portal.env) is gitignored.
|
||||||
|
|
||||||
|
# Where the site and the identity provider are served. Both need an A
|
||||||
|
# record pointing at this host before the first start (Caddy gets the
|
||||||
|
# certificates over HTTP-01). tomtervel.no itself stays where it is
|
||||||
|
# until the vel decides to point the apex here.
|
||||||
|
PORTAL_HOST=portal.tomtervel.no
|
||||||
|
ID_HOST=id.tomtervel.no
|
||||||
|
|
||||||
|
# The portal version to run: a tag of https://project.uhhm.no/uhhm/portal
|
||||||
|
PORTAL_RELEASE=v0.3.36
|
||||||
|
|
||||||
|
# The content this instance serves, and reloads live on every push.
|
||||||
|
CONTENT_REPO=https://prosjekt.klingenbergbygg.no/tomtervel/questions
|
||||||
|
CONTENT_BRANCH=main
|
||||||
|
SITE_NAME=Tomter Vel
|
||||||
|
|
||||||
|
# Generated once by bootstrap.sh if left empty.
|
||||||
|
NATS_PASSWORD=
|
||||||
|
|
||||||
|
# Filled in by bootstrap.sh after it creates the Kanidm client.
|
||||||
|
OAUTH2_CLIENT_ID=tomtervel-portal
|
||||||
|
OAUTH2_CLIENT_SECRET=
|
||||||
|
|
||||||
|
# Only for the optional runner profile: a registration token from
|
||||||
|
# prosjekt.klingenbergbygg.no -> tomtervel org -> Settings -> Actions -> Runners.
|
||||||
|
RUNNER_REGISTRATION_TOKEN=
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
# Deploy from this repo: on the vel's own host, a runner registered
|
||||||
|
# against prosjekt.klingenbergbygg.no with the label `tomtervel`
|
||||||
|
# (the `runner` profile in compose.yml) checks out this repo and
|
||||||
|
# brings the stack up. Rolling out a new portal version is a commit
|
||||||
|
# that bumps PORTAL_RELEASE in .env.example and, on the host, in .env.
|
||||||
|
#
|
||||||
|
# Until that runner exists, this workflow queues and does nothing;
|
||||||
|
# deploy by hand with `git pull && docker compose up -d --build` on
|
||||||
|
# the host.
|
||||||
|
name: deploy
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
paths:
|
||||||
|
- compose.yml
|
||||||
|
- Caddyfile
|
||||||
|
- portal/**
|
||||||
|
- kanidm/server.toml.tpl
|
||||||
|
- nats/nats.conf.tpl
|
||||||
|
- .gitea/workflows/deploy.yml
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: tomtervel
|
||||||
|
steps:
|
||||||
|
- name: Pull and restart
|
||||||
|
run: |
|
||||||
|
set -eu
|
||||||
|
cd /srv/tomtervel/infrastructure
|
||||||
|
git pull --ff-only
|
||||||
|
sh bootstrap.sh
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
.env
|
||||||
|
portal.env
|
||||||
|
kanidm/server.toml
|
||||||
|
nats/nats.conf
|
||||||
|
certs/
|
||||||
|
.kanidm-recovered
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Public TLS for both hosts, real certificates from Let's Encrypt over
|
||||||
|
# HTTP-01: the DNS A records for ${PORTAL_HOST} and ${ID_HOST} must
|
||||||
|
# point at this host before the first start.
|
||||||
|
|
||||||
|
{$ID_HOST} {
|
||||||
|
# Kanidm serves its own (internal, self-signed) TLS; verification is
|
||||||
|
# skipped on the inside hop only.
|
||||||
|
reverse_proxy kanidm:8443 {
|
||||||
|
transport http {
|
||||||
|
tls_insecure_skip_verify
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log {
|
||||||
|
output file /data/id.log
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{$PORTAL_HOST} {
|
||||||
|
reverse_proxy portal:3000
|
||||||
|
log {
|
||||||
|
output file /data/portal.log
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
# 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](https://prosjekt.klingenbergbygg.no/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 docker (compose plugin) and openssl:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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
|
||||||
|
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.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
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. For this host, start the runner profile once with
|
||||||
|
a registration token from the tomtervel org's Actions settings:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
docker compose --profile runner up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
and give the content repo's `lint-and-reload.yml` a reload job with
|
||||||
|
`runs-on: tomtervel` that runs
|
||||||
|
`nats --server nats://portal:$NATS_PASSWORD@127.0.0.1:4222 pub portal.content.reload ""`.
|
||||||
|
Until then, `docker compose restart portal` picks up new content.
|
||||||
|
|
||||||
|
## Upgrading portal
|
||||||
|
|
||||||
|
Bump `PORTAL_RELEASE` in `.env` (a tag of uhhm/portal) and
|
||||||
|
`docker 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_data` volume: 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.
|
||||||
Executable
+61
@@ -0,0 +1,61 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# First start on a fresh host. Idempotent: rerunning renders configs
|
||||||
|
# again and skips what exists. Needs docker with the compose plugin,
|
||||||
|
# openssl, and DNS for PORTAL_HOST and ID_HOST already pointing here.
|
||||||
|
#
|
||||||
|
# cp .env.example .env # fill in the hosts
|
||||||
|
# sh bootstrap.sh
|
||||||
|
set -eu
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
[ -f .env ] || { echo "copy .env.example to .env and fill it in first"; exit 1; }
|
||||||
|
. ./.env
|
||||||
|
|
||||||
|
# A NATS password, once.
|
||||||
|
if [ -z "${NATS_PASSWORD:-}" ]; then
|
||||||
|
NATS_PASSWORD=$(openssl rand -base64 36 | tr -d '/+=' | cut -c1-40)
|
||||||
|
sed -i "s|^NATS_PASSWORD=.*|NATS_PASSWORD=$NATS_PASSWORD|" .env
|
||||||
|
echo "NATS_PASSWORD generated into .env"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Rendered configs (gitignored).
|
||||||
|
sed "s|\${ID_HOST}|$ID_HOST|g" kanidm/server.toml.tpl > kanidm/server.toml
|
||||||
|
sed "s|\${NATS_PASSWORD}|$NATS_PASSWORD|g" nats/nats.conf.tpl > nats/nats.conf
|
||||||
|
cat > portal.env <<EOF
|
||||||
|
NATS_URL=nats://portal:$NATS_PASSWORD@nats:4222
|
||||||
|
KANIDM_URL=https://$ID_HOST
|
||||||
|
OAUTH2_CLIENT_ID=$OAUTH2_CLIENT_ID
|
||||||
|
OAUTH2_CLIENT_SECRET=${OAUTH2_CLIENT_SECRET:-}
|
||||||
|
PUBLIC_URL=https://$PORTAL_HOST
|
||||||
|
COOKIE_SECURE=true
|
||||||
|
CONTENT_REPO=$CONTENT_REPO
|
||||||
|
CONTENT_BRANCH=$CONTENT_BRANCH
|
||||||
|
SITE_NAME=$SITE_NAME
|
||||||
|
EOF
|
||||||
|
chmod 600 portal.env
|
||||||
|
|
||||||
|
# Kanidm's internal certificate: Caddy holds the public one.
|
||||||
|
mkdir -p certs
|
||||||
|
if [ ! -f certs/kanidm-key.pem ]; then
|
||||||
|
openssl req -x509 -newkey rsa:2048 -nodes -days 3650 \
|
||||||
|
-subj "/CN=kanidm" -addext "subjectAltName=DNS:kanidm,DNS:$ID_HOST" \
|
||||||
|
-keyout certs/kanidm-key.pem -out certs/kanidm-chain.pem >/dev/null 2>&1
|
||||||
|
chmod 600 certs/kanidm-key.pem
|
||||||
|
echo "internal Kanidm certificate made"
|
||||||
|
fi
|
||||||
|
|
||||||
|
docker compose up -d --build
|
||||||
|
echo "containers up; Caddy is fetching certificates for $PORTAL_HOST and $ID_HOST"
|
||||||
|
|
||||||
|
# The Kanidm admin accounts exist only after the first start; their
|
||||||
|
# passwords are set by recovery. Do this once; the output is the
|
||||||
|
# password, shown once.
|
||||||
|
if [ ! -f .kanidm-recovered ]; then
|
||||||
|
echo
|
||||||
|
echo "=== Kanidm admin recovery (write these passwords down) ==="
|
||||||
|
docker compose exec kanidm kanidmd recover-account admin
|
||||||
|
docker compose exec kanidm kanidmd recover-account idm_admin
|
||||||
|
touch .kanidm-recovered
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo "Next: sh kanidm-setup.sh (log in as idm_admin, create the portal client and desk groups)"
|
||||||
+105
@@ -0,0 +1,105 @@
|
|||||||
|
# Tomter Vel's own platform: one host, four containers, everything
|
||||||
|
# behind Caddy. The content (pages, forms, desks) is not here: portal
|
||||||
|
# fetches it from tomtervel/questions on prosjekt.klingenbergbygg.no
|
||||||
|
# and hot-reloads it over NATS. This repo owns the host: identity,
|
||||||
|
# the bus, TLS, and which portal version runs.
|
||||||
|
#
|
||||||
|
# bootstrap.sh first time on a fresh host: renders configs from
|
||||||
|
# .env, makes Kanidm's internal cert, starts it all,
|
||||||
|
# recovers the Kanidm admin, creates the portal client
|
||||||
|
# and desk groups.
|
||||||
|
# docker compose up -d --build every time after that.
|
||||||
|
|
||||||
|
name: tomtervel
|
||||||
|
|
||||||
|
services:
|
||||||
|
caddy:
|
||||||
|
image: caddy:2
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
- "443:443/udp"
|
||||||
|
environment:
|
||||||
|
PORTAL_HOST: ${PORTAL_HOST}
|
||||||
|
ID_HOST: ${ID_HOST}
|
||||||
|
volumes:
|
||||||
|
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- caddy_data:/data
|
||||||
|
- caddy_config:/config
|
||||||
|
depends_on:
|
||||||
|
- portal
|
||||||
|
- kanidm
|
||||||
|
|
||||||
|
nats:
|
||||||
|
image: nats:2.14.6-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
command: ["-c", "/etc/nats/nats.conf"]
|
||||||
|
volumes:
|
||||||
|
- ./nats/nats.conf:/etc/nats/nats.conf:ro
|
||||||
|
- nats_data:/data
|
||||||
|
# Published so a runner or a person on the host can `nats pub
|
||||||
|
# portal.content.reload ""`; password-protected (see nats.conf).
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:4222:4222"
|
||||||
|
healthcheck:
|
||||||
|
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8222/healthz"]
|
||||||
|
interval: 10s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
|
||||||
|
kanidm:
|
||||||
|
image: kanidm/server:1.11.1
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
KANIDM_CONFIG_PATH: /data/server.toml
|
||||||
|
volumes:
|
||||||
|
- kanidm_data:/data
|
||||||
|
- ./kanidm/server.toml:/data/server.toml:ro
|
||||||
|
# Internal self-signed TLS: Caddy terminates the public
|
||||||
|
# certificate and proxies here without verification.
|
||||||
|
- ./certs/kanidm-chain.pem:/data/chain.pem:ro
|
||||||
|
- ./certs/kanidm-key.pem:/data/key.pem:ro
|
||||||
|
# No published ports: only Caddy talks to it.
|
||||||
|
|
||||||
|
portal:
|
||||||
|
build:
|
||||||
|
context: ./portal
|
||||||
|
args:
|
||||||
|
PORTAL_RELEASE: ${PORTAL_RELEASE}
|
||||||
|
restart: unless-stopped
|
||||||
|
env_file: portal.env
|
||||||
|
environment:
|
||||||
|
LEPTOS_SITE_ADDR: 0.0.0.0:3000
|
||||||
|
LEPTOS_SITE_ROOT: site
|
||||||
|
LEPTOS_HASH_FILES: "true"
|
||||||
|
depends_on:
|
||||||
|
nats:
|
||||||
|
condition: service_healthy
|
||||||
|
kanidm:
|
||||||
|
condition: service_started
|
||||||
|
|
||||||
|
# Optional: a Gitea Actions runner on this host, so the content repo's
|
||||||
|
# lint-and-reload can reach this NATS. Register it once against
|
||||||
|
# prosjekt.klingenbergbygg.no with the label `tomtervel`, then give
|
||||||
|
# the content repo a reload job with `runs-on: tomtervel`.
|
||||||
|
# docker compose --profile runner up -d
|
||||||
|
runner:
|
||||||
|
profiles: ["runner"]
|
||||||
|
image: gitea/act_runner:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
GITEA_INSTANCE_URL: https://prosjekt.klingenbergbygg.no
|
||||||
|
GITEA_RUNNER_REGISTRATION_TOKEN: ${RUNNER_REGISTRATION_TOKEN:-}
|
||||||
|
GITEA_RUNNER_NAME: tomtervel
|
||||||
|
GITEA_RUNNER_LABELS: tomtervel:host
|
||||||
|
volumes:
|
||||||
|
- runner_data:/data
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
caddy_data:
|
||||||
|
caddy_config:
|
||||||
|
nats_data:
|
||||||
|
kanidm_data:
|
||||||
|
runner_data:
|
||||||
Executable
+46
@@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# The portal's OAuth2 client and one Kanidm group per desk, mapped into
|
||||||
|
# the `groups` claim under the names the pages use in `qualifies`.
|
||||||
|
# Portal reads that claim at login (portal src/auth.rs). Rerunnable.
|
||||||
|
#
|
||||||
|
# Logs in first (interactive, idm_admin's password from bootstrap.sh),
|
||||||
|
# then writes the client secret into .env and portal.env and restarts
|
||||||
|
# the portal. Needs the kanidm CLI on this machine.
|
||||||
|
set -eu
|
||||||
|
cd "$(dirname "$0")"
|
||||||
|
. ./.env
|
||||||
|
K="kanidm -D idm_admin -H https://$ID_HOST"
|
||||||
|
C=$OAUTH2_CLIENT_ID
|
||||||
|
|
||||||
|
$K login
|
||||||
|
has_client() { $K system oauth2 get "$1" 2>/dev/null | grep -q '^name:'; }
|
||||||
|
has_group() { $K group get "$1" 2>/dev/null | grep -q '^name:'; }
|
||||||
|
|
||||||
|
has_client $C || $K system oauth2 create $C "$SITE_NAME" "https://$PORTAL_HOST"
|
||||||
|
$K system oauth2 add-redirect-url $C "https://$PORTAL_HOST/auth/callback" || true
|
||||||
|
|
||||||
|
# The desk groups: every group the content gates a directory on. Keep
|
||||||
|
# this list equal to the `qualifies` values under questions/.
|
||||||
|
has_group tomtervel_members || $K group create tomtervel_members
|
||||||
|
for g in kasserer styret trafikkomite lekeplasskomite arrangementskomite nabohjelp komiteer; do
|
||||||
|
has_group tomtervel_$g || $K group create tomtervel_$g
|
||||||
|
$K group add-members tomtervel_members tomtervel_$g
|
||||||
|
done
|
||||||
|
|
||||||
|
# Portal asks for openid, profile and email; groups arrive as a claim.
|
||||||
|
$K system oauth2 update-scope-map $C tomtervel_members openid profile email
|
||||||
|
for g in kasserer styret trafikkomite lekeplasskomite arrangementskomite nabohjelp komiteer; do
|
||||||
|
$K system oauth2 update-claim-map $C groups tomtervel_$g $g
|
||||||
|
done
|
||||||
|
$K system oauth2 update-claim-map-join $C groups array
|
||||||
|
|
||||||
|
secret=$($K system oauth2 show-basic-secret $C 2>/dev/null | tail -1)
|
||||||
|
sed -i "s|^OAUTH2_CLIENT_SECRET=.*|OAUTH2_CLIENT_SECRET=$secret|" .env portal.env
|
||||||
|
docker compose restart portal
|
||||||
|
echo "client $C configured; portal restarted with its secret"
|
||||||
|
echo
|
||||||
|
echo "Give people their desk (membership is read at login):"
|
||||||
|
echo " $K group add-members tomtervel_styret <person>"
|
||||||
|
echo "Create a person:"
|
||||||
|
echo " $K person create <name> '<Display Name>' && $K person update <name> --mail <email>"
|
||||||
|
echo " $K person credential create-reset-token <name>"
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Kanidm server configuration. bootstrap.sh renders this into
|
||||||
|
# server.toml from .env; edit the template, not the rendered file.
|
||||||
|
# Reference: https://kanidm.github.io/kanidm/stable/server_configuration.html
|
||||||
|
version = "2"
|
||||||
|
|
||||||
|
bindaddress = "0.0.0.0:8443"
|
||||||
|
|
||||||
|
# Internal self-signed pair made by bootstrap.sh; Caddy terminates the
|
||||||
|
# public certificate and proxies here with verification disabled.
|
||||||
|
tls_chain = "/data/chain.pem"
|
||||||
|
tls_key = "/data/key.pem"
|
||||||
|
|
||||||
|
db_path = "/data/kanidm.db"
|
||||||
|
db_fs_type = "other"
|
||||||
|
db_arc_size = 2048
|
||||||
|
|
||||||
|
log_level = "info"
|
||||||
|
|
||||||
|
# domain must equal the DNS name Kanidm is served at.
|
||||||
|
domain = "${ID_HOST}"
|
||||||
|
origin = "https://${ID_HOST}"
|
||||||
|
|
||||||
|
# Trust X-Forwarded-For from Caddy on the compose network.
|
||||||
|
[http_client_address_info]
|
||||||
|
x-forward-for = ["172.16.0.0/12"]
|
||||||
|
|
||||||
|
[online_backup]
|
||||||
|
path = "/data/backups/"
|
||||||
|
schedule = "00 22 * * *"
|
||||||
|
versions = 7
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# NATS with JetStream: portal's records, events and projections live
|
||||||
|
# here. bootstrap.sh renders this into nats.conf from .env.
|
||||||
|
port: 4222
|
||||||
|
http_port: 8222
|
||||||
|
|
||||||
|
max_payload: 8388608
|
||||||
|
max_connections: 1000
|
||||||
|
|
||||||
|
# User and password rather than a token: URL credentials
|
||||||
|
# (nats://user:pass@host) behave the same in every client library.
|
||||||
|
authorization {
|
||||||
|
users = [
|
||||||
|
{ user: "portal", password: "${NATS_PASSWORD}" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
jetstream {
|
||||||
|
store_dir: /data
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Portal, from the release tarball uhhm/portal publishes on
|
||||||
|
# project.uhhm.no (the same artifact the bare-metal instances run).
|
||||||
|
# PORTAL_RELEASE in .env pins the version; bumping it and rebuilding is
|
||||||
|
# the whole upgrade.
|
||||||
|
FROM debian:bookworm-slim
|
||||||
|
ARG PORTAL_RELEASE
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends ca-certificates curl \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
WORKDIR /app
|
||||||
|
RUN curl -sfL "https://project.uhhm.no/uhhm/portal/releases/download/${PORTAL_RELEASE}/portal-${PORTAL_RELEASE}.tar.gz" \
|
||||||
|
| tar -xz -C /app \
|
||||||
|
&& test -x /app/portal
|
||||||
|
# A non-root user; the image ships nothing writable it needs.
|
||||||
|
RUN useradd --system --no-create-home portal
|
||||||
|
USER portal
|
||||||
|
EXPOSE 3000
|
||||||
|
CMD ["/app/portal"]
|
||||||
Reference in New Issue
Block a user