generated from coulomb/repo-seed
New Workplans
This commit is contained in:
293
workplans/NK-WP-0003-keycape-privacyidea-cluster-deployment.md
Normal file
293
workplans/NK-WP-0003-keycape-privacyidea-cluster-deployment.md
Normal file
@@ -0,0 +1,293 @@
|
||||
---
|
||||
id: NK-WP-0003
|
||||
type: workplan
|
||||
title: "KeyCape + privacyIDEA Stack — Cluster Deployment"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: active
|
||||
owner: custodian
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-20"
|
||||
updated: "2026-03-20"
|
||||
state_hub_workstream_id: "f24cefd4-a09b-4fa1-9b25-94bf783b425e"
|
||||
---
|
||||
|
||||
# KeyCape + privacyIDEA Stack — Cluster Deployment
|
||||
|
||||
## Goal
|
||||
|
||||
Deploy the full NetKingdom identity stack on the live k3s cluster without
|
||||
Keycloak. KeyCape (v0.1, complete) is the OIDC orchestration layer; it
|
||||
binds LLDAP (directory), Authelia (auth sessions), and privacyIDEA (MFA).
|
||||
|
||||
NK-WP-0001 was scoped around Keycloak and is deferred. This workplan
|
||||
covers everything needed to reach a production-ready identity plane.
|
||||
|
||||
## Pre-conditions
|
||||
|
||||
- [x] k3s cluster healthy — RAIL-BS-WP-0002 ✓
|
||||
- [x] kubeconfig available at `~/.kube/config-hosteurope` — RAIL-BS-WP-0005 ✓
|
||||
- [x] All manifests committed — net-kingdom `sso-mfa/k8s/` ✓
|
||||
- [x] KeyCape v0.1 complete — KEY-WP-0001 ✓
|
||||
- [ ] SOPS + age integrated into net-kingdom (T01 below)
|
||||
- [ ] Credential ops-bundle generated and stored in KeePassXC (T01 below)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Internet → Traefik (k3s) → cert-manager TLS
|
||||
├── auth.coulomb.social → Authelia
|
||||
├── pink.coulomb.social → privacyIDEA portal
|
||||
└── id.coulomb.social → KeyCape (OIDC)
|
||||
|
||||
KeyCape ──► Authelia (session, password)
|
||||
──► LLDAP (directory, user lookup)
|
||||
──► privacyIDEA (MFA challenges via trigger-admin token)
|
||||
|
||||
privacyIDEA ──► PostgreSQL (privacyidea_db via CloudNativePG)
|
||||
LLDAP ──► PostgreSQL (lldap_db via CloudNativePG)
|
||||
Authelia ──► PostgreSQL (authelia_db via CloudNativePG)
|
||||
```
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — Credential setup: SOPS + age + ops-bundle
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T01
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "6a22e17e-5854-4f8b-b419-9dc86d490357"
|
||||
```
|
||||
|
||||
Net-kingdom currently uses a manual KeePassXC + age-bundle approach while
|
||||
railiance-infra uses SOPS with age keys. This task aligns them under the
|
||||
Credential Management Standard (`canon/standards/credential-management_v0.1.md`).
|
||||
|
||||
Steps:
|
||||
1. Verify the operator age keypair exists at `~/.config/sops/age/key.txt`
|
||||
(reuse the railiance key if already present — one keypair per operator)
|
||||
2. Add `.sops.yaml` to net-kingdom root (mirror railiance-infra pattern):
|
||||
- Encrypt files matching `secrets/.*` and `**/*.sops.yaml`
|
||||
- Recipient: operator age public key
|
||||
3. Run `sso-mfa/bootstrap/gen-secrets.sh ./secrets` to generate all service secrets
|
||||
4. Store each secret in KeePassXC under the `net-kingdom/` group hierarchy
|
||||
(see credential management standard for group layout)
|
||||
5. Run `sso-mfa/bootstrap/pack-bundle.sh ./secrets <age-pub-key>` → encrypted ops bundle
|
||||
6. Store ops bundle offsite (separate from KeePassXC)
|
||||
7. Shred plaintext secrets: `find secrets/ -type f -exec shred -u {} \;`
|
||||
|
||||
### T02 — Apply cluster foundations
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T02
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "a14e3a6b-18ee-4172-8a47-bd531f21e55a"
|
||||
```
|
||||
|
||||
Apply the K8s infrastructure foundations. All manifests already committed.
|
||||
|
||||
```bash
|
||||
export KUBECONFIG=~/.kube/config-hosteurope
|
||||
kubectl apply -f sso-mfa/k8s/namespaces/
|
||||
kubectl apply -f sso-mfa/k8s/network-policies/
|
||||
kubectl apply -f sso-mfa/k8s/cert-manager/
|
||||
```
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t02.sh`
|
||||
|
||||
Expected: namespaces `sso`, `mfa`, `databases` exist; NetworkPolicies applied;
|
||||
cert-manager pods Running.
|
||||
|
||||
### T03 — Deploy PostgreSQL (CloudNativePG)
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "19e375d0-66bd-4cf0-9c2d-59d5c0d5989e"
|
||||
```
|
||||
|
||||
Deploy the shared database cluster with three databases:
|
||||
- `privacyidea_db` — privacyIDEA
|
||||
- `lldap_db` — LLDAP
|
||||
- `authelia_db` — Authelia
|
||||
|
||||
```bash
|
||||
kubectl apply -f sso-mfa/k8s/postgres/
|
||||
```
|
||||
|
||||
Wait for cluster to be `Ready`, then verify: `bash sso-mfa/k8s/verify-t03.sh`
|
||||
|
||||
**Note**: Do not proceed to T04 until the CloudNativePG cluster is fully
|
||||
healthy. Migration jobs will fail on a partially-started cluster.
|
||||
|
||||
### T04 — Deploy privacyIDEA
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T04
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "9c9c1ec9-0cf5-4546-a83e-d74dbf3b27af"
|
||||
```
|
||||
|
||||
Deploy privacyIDEA into the `mfa` namespace.
|
||||
|
||||
**Step 1 — Create K8s secrets from KeePassXC:**
|
||||
```bash
|
||||
cd sso-mfa/k8s/privacyidea
|
||||
bash create-secrets.sh # reads from env vars; source from KeePassXC
|
||||
```
|
||||
|
||||
**Step 2 — Apply manifests:**
|
||||
```bash
|
||||
kubectl apply -f pvc.yaml
|
||||
kubectl apply -f configmap.yaml
|
||||
kubectl apply -f middleware.yaml
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
```
|
||||
|
||||
**Step 3 — Bootstrap key material (time-sensitive):**
|
||||
Run immediately once the pod reaches `Running` state. This window must not
|
||||
be missed — if the pod is deleted before this runs, the enckey is lost.
|
||||
```bash
|
||||
bash enckey-bootstrap.sh # extracts PI_ENCFILE + audit keys → K8s Secrets + KeePassXC
|
||||
```
|
||||
|
||||
**Step 4 — Create admin accounts:**
|
||||
```bash
|
||||
bash bootstrap-admin.sh # creates pi-admin + trigger-admin, sets policies
|
||||
# store trigger-admin token in KeePassXC net-kingdom/privacyidea/trigger-admin
|
||||
```
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t04.sh`
|
||||
|
||||
Expected: pod Running, TLS cert issued for `pink.coulomb.social`, admin
|
||||
accounts exist, enckey backed up.
|
||||
|
||||
### T05 — Deploy LLDAP
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T05
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "82fc90f7-8eb4-4718-b02a-dfd5fa39e5bc"
|
||||
```
|
||||
|
||||
Deploy LLDAP into the `sso` namespace.
|
||||
|
||||
```bash
|
||||
cd sso-mfa/k8s/lldap
|
||||
bash create-secrets.sh
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
kubectl apply -f middleware.yaml
|
||||
bash bootstrap-users.sh # creates base OU structure + initial admin user
|
||||
```
|
||||
|
||||
Verify pod Running and LDAP bind works on `ldap.coulomb.social`.
|
||||
|
||||
### T06 — Deploy Authelia
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T06
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "3a28ff10-fbfa-443b-a64d-bbfe6153c544"
|
||||
```
|
||||
|
||||
Deploy Authelia into the `sso` namespace.
|
||||
|
||||
```bash
|
||||
cd sso-mfa/k8s/authelia
|
||||
bash create-secrets.sh
|
||||
kubectl apply -f configmap.yaml
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
```
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t05.sh` (covers LLDAP + Authelia together)
|
||||
|
||||
### T07 — Deploy KeyCape
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T07
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "496a97c9-3e2a-486e-ba62-18449868c6cf"
|
||||
```
|
||||
|
||||
Deploy KeyCape into the `sso` namespace.
|
||||
|
||||
```bash
|
||||
cd sso-mfa/k8s/keycape
|
||||
bash create-secrets.sh # includes privacyIDEA trigger-admin token
|
||||
bash create-pi-token.sh # registers KeyCape as a privacyIDEA application
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f ingress.yaml
|
||||
kubectl apply -f middleware.yaml
|
||||
```
|
||||
|
||||
Verify: OIDC discovery endpoint reachable at
|
||||
`https://id.coulomb.social/.well-known/openid-configuration`
|
||||
|
||||
### T08 — End-to-end authentication test
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T08
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "0fba3392-c916-43fd-a2c1-24ce39481043"
|
||||
```
|
||||
|
||||
Prove the full auth flow works:
|
||||
1. OIDC discovery resolves at `id.coulomb.social`
|
||||
2. Authelia password auth succeeds for a test user
|
||||
3. privacyIDEA TOTP challenge issued and accepted
|
||||
4. KeyCape issues a valid access token
|
||||
5. Token introspection returns expected claims (sub, groups, email)
|
||||
|
||||
Use the KeyCape acceptance test suite:
|
||||
```bash
|
||||
cd /home/worsch/key-cape
|
||||
go test ./tests/... -run TestProfileBaseline -v
|
||||
```
|
||||
|
||||
### T09 — Backup, DR, and monitoring
|
||||
|
||||
```task
|
||||
id: NK-WP-0003-T09
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "a82751d8-4de8-4668-8568-8dc140a6322b"
|
||||
```
|
||||
|
||||
Operational hardening:
|
||||
|
||||
1. Deploy backup CronJob for CloudNativePG → MinIO/S3
|
||||
```bash
|
||||
kubectl apply -f sso-mfa/k8s/backup/
|
||||
```
|
||||
2. Execute DB restore drill (mandatory before production traffic):
|
||||
restore `privacyidea_db` from a backup into a test namespace, verify
|
||||
privacyIDEA starts cleanly with the restored data
|
||||
3. Deploy break-glass admin access (disabled by default):
|
||||
```bash
|
||||
bash sso-mfa/k8s/lldap/break-glass.sh setup
|
||||
```
|
||||
4. Verify Prometheus scraping for privacyIDEA and Authelia metrics
|
||||
5. Confirm NetworkPolicies block all unexpected egress
|
||||
|
||||
Verify: `bash sso-mfa/k8s/verify-t08.sh` (if exists) or manual checklist
|
||||
from NK-WP-0001 T08 scope.
|
||||
|
||||
## Done criteria
|
||||
|
||||
- [ ] All verify-t*.sh scripts exit 0
|
||||
- [ ] KeyCape acceptance test suite passes
|
||||
- [ ] DB restore drill completed
|
||||
- [ ] All key material backed up in KeePassXC + ops bundle
|
||||
- [ ] privacyIDEA enckey backed up (K8s Secret + KeePassXC)
|
||||
- [ ] Monitoring active (Prometheus scraping all three services)
|
||||
357
workplans/NK-WP-0004-credential-management-foundation.md
Normal file
357
workplans/NK-WP-0004-credential-management-foundation.md
Normal file
@@ -0,0 +1,357 @@
|
||||
---
|
||||
id: NK-WP-0004
|
||||
type: workplan
|
||||
title: "Credential Management Foundation"
|
||||
domain: netkingdom
|
||||
repo: net-kingdom
|
||||
status: active
|
||||
owner: custodian
|
||||
topic_slug: netkingdom
|
||||
created: "2026-03-20"
|
||||
updated: "2026-03-20"
|
||||
state_hub_workstream_id: "d9cf7c4b-886b-4cd1-ad7b-99c4e1929c9e"
|
||||
---
|
||||
|
||||
# Credential Management Foundation
|
||||
|
||||
## Goal
|
||||
|
||||
Make credential management a first-class, reliable foundation rather than
|
||||
a manual side-task. By the end of this workplan an operator can:
|
||||
|
||||
1. Run `make creds-init` to set up the full SOPS + age + KeePassXC workflow
|
||||
2. Run `make creds-generate` to produce all service secrets and be guided on
|
||||
KeePassXC entry
|
||||
3. Run `make creds-apply` to inject secrets into the cluster in the correct order
|
||||
4. Run `make creds-status` to see what is generated, applied, and verified
|
||||
5. Invoke `/creds-bootstrap` in Claude Code for guided assistance through
|
||||
the bootstrap process
|
||||
|
||||
This workplan is a **pre-condition for NK-WP-0003** (cluster deployment).
|
||||
NK-WP-0003-T01 is blocked until this workplan is complete.
|
||||
|
||||
## Problem
|
||||
|
||||
Current state:
|
||||
- `gen-secrets.sh` and `pack-bundle.sh` exist but are run manually, in
|
||||
isolation, with no orchestration
|
||||
- The five `create-secrets.sh` scripts must be run in a specific order
|
||||
(postgres → lldap → authelia → privacyidea → keycape) but this is
|
||||
undocumented and unenforced
|
||||
- Shared secrets (LLDAP_LDAP_USER_PASS, PI_DB_PASSWORD) are referenced
|
||||
across component scripts but there is no enforcement that source exists
|
||||
before consumer runs
|
||||
- No git pre-commit hook — plaintext secrets can accidentally be committed
|
||||
- No `.sops.yaml` — net-kingdom is not SOPS-enabled, unlike railiance-infra
|
||||
- No credential state file — no way to know which secrets are generated,
|
||||
which are applied, which are verified, without manual cluster inspection
|
||||
- The enckey-bootstrap.sh step is time-sensitive (must run while the
|
||||
privacyIDEA pod is live) but nothing flags this or sequences it
|
||||
- Operator must hold all of this in their head
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
Operator
|
||||
│
|
||||
├── make creds-init # one-time: age key check, .sops.yaml, git hook
|
||||
├── make creds-generate # run gen-secrets.sh → guided KeePassXC entry
|
||||
├── make creds-bundle # age-encrypt ops bundle → offsite
|
||||
├── make creds-apply # run all create-secrets.sh in correct order
|
||||
├── make creds-verify # check all K8s secrets exist with expected keys
|
||||
├── make creds-status # show credential state file
|
||||
└── make creds-rotate SECRET=<name> # guided rotation for one secret
|
||||
|
||||
Claude Code skill: /creds-bootstrap
|
||||
└── guided session for first-time bootstrap (reads credential state,
|
||||
knows what's done, provides KeePassXC entry instructions,
|
||||
warns about time-sensitive steps like enckey-bootstrap)
|
||||
```
|
||||
|
||||
## Dependency on canon standard
|
||||
|
||||
All design decisions in this workplan follow
|
||||
`canon/standards/credential-management_v0.1.md`.
|
||||
The KeePassXC group structure, phase model, SOPS policy, and prohibited
|
||||
patterns defined there are normative. This workplan implements them.
|
||||
|
||||
## Tasks
|
||||
|
||||
### T01 — SOPS integration
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T01
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "2340f2a3-9c11-44a8-b264-41d75b6dbc3e"
|
||||
```
|
||||
|
||||
Add SOPS encryption infrastructure to net-kingdom, aligned with
|
||||
railiance-infra (same age key, same approach).
|
||||
|
||||
**Steps:**
|
||||
|
||||
1. Verify the operator age key exists:
|
||||
```bash
|
||||
ls ~/.config/sops/age/key.txt || age-keygen -o ~/.config/sops/age/key.txt
|
||||
```
|
||||
The public key (`age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4`
|
||||
for the primary operator) is already in railiance-infra. Reuse the same
|
||||
keypair — one age key per operator across all repos.
|
||||
|
||||
2. Create `keys/age.pub` at the repo root:
|
||||
```
|
||||
age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4
|
||||
```
|
||||
|
||||
3. Create `.sops.yaml` at the repo root:
|
||||
```yaml
|
||||
creation_rules:
|
||||
- path_regex: secrets/.*$
|
||||
key_groups:
|
||||
- age:
|
||||
- age1aq8twfd78wvpra0had8cezcnj96tj4q0068edrz5jez8d6xwmflqdepsh4
|
||||
```
|
||||
|
||||
4. Add `secrets/` to `.gitignore` (plaintext secrets MUST NOT enter git).
|
||||
SOPS-encrypted files (`.sops.yaml` extension) may be committed.
|
||||
|
||||
5. Create `.githooks/pre-commit` mirroring railiance-infra:
|
||||
- Blocks any commit that includes a file under `secrets/` lacking
|
||||
`sops:` or `"sops":` marker (i.e. plaintext)
|
||||
- Also blocks any file named `*.env` outside of `sso-mfa/bootstrap/`
|
||||
being committed
|
||||
|
||||
6. `make hooks` target to enable the hook:
|
||||
```makefile
|
||||
hooks:
|
||||
git config core.hooksPath .githooks
|
||||
```
|
||||
|
||||
### T02 — Makefile: SOPS targets
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T02
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "f6ad469c-e1d3-4253-b855-e0554e43f612"
|
||||
```
|
||||
|
||||
Create the top-level `Makefile` for net-kingdom. Port SOPS targets from
|
||||
railiance-infra and add net-kingdom-specific targets.
|
||||
|
||||
**Targets to implement:**
|
||||
|
||||
```makefile
|
||||
## One-time setup
|
||||
sops-setup: # Copy age key to ~/.config/sops/age/keys.txt
|
||||
hooks: # Enable git pre-commit hook
|
||||
|
||||
## SOPS operations
|
||||
sops-edit: # sops <file>
|
||||
sops-encrypt: # sops --encrypt --in-place $(FILE)
|
||||
sops-decrypt: # sops -d $(FILE) (stdout only, never write plaintext to disk)
|
||||
sops-rotate: # sops --rotate --in-place $(FILE) (after adding new recipient)
|
||||
check-secrets: # fail if any secrets/ file is not SOPS-encrypted
|
||||
|
||||
## Credential lifecycle
|
||||
creds-init: # prerequisite check + sops-setup + hooks
|
||||
creds-generate: # run gen-secrets.sh + print KeePassXC entry guide
|
||||
creds-bundle: # run pack-bundle.sh with operator age public key
|
||||
creds-apply: # run all create-secrets.sh in dependency order
|
||||
creds-verify: # check all expected K8s secrets exist
|
||||
creds-status: # print credential state file
|
||||
|
||||
## Single-secret rotation
|
||||
creds-rotate: # guided rotation for SECRET= (generate → KeePassXC → apply → verify)
|
||||
```
|
||||
|
||||
### T03 — Credential orchestrator: `creds-apply` ordering
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T03
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "4b386b92-8db9-440c-b116-52dbb2bd68cb"
|
||||
```
|
||||
|
||||
The `creds-apply` Makefile target must run `create-secrets.sh` scripts in
|
||||
the correct dependency order, with prerequisite checks at each step.
|
||||
|
||||
**Dependency graph:**
|
||||
|
||||
```
|
||||
postgres/create-secrets.sh (no dependencies)
|
||||
│
|
||||
lldap/create-secrets.sh (needs: lldap/secrets.env)
|
||||
│
|
||||
├── authelia/create-secrets.sh (needs: lldap/secrets.env → LLDAP_LDAP_USER_PASS)
|
||||
│
|
||||
└── keycape/create-secrets.sh (needs: lldap/secrets.env + PI_ADMIN_TOKEN)
|
||||
└── PI_ADMIN_TOKEN available only after T04
|
||||
privacyidea/create-secrets.sh (needs: privacyidea/secrets.env)
|
||||
│
|
||||
└── enckey-bootstrap.sh ← TIME-SENSITIVE: must run while pod is live
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
|
||||
Create `sso-mfa/bootstrap/creds-apply.sh` that:
|
||||
1. Checks `KUBECONFIG` is set and cluster is reachable
|
||||
2. Checks each `secrets/<component>/secrets.env` exists before sourcing it
|
||||
3. Runs scripts in order: postgres → lldap → authelia → privacyidea
|
||||
4. Explicitly skips keycape (requires PI_ADMIN_TOKEN from post-T04 bootstrap)
|
||||
5. Prints the keycape step as a manual reminder with the exact command
|
||||
6. On success, updates `sso-mfa/bootstrap/creds-state.yaml`
|
||||
|
||||
### T04 — Credential state file
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T04
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "5bc125a7-ae42-40a3-864c-c356e5fc122d"
|
||||
```
|
||||
|
||||
Create `sso-mfa/bootstrap/creds-state.yaml` — a tracked file (safe to
|
||||
commit, contains no secrets) that records what has been done:
|
||||
|
||||
```yaml
|
||||
# Credential state — net-kingdom SSO/MFA stack
|
||||
# This file is safe to commit. It contains no secrets.
|
||||
# Updated automatically by make creds-* targets.
|
||||
generated_at: null # ISO datetime from last gen-secrets.sh run
|
||||
bundle_at: null # ISO datetime from last pack-bundle.sh run
|
||||
keepass_confirmed: false # Manually set to true after KeePassXC entry
|
||||
|
||||
secrets_applied:
|
||||
postgres: false
|
||||
lldap: false
|
||||
authelia: false
|
||||
privacyidea: false
|
||||
keycape: false # Requires PI_ADMIN_TOKEN (post privacyIDEA T04)
|
||||
|
||||
enckey_bootstrapped: false # Set after enckey-bootstrap.sh runs
|
||||
pi_admin_created: false # Set after bootstrap-admin.sh runs
|
||||
```
|
||||
|
||||
The `make creds-status` target reads this file and prints a human-readable
|
||||
status table. The `make creds-verify` target checks actual K8s secret
|
||||
existence and updates `secrets_applied` accordingly.
|
||||
|
||||
`keepass_confirmed` is the only field that requires manual operator
|
||||
intervention to set to `true` — it represents the irreducibly human step
|
||||
in the bootstrap process.
|
||||
|
||||
### T05 — git pre-commit hook + `check-secrets` gate
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T05
|
||||
status: todo
|
||||
priority: high
|
||||
state_hub_task_id: "d8ea8fbf-ae89-4675-afba-958187ca37f1"
|
||||
```
|
||||
|
||||
Implement `.githooks/pre-commit` that prevents plaintext secrets from
|
||||
entering git. Port from railiance-infra with net-kingdom-specific additions:
|
||||
|
||||
**Blocks:**
|
||||
- Any file under `secrets/` without a SOPS marker
|
||||
- Any file matching `*.env` outside of `sso-mfa/bootstrap/`
|
||||
- Any file containing any of these patterns: `PI_SECRET_KEY=`, `PI_PEPPER=`,
|
||||
`LLDAP_JWT_SECRET=`, `AUTHELIA_`, `BREAKGLASS_PASSWORD=`
|
||||
|
||||
**Warning only (does not block):**
|
||||
- Files matching `*-bundle*.tar.age` being committed (large encrypted
|
||||
artifacts belong offsite, not in git)
|
||||
|
||||
Add `make hooks-test` target that verifies the hook blocks plaintext
|
||||
(mirrors railiance-infra pattern).
|
||||
|
||||
### T06 — Claude Code skill: `/creds-bootstrap`
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T06
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "b9ecbd3f-17f0-4c1d-97e5-84bfbb43d360"
|
||||
```
|
||||
|
||||
Create `~/.claude/commands/creds-bootstrap.md` — a Claude Code skill that
|
||||
provides guided assistance during the credential bootstrap process.
|
||||
|
||||
**When to use it:** First-time bootstrap or onboarding a new operator.
|
||||
The skill reads `sso-mfa/bootstrap/creds-state.yaml` and provides
|
||||
contextual guidance based on what has been done.
|
||||
|
||||
**Skill behavior:**
|
||||
1. Read `creds-state.yaml` to determine current state
|
||||
2. Identify the next required step (first `false` in dependency order)
|
||||
3. For KeePassXC entry steps: display the exact group path and field names
|
||||
to enter, with values sourced from `secrets/` env files (if present)
|
||||
4. For time-sensitive steps (enckey-bootstrap): print a prominent warning
|
||||
with the exact command and timing constraint
|
||||
5. For verification steps: run `make creds-verify` and interpret results
|
||||
6. After each confirmed step: prompt operator to update `creds-state.yaml`
|
||||
or do it automatically when the state can be derived from cluster state
|
||||
|
||||
**Skill definition file structure:**
|
||||
```yaml
|
||||
---
|
||||
description: "Guide through net-kingdom credential bootstrap. Reads creds-state.yaml and provides step-by-step KeePassXC entry instructions, timing warnings, and verification."
|
||||
argument-hint: "[--repo-path /path/to/net-kingdom]"
|
||||
allowed-tools:
|
||||
- Read
|
||||
- Bash(make creds-status:*)
|
||||
- Bash(make creds-verify:*)
|
||||
- Bash(kubectl get secret:*)
|
||||
---
|
||||
```
|
||||
|
||||
**Note:** The skill does NOT automate KeePassXC entry (that remains a
|
||||
human step). It provides the information an operator needs to do it
|
||||
correctly and verifies the result afterwards.
|
||||
|
||||
### T07 — Secret rotation runbook
|
||||
|
||||
```task
|
||||
id: NK-WP-0004-T07
|
||||
status: todo
|
||||
priority: medium
|
||||
state_hub_task_id: "e27762d9-aa6a-4a7e-9c34-f8c546797548"
|
||||
```
|
||||
|
||||
Document and automate the rotation procedure for each secret type.
|
||||
Different secrets have different rotation complexity:
|
||||
|
||||
| Secret | Rotation impact | Procedure |
|
||||
|--------|----------------|-----------|
|
||||
| PI_SECRET_KEY | Flask session reset — all users logged out | Stop pod, rotate, restart |
|
||||
| PI_PEPPER | Cannot rotate without re-hashing all passwords | Treat as permanent |
|
||||
| PI_DB_PASSWORD | DB + K8s Secret must be rotated atomically | pg GRANT + Secret update |
|
||||
| LLDAP_JWT_SECRET | All LLDAP sessions invalidated | Rotate Secret, restart pod |
|
||||
| LLDAP_LDAP_USER_PASS | Must update LLDAP + Authelia + KeyCape atomically | 3-step coordinated |
|
||||
| AUTHELIA_SESSION_SECRET | All Authelia sessions invalidated | Rotate, restart |
|
||||
| AUTHELIA_KEYCAPE_CLIENT_SECRET | Must update Authelia (bcrypt) + KeyCape simultaneously | Coordinated 2-step |
|
||||
| KeyCape RSA signing key | All issued tokens immediately invalidated | Brief auth outage |
|
||||
| PI_ENCFILE | Cannot rotate — replace and re-enroll all tokens | Major operation |
|
||||
| BREAKGLASS_PASSWORD | Low impact, rotate freely | Simple update |
|
||||
|
||||
Implement `make creds-rotate SECRET=<name>` that:
|
||||
1. Validates the secret name is known
|
||||
2. Prints the rotation impact and required coordination steps
|
||||
3. Generates a new value (same entropy as original)
|
||||
4. Guides through the atomic update sequence for that secret
|
||||
5. Updates `creds-state.yaml` and ops bundle after rotation
|
||||
|
||||
## Done criteria
|
||||
|
||||
- [ ] `make creds-init` runs cleanly on a fresh workstation (age key check + setup)
|
||||
- [ ] `make creds-generate` produces all secrets and prints KeePassXC entry guide
|
||||
- [ ] `make creds-bundle` produces an age-encrypted ops bundle
|
||||
- [ ] `make creds-apply` runs all `create-secrets.sh` scripts in dependency order
|
||||
- [ ] `make creds-verify` accurately reflects K8s secret state
|
||||
- [ ] `make creds-status` shows a readable state table from `creds-state.yaml`
|
||||
- [ ] `make hooks-test` confirms pre-commit hook blocks plaintext commits
|
||||
- [ ] `/creds-bootstrap` skill loads, reads state, and provides correct next step
|
||||
- [ ] NK-WP-0003-T01 can be marked done by referencing this workplan as complete
|
||||
Reference in New Issue
Block a user