Pocket ID replaces Forgejo as the OIDC provider for Outline, Vaultwarden and NetBird; Forgejo logs in through it. Uptime Kuma sits behind tinyauth via Caddy forward_auth (group ops), the public status page stays public. offboarding/offboard.py switches one person off in every service with an API and prints what a human still has to do: the shared passwords they could read. Measured: SSO lock alone left 10 of 13 ways open for 45 minutes; the script leaves 1 of 14, the vault copy on their laptop. HARDENING.md: the ufw hairpin that broke Vaultwarden SSO and Kuma, the mesh route into the docker bridges, and a fail2ban exception for Pocket ID.
96 lines
4.3 KiB
Text
96 lines
4.3 KiB
Text
# ~$ selfhostedops — copy me: cp .env.example .env
|
|
# Generate secrets with: openssl rand -hex 32
|
|
|
|
# Your base domain. Services live at git.* wiki.* vault.* status.* ntfy.* vpn.*
|
|
# DNS: one wildcard A (and AAAA) record *.ops.example.com -> your server IP
|
|
DOMAIN=ops.example.com
|
|
|
|
# Let's Encrypt notifications + expiry mails
|
|
ACME_EMAIL=you@example.com
|
|
|
|
TZ=Europe/Berlin
|
|
|
|
# --- Outline ---
|
|
OUTLINE_PG_PASSWORD=CHANGE_ME_openssl_rand_hex_32
|
|
OUTLINE_SECRET_KEY=CHANGE_ME_openssl_rand_hex_32
|
|
OUTLINE_UTILS_SECRET=CHANGE_ME_openssl_rand_hex_32
|
|
# From Pocket ID (episode 15): Administration -> OIDC Clients, allowed group staff
|
|
# Callback URL: https://wiki.<DOMAIN>/auth/oidc.callback
|
|
OUTLINE_OIDC_CLIENT_ID=
|
|
OUTLINE_OIDC_CLIENT_SECRET=
|
|
|
|
# --- Vaultwarden ---
|
|
# Admin panel token (argon2 hash recommended, plain string works to start)
|
|
VAULTWARDEN_ADMIN_TOKEN=CHANGE_ME_openssl_rand_hex_32
|
|
# Flip to false once your team has signed up:
|
|
VAULTWARDEN_SIGNUPS_ALLOWED=true
|
|
# Single sign-on (episode 4 with Forgejo, since episode 15 Pocket ID).
|
|
# From Pocket ID: Administration -> OIDC Clients, allowed group staff
|
|
# Callback URL must be EXACTLY: https://vault.<DOMAIN>/identity/connect/oidc-signin
|
|
VAULTWARDEN_SSO_CLIENT_ID=
|
|
VAULTWARDEN_SSO_CLIENT_SECRET=
|
|
|
|
# --- wg-easy (optional, --profile vpn) ---
|
|
# Generate with: docker run --rm ghcr.io/wg-easy/wg-easy wgpw 'yourpassword'
|
|
WGEASY_PASSWORD_HASH=
|
|
|
|
# --- CI (episode 2, optional: docker compose --profile ci up -d) ---
|
|
# One shared secret registers the runner declaratively. Generate it with:
|
|
# openssl rand -hex 20
|
|
# Then derive the UUID Forgejo will know it by:
|
|
# python3 -c 'import uuid,sys;print(uuid.UUID(bytes=sys.argv[1][:16].encode()))' <secret>
|
|
# And register it on the Forgejo side:
|
|
# docker compose exec -u git forgejo forgejo forgejo-cli actions register \
|
|
# --name box-runner --labels docker --secret <secret>
|
|
# A registration token copied from the web UI does NOT work here.
|
|
RUNNER_SECRET=
|
|
RUNNER_UUID=
|
|
|
|
|
|
# --- ntfy push notifications (episode 6) ---
|
|
# No secrets here: ntfy keeps its users in its own database. Create them once:
|
|
# docker compose exec -e NTFY_PASSWORD=<pw> ntfy ntfy user add --role=user kuma
|
|
# docker compose exec ntfy ntfy access kuma alerts write-only
|
|
# See monitoring/README.md for the full walkthrough.
|
|
|
|
# --- n8n automation (episode 8) ---
|
|
# No variables needed here either: the workflows keep their credentials inside
|
|
# n8n, encrypted with the key in /home/node/.n8n/config. That file is inside the
|
|
# n8n_data volume and therefore inside the nightly backup from episode 7.
|
|
#
|
|
# If you would rather pin the key yourself (recommended once more than one
|
|
# person can restore this box), uncomment the line below and put the same value
|
|
# in your password manager. It wins over the file, and the file is then never
|
|
# written. Changing it later makes every existing credential undecryptable.
|
|
# openssl rand -base64 24
|
|
# N8N_ENCRYPTION_KEY=
|
|
#
|
|
# The ntfy user the workflows publish as:
|
|
# docker compose exec -e NTFY_PASSWORD=<pw> ntfy ntfy user add --role=user n8n
|
|
# docker compose exec ntfy ntfy access n8n alerts write-only
|
|
# docker compose exec ntfy ntfy access n8n deploys write-only
|
|
# See automation/README.md for the rest.
|
|
|
|
# --- Episode 9: the ops triage agent ---------------------------------------
|
|
# The shared secret between n8n and the ops-actions runner. Anything that can
|
|
# present this header can ask for a container restart, so treat it like a key:
|
|
# openssl rand -hex 24
|
|
# The runner still refuses any action or any target that is not on its own
|
|
# hard-coded allowlists, so a leaked token cannot widen the blast radius.
|
|
OPS_ACTIONS_TOKEN=
|
|
|
|
# --- Episode 12: CrowdSec -------------------------------------------------
|
|
# The Caddy bouncer's key for the CrowdSec Local API on the host:
|
|
# cscli bouncers add caddy-bouncer -o raw
|
|
# Without it Caddy still starts, but blocks nothing and logs auth errors.
|
|
CROWDSEC_BOUNCER_KEY=
|
|
|
|
# --- Episode 15: Pocket ID + tinyauth ---------------------------------------
|
|
# Encrypts Pocket ID's signing keys at rest: openssl rand -base64 32
|
|
# Then open https://id.<DOMAIN>/setup IMMEDIATELY: until the first admin
|
|
# exists, that page belongs to whoever finds it first (scanners did, in seconds).
|
|
POCKET_ID_ENCRYPTION_KEY=
|
|
# The OIDC client "Tinyauth" in Pocket ID
|
|
# (callback https://auth.<DOMAIN>/api/oauth/callback/pocketid, allowed group ops).
|
|
TINYAUTH_CLIENT_ID=
|
|
TINYAUTH_CLIENT_SECRET=
|