From 67e7e4eb347fb4952845fedc3c60e5bdbbba918e Mon Sep 17 00:00:00 2001 From: William Mantly Date: Fri, 7 Aug 2026 17:20:01 -0400 Subject: [PATCH] Fix --seed-node-secret path/stdin bugs; docs title casing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit seed_node_conf() (added for sso-manager-node's node-scoped secrets engine, DESIGN.md §5) had two bugs that made it fail on every call: - Path had an extra "data/" segment (secret/data/nodes//). bao kv put takes the mount-relative path and inserts "data/" itself for KV v2 -- same convention seed_app_conf already uses just above it (secret/${vault_path}, not secret/data/${vault_path}). Fixed to secret/nodes//, which resolves under the hood to the secret/data/nodes//* path api_agent_ops.js's node-scope check expects. - It piped "key=value\n" lines to `bao kv put path -`, but `-` there means "read a JSON object from stdin", not KV lines -- failed with "invalid key/value pair \"-\"" before ever reaching OpenBao. Fixed to pass key=value pairs as ordinary CLI args. Verified against a real OpenBao round-trip (write via the fixed function, read back both via the CLI and the same HTTP path the SSO's node-scope check uses). docs/_config.yml: "theta-suite" -> "Theta Suite" in the Jekyll site title. Co-Authored-By: Claude Sonnet 5 --- docs/_config.yml | 2 +- setup.sh | 40 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/docs/_config.yml b/docs/_config.yml index 8e129ea..a9049d1 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -1,4 +1,4 @@ -title: theta-suite +title: Theta Suite description: A unified, one-command SSO Manager + OIDC proxy stack for home labs and small businesses. url: "https://theta42.github.io" baseurl: "/theta-suite" diff --git a/setup.sh b/setup.sh index 5cc4150..7c6b91a 100755 --- a/setup.sh +++ b/setup.sh @@ -78,10 +78,13 @@ die() { error "$*"; exit 1; } # (stale policies/tokens causing vault 403s). The Redis vault-token cache is # flushed once sso-manager is back up (see the OpenBao bootstrap section). RESET_OPENBAO=0 +SEED_NODE_SECRET=0 +SEED_NODE_ARGS=() for arg in "$@"; do case "$arg" in --reset-openbao) RESET_OPENBAO=1 ;; - *) warn "unknown argument: $arg (ignored)" ;; + --seed-node-secret) SEED_NODE_SECRET=1 ;; + *) if [[ "$SEED_NODE_SECRET" == 1 ]]; then SEED_NODE_ARGS+=("$arg"); else warn "unknown argument: $arg (ignored)"; fi ;; esac done @@ -878,6 +881,29 @@ seed_app_conf() { || warn " could not seed secret/${vault_path} (continuing — app will use its file fallback)" } +# Seed a node-scoped secret for a theta-agent (DESIGN.md §5). Node secrets live +# at secret/data/nodes//* and are read by the agent (via the SSO's +# /api/v1/agent/secrets) on behalf of 3rd-party apps on the host. Agent ids are +# minted at enrollment, so this is a helper the operator calls per node, not a +# boot-time seed: +# ./setup.sh --seed-node-secret = [=...] +seed_node_conf() { + local agent_id="$1" name="$2"; shift 2 + [[ -n "$agent_id" && -n "$name" ]] || die "seed_node_conf: need " + # CLI paths are mount-relative (no "data/" segment -- the CLI inserts that + # itself for KV v2, same as seed_app_conf's "secret/${vault_path}" above). + # The HTTP API path api_agent_ops.js checks against (secret/data/nodes/...) + # is what this resolves to underneath. + local path="secret/nodes/${agent_id}/${name}" + if bao_run kv get "$path" >/dev/null 2>&1; then + info " ${path} already seeded — keeping." + return 0 + fi + info "Seeding ${path}..." + docker exec -e BAO_TOKEN="$VAULT_TOKEN" openbao bao kv put "$path" "$@" >/dev/null \ + || die "failed to seed ${path}" +} + info "Configuring OpenBao policies..." # sso-broker — sso's authority to read/write its own conf, mint per-user and # per-app tokens (auth/token/create/sso-broker), and create the matching @@ -900,6 +926,11 @@ path "secret/metadata/plugins/*" { capabilities = ["list", "read", "delete"] } # a key that changes on every boot makes signature verification meaningless. path "secret/data/agent/*" { capabilities = ["create", "read", "update", "delete", "list"] } path "secret/metadata/agent/*" { capabilities = ["list", "read", "delete"] } +# Node-scoped secrets for theta-agent (DESIGN.md §5): each node reads only its +# own secret/data/nodes//* subtree via the SSO's /api/v1/agent/secrets +# endpoint. The SSO (sso-broker) must be able to read them on the agent's behalf. +path "secret/data/nodes/*" { capabilities = ["create", "read", "update", "delete", "list"] } +path "secret/metadata/nodes/*" { capabilities = ["list", "read", "delete"] } path "auth/token/create/sso-broker" { capabilities = ["update"] } path "auth/token/create/sso-app" { capabilities = ["update"] } path "auth/token/renew-accessor" { capabilities = ["update"] } @@ -980,6 +1011,13 @@ info " policies: sso-broker, sso-admin, proxy, jump-host (+ per-user/app cre info " token roles: sso-broker (user-*/app-*/sso-admin, 24h period), sso-app (app-*, 768h period), theta-svc (service tokens, 768h period)" info " app tokens: SSO_VAULT_TOKEN, PROXY_VAULT_TOKEN, JUMP_VAULT_TOKEN in .env (periodic; renewed by bao-renewer)" +# --seed-node-secret =... : seed a node-scoped +# secret for a theta-agent (DESIGN.md §5). Runs after OpenBao is configured so +# the sso-broker policy (which grants secret/data/nodes/*) is in place. +if [[ "$SEED_NODE_SECRET" == 1 ]]; then + seed_node_conf "${SEED_NODE_ARGS[@]}" +fi + # bao-renewer: renews the three periodic service tokens every 12h so they never # hit their period boundary while the stack is running. Recreated (not just # started) so it always picks up freshly re-minted tokens from .env.