docs(reference-ansible): add docs/ tree and document repo, playbooks, Makefile
Addresses the WKS PoC review (Notion 2026-05-26). All docs in English. - README: purpose, docs table of contents, annotated repo tree - docs/getting_started.md: prerequisites (WKS account, OIDC, SSH, VPN) + first deploy - docs/ansible.md: playbook table, "Running Ansible", service parameters, cheatsheet - docs/secrets.md: canonical Bao login (moved out of README) + demo defaults - docs/operations.md: full Makefile reference - docs/inventories.md: repo layout, topology, standard folder structure, walkthrough - docs/testing.md: static checks, inventory resolution, smoke test / dry run - remove ARCHITECTURE.md (architecture docs live externally) Also includes the gymburgdorf inventory build-out (bookstack, homarr, opnform, send) and scripts/bao-seed.sh. site.yml keeps a third traefik play (traefik_servers minus the vagrant _dmz/_backend split) so the demo inventories still configure their reverse proxy after the rebase onto main.
This commit is contained in:
parent
c67e9aac43
commit
2ba0c07cd3
24 changed files with 1541 additions and 525 deletions
191
scripts/bao-seed.sh
Executable file
191
scripts/bao-seed.sh
Executable file
|
|
@ -0,0 +1,191 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# Seed OpenBao secrets for a demo inventory. Merge semantics: existing
|
||||
# keys at a given path are kept; only missing keys are generated. OIDC
|
||||
# client secrets are synced between <mount>/data/authentik and the
|
||||
# per-service secret so authentik and the service agree on the same
|
||||
# value.
|
||||
#
|
||||
# Usage:
|
||||
# scripts/bao-seed.sh demo-gymburgdorf
|
||||
#
|
||||
# Requirements:
|
||||
# - bao CLI in $PATH, authenticated (make bao)
|
||||
# - jq in $PATH
|
||||
# - openssl in $PATH
|
||||
#
|
||||
# Environment overrides:
|
||||
# BAO_ADDR default https://bao.digitalboard.ch
|
||||
# DRY_RUN=1 print what would change without writing
|
||||
|
||||
set -eu
|
||||
|
||||
MOUNT="${1:-}"
|
||||
if [[ -z "$MOUNT" ]]; then
|
||||
echo "usage: $0 <bao-mount> (e.g. demo-gymburgdorf)" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
: "${BAO_ADDR:=https://bao.digitalboard.ch}"
|
||||
export BAO_ADDR
|
||||
|
||||
for cmd in bao jq openssl mktemp; do
|
||||
command -v "$cmd" >/dev/null || { echo "missing: $cmd" >&2; exit 1; }
|
||||
done
|
||||
|
||||
if ! bao token lookup >/dev/null 2>&1; then
|
||||
echo "not authenticated — run 'make bao' first" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
DRY_RUN="${DRY_RUN:-0}"
|
||||
|
||||
WORKDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$WORKDIR"' EXIT
|
||||
|
||||
# Read current data for a KV-v2 secret into $WORKDIR/<path>.json.
|
||||
# Writes {} if the secret does not exist or has no data yet.
|
||||
read_secret() {
|
||||
local path="$1"
|
||||
local out="$WORKDIR/$path.json"
|
||||
local raw="$WORKDIR/$path.raw"
|
||||
|
||||
if bao kv get -format=json "$MOUNT/$path" >"$raw" 2>/dev/null; then
|
||||
if jq -e -c '.data.data // {}' "$raw" >"$out" 2>/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
echo '{}' >"$out"
|
||||
}
|
||||
|
||||
# Write the in-memory secret back to bao using `bao kv put @file` so
|
||||
# values containing `=` (e.g. base64 padding) survive intact.
|
||||
write_secret() {
|
||||
local path="$1"
|
||||
local src="$WORKDIR/$path.json"
|
||||
|
||||
if [[ "$DRY_RUN" == "1" ]]; then
|
||||
echo " [dry-run] would write $MOUNT/$path"
|
||||
return
|
||||
fi
|
||||
bao kv put "$MOUNT/$path" "@$src" >/dev/null
|
||||
}
|
||||
|
||||
# Print the current value of a key from a secret file, empty string if
|
||||
# absent.
|
||||
get_key() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
jq -r --arg k "$key" '.[$k] // ""' "$file"
|
||||
}
|
||||
|
||||
# Set a key inside the secret file to the given literal value.
|
||||
set_key() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local value="$3"
|
||||
local tmp="$file.tmp"
|
||||
jq --arg k "$key" --arg v "$value" '.[$k] = $v' "$file" >"$tmp"
|
||||
mv "$tmp" "$file"
|
||||
}
|
||||
|
||||
# Generate a key in the secret file if missing.
|
||||
ensure_key() {
|
||||
local path="$1"
|
||||
local key="$2"
|
||||
local generator="$3"
|
||||
local file="$WORKDIR/$path.json"
|
||||
|
||||
if [[ -n "$(get_key "$file" "$key")" ]]; then
|
||||
return
|
||||
fi
|
||||
local value
|
||||
value="$($generator)"
|
||||
set_key "$file" "$key" "$value"
|
||||
echo " + $key"
|
||||
}
|
||||
|
||||
# Force a key on path2 to match path1's value for the named key.
|
||||
# Prints when a change happens.
|
||||
sync_key_from() {
|
||||
local src_path="$1"
|
||||
local src_key="$2"
|
||||
local dst_path="$3"
|
||||
local dst_key="$4"
|
||||
|
||||
local src_file="$WORKDIR/$src_path.json"
|
||||
local dst_file="$WORKDIR/$dst_path.json"
|
||||
|
||||
local src_val
|
||||
src_val="$(get_key "$src_file" "$src_key")"
|
||||
if [[ -z "$src_val" ]]; then
|
||||
echo " ! $src_path/$src_key missing — cannot sync to $dst_path/$dst_key" >&2
|
||||
return
|
||||
fi
|
||||
|
||||
local dst_val
|
||||
dst_val="$(get_key "$dst_file" "$dst_key")"
|
||||
if [[ "$src_val" == "$dst_val" ]]; then
|
||||
return
|
||||
fi
|
||||
set_key "$dst_file" "$dst_key" "$src_val"
|
||||
echo " = $dst_key (synced from $src_path/$src_key)"
|
||||
}
|
||||
|
||||
gen_hex32() { openssl rand -hex 32; }
|
||||
gen_hex64() { openssl rand -hex 64; }
|
||||
gen_pass() { openssl rand -base64 24 | tr -d '/+='; }
|
||||
gen_long_pass() { openssl rand -base64 32 | tr -d '/+='; }
|
||||
gen_app_key() { echo "base64:$(openssl rand -base64 32)"; }
|
||||
|
||||
# OpnForm requires: min 8 chars, a letter, a digit, AND one of the
|
||||
# special chars @$!%*#?&-_+=.,:;<>^()[]{}|~. The base64 generator alone
|
||||
# only produces [A-Za-z0-9], so we append a fixed-position special char
|
||||
# and digit to guarantee the rule passes regardless of entropy outcome.
|
||||
gen_opnform_pass() { echo "$(openssl rand -base64 18 | tr -d '/+=')!1Aa"; }
|
||||
|
||||
# ---------------------------------------------------------------- main
|
||||
|
||||
echo "==> seeding $MOUNT (dry-run=$DRY_RUN)"
|
||||
|
||||
echo "-> authentik"
|
||||
read_secret authentik
|
||||
ensure_key authentik secret_key gen_hex64
|
||||
ensure_key authentik postgres_password gen_pass
|
||||
ensure_key authentik admin_password gen_pass
|
||||
ensure_key authentik ldap_outpost_token gen_hex32
|
||||
ensure_key authentik nextcloud_oidc_secret gen_hex32
|
||||
ensure_key authentik opnform_oidc_secret gen_hex32
|
||||
ensure_key authentik homarr_oidc_secret gen_hex32
|
||||
ensure_key authentik bookstack_oidc_secret gen_hex32
|
||||
write_secret authentik
|
||||
|
||||
echo "-> opnform"
|
||||
read_secret opnform
|
||||
ensure_key opnform app_key gen_app_key
|
||||
ensure_key opnform jwt_secret gen_hex32
|
||||
ensure_key opnform front_api_secret gen_hex32
|
||||
ensure_key opnform db_password gen_long_pass
|
||||
ensure_key opnform admin_password gen_opnform_pass
|
||||
ensure_key opnform oidc_client_secret gen_hex32
|
||||
sync_key_from authentik opnform_oidc_secret opnform oidc_client_secret
|
||||
write_secret opnform
|
||||
|
||||
echo "-> homarr"
|
||||
read_secret homarr
|
||||
ensure_key homarr secret_encryption_key gen_hex32
|
||||
ensure_key homarr admin_password gen_pass
|
||||
ensure_key homarr oidc_client_secret gen_hex32
|
||||
sync_key_from authentik homarr_oidc_secret homarr oidc_client_secret
|
||||
write_secret homarr
|
||||
|
||||
echo "-> bookstack"
|
||||
read_secret bookstack
|
||||
ensure_key bookstack db_root_password gen_long_pass
|
||||
ensure_key bookstack db_password gen_long_pass
|
||||
ensure_key bookstack admin_password gen_pass
|
||||
ensure_key bookstack oidc_client_secret gen_hex32
|
||||
sync_key_from authentik bookstack_oidc_secret bookstack oidc_client_secret
|
||||
write_secret bookstack
|
||||
|
||||
echo "==> done"
|
||||
Loading…
Add table
Add a link
Reference in a new issue