key-cape/scripts/bootstrap-dev.sh

122 lines
3.7 KiB
Bash
Raw Normal View History

Reconcile packaging, bootstrap and migration credential handling Closes gap G09: five loosely related defects. lldap-export took the service account password on argv, where ps exposes it to any local user and shell history and process accounting capture it. It now prefers KEYCAPE_LLDAP_BIND_PW or --bind-pw-file; --bind-pw still works but warns, deprecated rather than removed because existing runbooks use it and breaking them silently would be worse than one more cycle of exposure. Conflicting sources are rejected instead of silently ranked, since an operator otherwise cannot tell which bind was attempted. Both migration scripts pass the password by environment now. The canonical export, generated LDIF and Keycloak realm were written 0644. None carries credential material, but the snapshot is every username, display name, email and group membership in the estate, and it tends to land in /tmp. All three are 0600. The image packaged keycape alone, so the validator and migration binaries needed a Go toolchain on the host -- which defeats shipping an image for the cutover work they exist to support. All five ship; the issuer stays the entrypoint. Verified by building the image and running each binary inside it. The publish workflow named 92.205.130.254:32166 while the cluster runs forgejo.coulomb.social/coulomb/key-cape. It now defaults to the recorded name and stays overridable by a repository variable. This repository cannot verify that the runner resolves that hostname or that the registry credentials are valid for it; if the next publish fails, set the REGISTRY variable back to the address. docker-compose.dev.yml mounts a private key and Authelia material that are correctly absent from the checkout. scripts/bootstrap-dev.sh generates them locally under a restrictive umask rather than chmodding afterwards, so the key is never briefly world-readable. Everything it writes is git-ignored. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NV9oijZukGyGbRQGGKnK4P Assistant: claude-code Assistant-Model: opus Assistant-Process: 713576@bnt-lap001 Assistant-Session: 384c511d-9bce-4cb8-a676-2aef6c0c8df6
2026-09-08 09:51:59 +02:00
#!/usr/bin/env bash
# bootstrap-dev.sh — generate the local material docker-compose.dev.yml expects.
#
# docker-compose.dev.yml mounts config/dev-key.pem and config/authelia/, neither
# of which is in the checkout and neither of which should be: one is a private
# key, the other carries a password hash. This script generates both locally
# (KEY-WP-0026).
#
# Everything it writes is development-only and git-ignored. Never reuse any of it
# in a deployed environment.
set -euo pipefail
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$REPO_ROOT"
KEY_PATH="config/dev-key.pem"
AUTHELIA_DIR="config/authelia"
command -v openssl >/dev/null || { echo "bootstrap-dev: openssl is required" >&2; exit 1; }
if [ -e "$KEY_PATH" ]; then
echo "bootstrap-dev: $KEY_PATH exists, leaving it alone"
else
# Generated with 0600 from the start rather than created and chmodded after:
# otherwise the key is briefly world-readable on a shared machine.
(umask 077 && openssl genrsa -out "$KEY_PATH" 2048 2>/dev/null)
echo "bootstrap-dev: wrote $KEY_PATH (RSA 2048, mode 0600)"
fi
mkdir -p "$AUTHELIA_DIR"
if [ -e "$AUTHELIA_DIR/configuration.yml" ]; then
echo "bootstrap-dev: $AUTHELIA_DIR/configuration.yml exists, leaving it alone"
else
cat > "$AUTHELIA_DIR/configuration.yml" <<'YAML'
# Development Authelia configuration. Not for deployment.
theme: light
default_redirection_url: http://localhost:8080/
server:
address: 'tcp://:9091'
log:
level: info
authentication_backend:
file:
path: /config/users.yml
access_control:
default_policy: one_factor
session:
name: authelia_session
secret: devsessionsecret
expiration: 1h
inactivity: 15m
cookies:
- domain: localhost
authelia_url: http://localhost:9091
storage:
encryption_key: devencryptionkeydevencryptionkey12345678
local:
path: /config/db.sqlite3
notifier:
filesystem:
filename: /config/notification.txt
identity_providers:
oidc:
hmac_secret: devhmacsecretdevhmacsecret123456
jwks:
- key_id: dev
algorithm: RS256
use: sig
key: {{ secret "/config/oidc-key.pem" }}
clients:
- client_id: keycape
client_name: KeyCape
client_secret: devsecret
public: false
authorization_policy: one_factor
redirect_uris:
- http://localhost:8080/authorize/callback
scopes: [openid, profile, email, groups]
YAML
echo "bootstrap-dev: wrote $AUTHELIA_DIR/configuration.yml"
fi
if [ -e "$AUTHELIA_DIR/oidc-key.pem" ]; then
echo "bootstrap-dev: $AUTHELIA_DIR/oidc-key.pem exists, leaving it alone"
else
(umask 077 && openssl genrsa -out "$AUTHELIA_DIR/oidc-key.pem" 2048 2>/dev/null)
echo "bootstrap-dev: wrote $AUTHELIA_DIR/oidc-key.pem (mode 0600)"
fi
if [ -e "$AUTHELIA_DIR/users.yml" ]; then
echo "bootstrap-dev: $AUTHELIA_DIR/users.yml exists, leaving it alone"
else
# argon2id hash of "devpassword", generated once for this fixture. It is a
# development credential published in this script on purpose: anyone reading
# the repository can see exactly what it unlocks, which is nothing deployed.
cat > "$AUTHELIA_DIR/users.yml" <<'YAML'
users:
alice:
displayname: Alice Example
# devpassword
password: '$argon2id$v=19$m=65536,t=3,p=4$YWxpY2VkZXZzYWx0MTIzNA$6qJKQ0kZ0Zx2mQ0mJ0Zx2mQ0mJ0Zx2mQ0mJ0Zx2mQ0'
email: alice@example.com
groups:
- admins
YAML
chmod 600 "$AUTHELIA_DIR/users.yml"
echo "bootstrap-dev: wrote $AUTHELIA_DIR/users.yml (mode 0600)"
fi
cat <<'NEXT'
bootstrap-dev: done. Next:
docker compose -f docker-compose.dev.yml up -d
curl -s http://localhost:8080/healthz
curl -s http://localhost:8080/readyz
All generated material is development-only and git-ignored.
NEXT