From c659bd3c1f2ca4f974585e31d6a2db1a645fc651 Mon Sep 17 00:00:00 2001 From: eding Date: Sat, 5 Sep 2026 20:28:24 +0200 Subject: [PATCH] docs: contain credential rotation guidance --- How2Auth.md | 13 ++++- docs/runbooks/credential-rotation.md | 43 ++++++++++++++++ docs/secrets.md | 74 ++++++---------------------- 3 files changed, 69 insertions(+), 61 deletions(-) create mode 100644 docs/runbooks/credential-rotation.md diff --git a/How2Auth.md b/How2Auth.md index ad7fa76..4029446 100644 --- a/How2Auth.md +++ b/How2Auth.md @@ -1,2 +1,11 @@ -edingrech -dckr_pat_bfKKDH4g3qUxchs9UMLxFx2oTiU \ No newline at end of file +# Credential onboarding + +Do not put credential values in this file, Git, generated configuration, command arguments, terminal history, or logs. + +1. Obtain written authorization for ``, ``, ``, and ``. +2. Have the authorized operator enter `` only through the approved secure prompt or standard input boundary. +3. Store it only in the configured SOPS-encrypted operational-secrets file, then reseed and verify OpenBao before changing consumers. +4. Keep the previous credential active only for the approved overlap window; revoke it only after every consumer check succeeds. +5. Record ``, ``, ``, and `` without recording any credential value. + +See [docs/secrets.md](docs/secrets.md) and [docs/runbooks/credential-rotation.md](docs/runbooks/credential-rotation.md). diff --git a/docs/runbooks/credential-rotation.md b/docs/runbooks/credential-rotation.md new file mode 100644 index 0000000..f3970cf --- /dev/null +++ b/docs/runbooks/credential-rotation.md @@ -0,0 +1,43 @@ +# Credential rotation runbook + +## Authorization and inputs + +Before any live action, obtain approval for the exact ``, ``, ``, ``, ``, ``, and ``. The authorized operator must have access to the approved configuration, SOPS identity, recovery identity and encrypted recovery bundle, OpenBao recovery recipient, and the required Forgejo, Flux, webhook, Tekton, registry, and cluster permissions. Credential values must be entered only through an approved secure prompt or standard input boundary; never provide them to an agent or place them in an argument, file, log, or evidence record. + +## Consumer inventory template + +| Consumer | Logical secret path | Credential identifier | Owner | Validation | Status | +| --- | --- | --- | --- | --- | --- | +| `` | `` | `` | `` | `` | `` | + +Include Forgejo API access, Flux source authentication, webhook authorization, Tekton pipeline consumers, and registry pull/push consumers when applicable. Stop for an unlisted or ambiguous consumer; do not guess its credential source. + +## Ordered rotation + +1. Confirm the old credential remains valid for the approved overlap window and capture the sanitized baseline status. +2. Create the replacement with the approved minimum scope. Do not revoke or replace the old credential yet. +3. Put the replacement only in `sops.operationalSecretsPath`; encrypt it with `sops.ageKeyPath`. Keep the recovery identity and encrypted recovery bundle in their configured local paths. Do not write plaintext configuration or generated files. +4. Reseed OpenBao from the encrypted operational-secrets file. Verify OpenBao is initialized, unsealed, and ready, then verify External Secrets has refreshed the intended target Secret without reading or printing its data. +5. Validate each inventoried consumer: Forgejo authenticated operation, Flux source authentication and reconciliation readiness, webhook delivery authorization, Tekton trigger and PipelineRun behavior, and registry pull/push behavior. Record only identifiers, timestamps, and pass/fail status. +6. If every validation passes during the overlap window, revoke the old credential through its owning system and repeat the affected consumer checks. + +## Rollback + +If OpenBao reseed, readiness, External Secrets refresh, or any consumer validation fails, stop before revocation. Restore the previously encrypted operational-secret version, reseed OpenBao, verify readiness and all affected consumers, and keep the old credential active. Escalate if the previous encrypted version or recovery material is unavailable; do not reconstruct values from logs or configuration. + +## Sanitized evidence template + +| Field | Record | +| --- | --- | +| Rotation ID | `` | +| Credential identifier | `` | +| Scope approval | `` | +| Operator | `` | +| Started / completed | `` / `` | +| OpenBao initialized / unsealed / ready | `` / `` / `` | +| External Secrets target refresh | `: ` | +| Forgejo / Flux / webhook / Tekton / registry | `` / `` / `` / `` / `` | +| Old credential revocation | `` | +| Follow-up | `` | + +Never include values, encoded values, headers, token fragments, private keys, recovery shares, or decrypted manifest content in the evidence. diff --git a/docs/secrets.md b/docs/secrets.md index ad57fe6..fc50b11 100644 --- a/docs/secrets.md +++ b/docs/secrets.md @@ -1,66 +1,22 @@ -# Bootstrap Secret Inputs +# Secrets and recovery material -`bootstrap` uses the Flux age identity in ignored `.age/` storage to encrypt -the configured Democratic CSI Secret directly into the generated cluster -repository. Its TrueNAS API key is never printed or committed in plaintext. +## Storage contract -`operational-secrets.sops.yaml` is decrypted only in MaidnCLI memory after -OpenBao is initialized. It is not copied to the cluster repository. Its schema -is: +- `sops.operationalSecretsPath` defaults to `/operational-secrets.sops.yaml`. It is a SOPS-encrypted input containing operational secrets, keyed by logical OpenBao paths. It is never plaintext Git content. +- `sops.ageKeyPath` defaults to `/.age/key.txt`. This Flux SOPS identity is private local material; do not commit, print, or copy it. +- `sops.recoveryIdentityPath` defaults to `/.age/recovery-key.txt`. This separate OpenBao-recovery identity is private local material; do not commit, print, or copy it. +- `sops.recoveryBundlePath` defaults to `/.recovery/openbao-recovery.age`. This bundle is encrypted to `sops.recoveryRecipient`; it contains OpenBao recovery material and is never plaintext Git content. +- During generated Flux repository reconciliation, recovery material is rendered only into `base/openbao/unseal.sops.yaml`, encrypted with the Flux SOPS identity. This is the narrowly scoped encrypted recovery path for the `openbao-unseal` controller; no plaintext recovery material belongs in a workload, config, log, or command argument. -```yaml -secrets: - cicd/forgejo: - username: encrypted-value - token: encrypted-value - cicd/forgejo-registry: - dockerconfigjson: encrypted-value - cicd/forgejo-webhook: - authorization: encrypted-value - platform/pihole: - server: encrypted-value - password: encrypted-value - platform/cloudflare: - api-token: encrypted-value - platform/cloudflare-tunnel: - credentials: encrypted-value - config: encrypted-value -``` +## Bootstrap gates -Keys are written to OpenBao KV v2 under `secret/`. Additional paths are -allowed when they use lowercase path characters and scalar property names. +`bootstrap` resolves and validates the configuration before reconciliation. Any bootstrap requires a readable SOPS age identity. `talos.autoBootstrapFlux: true` additionally requires an existing encrypted operational-secrets file and a nonempty recovery recipient. A non-webhook bootstrap additionally requires configured, existing recovery-identity and recovery-bundle files. -Set all `democraticCsi` settings in the bootstrap configuration or provide -them through the interactive wizard. The CLI writes those values only to -`base/democratic-csi/secret.sops.yaml` in the generated cluster repository. -For an existing configuration, run `bootstrap --config ---prompt-democratic-csi` to enter the settings with the API key masked. +The webhook-only path requires a complete delivery contract, an approved configuration, and a readable SOPS age identity. It must reseed OpenBao and observe the refreshed `forgejo-webhook` target Secret plus the EventListener and Pipeline before Forgejo is changed. The registry-token and webhook-authorization flows also require an explicit configuration; they are live credential operations and are not offline-safe. -For a new operational-secret input, run `bootstrap --config ---prompt-operational-secrets`. It derives Forgejo Git and registry credentials -from the configured Forgejo account, prompts for the Pi-hole server and masked -password and Cloudflare API token, and generates the webhook authorization -value. The Cloudflare token issues the Gateway certificate and manages explicit -Tunnel CNAME records; it is not used by ExternalDNS. +## Rules -Import the user-approved local credential file before bootstrap validates the -tunnel state: - -```powershell -go run . cloudflare-tunnel import --config --credentials-file -``` - -The import command reads the file only in memory, validates its credential -shape, stores only the credentials JSON and terminal-404 local config shown -above, and seeds OpenBao. It never saves a Tunnel run token. -The template maps these simple OpenBao properties to the `credentials.json` and -`config.yml` Kubernetes filenames. - -`cicd/forgejo-webhook.authorization` is required for delivery bootstrap. The -CLI supplies it as the Forgejo webhook Authorization header and Tekton compares -that header against the ExternalSecret-derived `forgejo-webhook` Secret. - -Run `bootstrap --config --initialize-openbao-recovery` to create and -save a separate recovery age identity for `openbao-recovery.age`. The Flux SOPS -age identity is installed in `flux-system`; it must not encrypt OpenBao -recovery material. +- Credential values are accepted only at an approved secure input boundary and stored only in encrypted operational or recovery material. +- Never pass credential values in CLI arguments, URLs, logs, Git commits, generated config, tickets, or evidence. +- Do not revoke a previous credential until OpenBao, External Secrets, and every listed consumer have passed validation. +- Use the sanitized procedure in [runbooks/credential-rotation.md](runbooks/credential-rotation.md) for any live rotation.