2026-09-22 19:06:31 +02:00
|
|
|
#!/bin/sh
|
|
|
|
|
# First start on a fresh host. Idempotent: rerunning renders configs
|
2026-09-28 11:15:29 +02:00
|
|
|
# again and skips what exists. Needs podman + podman-compose and openssl,
|
|
|
|
|
# and DNS for PORTAL_HOST and ID_HOST already pointing here. Run rootful
|
|
|
|
|
# (as root) so Caddy can bind 80/443 and Kanidm sees a stable source IP.
|
2026-09-22 19:06:31 +02:00
|
|
|
#
|
|
|
|
|
# cp .env.example .env # fill in the hosts
|
2026-09-28 11:15:29 +02:00
|
|
|
# sudo sh bootstrap.sh
|
2026-09-22 19:06:31 +02:00
|
|
|
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
|
2026-09-29 15:45:41 +02:00
|
|
|
# The onboarding token is written by kanidm-setup.sh, once; a rerun of
|
|
|
|
|
# this script must not lose it, or every invite fails closed again.
|
|
|
|
|
kept_token=$(grep '^KANIDM_API_TOKEN=' portal.env 2>/dev/null | head -1 || true)
|
2026-09-22 19:06:31 +02:00
|
|
|
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
|
2026-09-29 15:45:41 +02:00
|
|
|
SEED_ADMIN_EMAIL=${SEED_ADMIN_EMAIL:-}
|
|
|
|
|
SEED_ADMIN_NAME=${SEED_ADMIN_NAME:-}
|
|
|
|
|
RESPONSIBLE_GROUP=${RESPONSIBLE_GROUP:-}
|
2026-09-22 19:06:31 +02:00
|
|
|
EOF
|
2026-09-29 15:45:41 +02:00
|
|
|
[ -n "$kept_token" ] && echo "$kept_token" >> portal.env
|
2026-09-22 19:06:31 +02:00
|
|
|
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
|
|
|
|
|
|
2026-09-28 11:15:29 +02:00
|
|
|
podman compose up -d --build
|
2026-09-22 19:06:31 +02:00
|
|
|
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) ==="
|
2026-09-28 11:15:29 +02:00
|
|
|
podman compose exec kanidm kanidmd recover-account admin
|
|
|
|
|
podman compose exec kanidm kanidmd recover-account idm_admin
|
2026-09-22 19:06:31 +02:00
|
|
|
touch .kanidm-recovered
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
echo
|
|
|
|
|
echo "Next: sh kanidm-setup.sh (log in as idm_admin, create the portal client and desk groups)"
|