feat(creds): implement NK-WP-0004 Credential Management Foundation
- .sops.yaml + keys/age.pub: SOPS age encryption for all secrets/ paths
- .gitignore: broad secrets/ catch-all (any depth)
- .githooks/pre-commit: blocks unencrypted secrets/, *.env outside bootstrap/,
and known plaintext patterns (PI_SECRET_KEY=, LLDAP_JWT_SECRET=, etc.)
- Makefile: full credential lifecycle (creds-init/generate/bundle/apply/verify/
status/rotate) + SOPS helpers (sops-setup/edit/encrypt/decrypt/rotate/check-secrets)
+ hooks/hooks-test
- creds-apply.sh: runs create-secrets.sh in dependency order (postgresql → lldap →
authelia → privacyidea), skips keycape with printed instructions, updates state
- creds-verify.sh: checks all K8s secrets exist, updates creds-state.yaml
- creds-status.sh: human-readable state table from creds-state.yaml
- creds-rotate.sh: guided rotation for all 9 secret types with impact descriptions
and atomic multi-component update sequences
- creds-state.yaml: committable state file tracking generation, bundle, KeePassXC
confirmation, per-component apply status, enckey and pi-admin bootstrap flags
NK-WP-0003-T01 unblocked. /creds-bootstrap skill registered separately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 23:39:35 +00:00
SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
# Operator age public key — used as bundle encryption recipient
OPERATOR_AGE_PUBKEY := $( shell cat keys/age.pub 2>/dev/null | tr -d '[:space:]' )
# ── Help ──────────────────────────────────────────────────────────────────────
help : ## Show this help
@echo "net-kingdom — available targets"
@echo ""
@grep -E '^[a-zA-Z0-9_-]+:.*?## ' $( MAKEFILE_LIST) \
| sort \
| awk 'BEGIN {FS = ":.*?## "}; {printf " %-28s %s\n", $$1, $$2}'
# ── Git hooks ─────────────────────────────────────────────────────────────────
hooks : ## Configure git to use repo-local hooks (.githooks)
@mkdir -p .githooks
git config core.hooksPath .githooks
@test -f .githooks/pre-commit \
|| ( echo "ERROR: .githooks/pre-commit not found" ; exit 1)
chmod +x .githooks/pre-commit
@echo "✔ git hooks enabled (.githooks/pre-commit is active)"
hooks-test : ## Test that the pre-commit hook blocks plaintext secrets
@mkdir -p sso-mfa/bootstrap/secrets/_hooktest
@echo 'PI_SECRET_KEY=deadbeef' > sso-mfa/bootstrap/secrets/_hooktest/test.env
@git add sso-mfa/bootstrap/secrets/_hooktest/test.env 2>/dev/null || true
@if git commit -m "TEST: hook must block this" 2>/dev/null; then \
echo "FAIL: hook did NOT block plaintext commit" ; \
git reset --soft HEAD~1 2>/dev/null || true; \
else \
echo "✔ Hook blocked plaintext as expected" ; \
fi
@git restore --staged sso-mfa/bootstrap/secrets/_hooktest/test.env 2>/dev/null || true
@rm -rf sso-mfa/bootstrap/secrets/_hooktest
# ── SOPS / age ────────────────────────────────────────────────────────────────
sops-setup : ## Copy age key to SOPS default path (~/.config/sops/age/keys.txt)
@mkdir -p ~/.config/sops/age
@if [ [ -f ~/.config/age/key.txt ] ] ; then \
cp -n ~/.config/age/key.txt ~/.config/sops/age/keys.txt || true; \
chmod 600 ~/.config/sops/age/keys.txt; \
echo "✔ SOPS key ready at ~/.config/sops/age/keys.txt" ; \
elif [ [ -f ~/.config/sops/age/keys.txt ] ] ; then \
echo "✔ SOPS key already present at ~/.config/sops/age/keys.txt" ; \
else \
echo "ERROR: age key not found at ~/.config/age/key.txt" ; \
echo " Generate one with: age-keygen -o ~/.config/age/key.txt" ; \
exit 1; \
fi
sops-edit : ## Edit an encrypted file with SOPS: make sops-edit FILE=secrets/foo.yaml
@[ [ -n " $( FILE) " ] ] || ( echo "Usage: make sops-edit FILE=secrets/path/to/file.yaml" ; exit 1)
sops $( FILE)
sops-encrypt : ## Encrypt a file in place: make sops-encrypt FILE=secrets/foo.yaml
@[ [ -n " $( FILE) " ] ] || ( echo "Usage: make sops-encrypt FILE=secrets/path/to/file.yaml" ; exit 1)
sops --encrypt --in-place $( FILE)
@echo " ✔ Encrypted $( FILE) "
sops-decrypt : ## Decrypt a file to stdout (never writes plaintext to disk): make sops-decrypt FILE=secrets/foo.yaml
@[ [ -n " $( FILE) " ] ] || ( echo "Usage: make sops-decrypt FILE=secrets/path/to/file.yaml" ; exit 1)
sops -d $( FILE)
sops-rotate : ## Rotate SOPS recipients after updating .sops.yaml: make sops-rotate FILE=secrets/foo.yaml
@[ [ -n " $( FILE) " ] ] || ( echo "Usage: make sops-rotate FILE=secrets/path/to/file.yaml" ; exit 1)
sops --rotate --in-place $( FILE)
@echo " ✔ Recipients rotated for $( FILE) "
check-secrets : ## Fail if any file under secrets/ is not SOPS-encrypted
@echo "Checking for unencrypted files under secrets/..."
@bad= 0; \
while IFS = read -r f; do \
[ [ -z " $$ f " ] ] && continue ; \
if grep -qE '^[[:space:]]*sops:[[:space:]]*$$|"sops"[[:space:]]*:' " $$ f " 2>/dev/null; then \
continue ; \
fi ; \
case " $$ f " in *.age| *.gpg) continue ; ; esac ; \
echo " UNENCRYPTED: $$ f " ; bad = 1; \
done < <( git ls-files --others --cached 'secrets' 2>/dev/null | grep -v '/$' ) ; \
if [ [ " $$ bad " -ne 0 ] ] ; then \
echo "" ; \
echo "ERROR: Unencrypted secret(s) detected. Encrypt with: sops --encrypt --in-place <file>" ; \
exit 1; \
fi ; \
echo "✔ All secrets/ files appear SOPS-encrypted"
# ── Credential lifecycle ──────────────────────────────────────────────────────
creds-init : sops -setup hooks ## One-time setup: verify prerequisites, configure SOPS and git hooks
@echo "=== creds-init: checking prerequisites ==="
@command -v age >/dev/null 2>& 1 || ( echo "ERROR: age not installed (apt install age)" ; exit 1)
@command -v sops >/dev/null 2>& 1 || ( echo "ERROR: sops not installed (see https://github.com/getsops/sops/releases)" ; exit 1)
@command -v kubectl >/dev/null 2>& 1 \
&& echo "✔ kubectl found" \
|| echo "WARN: kubectl not found (required for creds-apply/verify)"
@test -f ~/.config/sops/age/keys.txt \
|| ( echo "ERROR: age key not found — run 'make sops-setup' after placing your key at ~/.config/age/key.txt" ; exit 1)
@echo ""
@echo "✔ creds-init complete. Next: make creds-generate"
creds-generate : ## Generate all service secrets and print KeePassXC entry guide
@command -v openssl >/dev/null 2>& 1 || ( echo "ERROR: openssl not found" ; exit 1)
cd sso-mfa/bootstrap && bash gen-secrets.sh
@sed -i " s|^generated_at: .*|generated_at: $$ (date -Iseconds)| " sso-mfa/bootstrap/creds-state.yaml
@echo ""
@echo "State: generated_at updated in sso-mfa/bootstrap/creds-state.yaml"
@echo "Next : enter values into KeePassXC, then set keepass_confirmed: true"
@echo " Run 'make creds-bundle' to create an encrypted offsite backup."
creds-bundle : ## Age-encrypt the ops bundle for offsite storage
@[ [ -n " $( OPERATOR_AGE_PUBKEY) " ] ] \
|| ( echo "ERROR: keys/age.pub not found — cannot determine encryption recipient" ; exit 1)
@[ [ -d sso-mfa/bootstrap/secrets ] ] \
|| ( echo "ERROR: sso-mfa/bootstrap/secrets/ not found — run 'make creds-generate' first" ; exit 1)
cd sso-mfa/bootstrap && bash pack-bundle.sh secrets " $( OPERATOR_AGE_PUBKEY) "
@sed -i " s|^bundle_at: .*|bundle_at: $$ (date -Iseconds)| " sso-mfa/bootstrap/creds-state.yaml
@echo ""
@echo "State: bundle_at updated in sso-mfa/bootstrap/creds-state.yaml"
@echo "Store the .tar.age bundle offsite (external drive, encrypted cloud, second location)."
creds-apply : ## Apply all create-secrets.sh scripts to the cluster in dependency order
bash sso-mfa/bootstrap/creds-apply.sh
creds-verify : ## Check all expected K8s secrets exist and update creds-state.yaml
bash sso-mfa/bootstrap/creds-verify.sh
creds-status : ## Print human-readable credential state from creds-state.yaml
bash sso-mfa/bootstrap/creds-status.sh
creds-rotate : ## Guided rotation for one secret: make creds-rotate SECRET=<name>
@[ [ -n " $( SECRET) " ] ] \
|| ( echo "Usage: make creds-rotate SECRET=<name>" ; \
echo "" ; \
echo "Known secrets:" ; \
echo " PI_SECRET_KEY PI_PEPPER PI_DB_PASSWORD LLDAP_JWT_SECRET" ; \
echo " LLDAP_LDAP_USER_PASS AUTHELIA_SESSION_SECRET" ; \
echo " AUTHELIA_KEYCAPE_CLIENT_SECRET KEYCAPE_RSA_KEY" ; \
echo " BREAKGLASS_PASSWORD" ; \
exit 1)
SECRET = $( SECRET) bash sso-mfa/bootstrap/creds-rotate.sh
feat(creds): NK-WP-0005 — agent-driven credential bootstrap
Implements all 7 tasks from NK-WP-0005:
T01: creds-state.yaml → schema_version: 2, agent_mode: true
Replaces keepass_confirmed with emergency_bundle_delivered,
adds phase tracking fields for fully automated flow.
T02: creds-bootstrap-agent.sh — single entrypoint for autonomous
bootstrap. 10 phases, idempotent re-runs via state file.
Only human touchpoint: emergency bundle confirmation gate.
T03: emergency-bundle.sh — assembles and displays emergency bundle
(age key + break-glass passwords + ops bundle location).
Writes temp file, shreds on confirmation, clears screen.
Supports --reprint for re-delivery.
T04: ~/.claude/commands/creds-init.md — /creds-init skill replaces
/creds-bootstrap. Fully autonomous execution via the agent.
T05: Makefile — creds-agent-init, creds-agent-status,
creds-emergency-reprint targets.
T06: creds-rotate.sh — --non-interactive flag for agent-driven
rotation. Auto-confirms all gates; tracks last_rotated_<key>
in creds-state.yaml. LLDAP web UI step prints warning in
non-interactive mode.
T07: canon/standards/credential-management_v0.2.md — updated
standard: KeePassXC removed from operational path, agent
bootstrap as Phase 0, emergency bundle section, prohibited
patterns updated.
Also: creds-status.sh handles both schema v1 (legacy) and v2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:38:52 +00:00
## ── Agent-driven credential lifecycle (NK-WP-0005) ──────────────────────────
creds-agent-init : ## Fully automated credential bootstrap — generates, encrypts, injects, delivers emergency bundle
@bash sso-mfa/bootstrap/creds-bootstrap-agent.sh
creds-agent-status : ## Show current v2 bootstrap state (agent mode)
@bash sso-mfa/bootstrap/creds-status.sh --v2
creds-emergency-reprint : ## Re-deliver emergency bundle (if lost/stolen — reprints, rotates nothing)
@bash sso-mfa/bootstrap/emergency-bundle.sh --reprint
2026-05-22 14:35:31 +02:00
iam-profile-conformance-test : ## Run IAM Profile v0.2 conformance fixture tests
python3 -m pytest tools/iam-profile-conformance/tests
2026-05-22 14:49:25 +02:00
playbook-contract-test : ## Run Playbook Capability Contract fixture tests
python3 -m pytest tools/playbook-capability-contract/tests
2026-05-24 09:26:02 +02:00
security-bootstrap-console : ## Show guided security bootstrap status and safe actions
python3 tools/security-bootstrap-console/security_bootstrap_console.py status
security-bootstrap-king-kit : ## Print the king credential kit checklist
python3 tools/security-bootstrap-console/security_bootstrap_console.py king-kit
security-bootstrap-validate-kit : ## Validate non-secret king credential metadata: make security-bootstrap-validate-kit METADATA=/tmp/security-bootstrap.json
@[ [ -n " $( METADATA) " ] ] || ( echo "Usage: make security-bootstrap-validate-kit METADATA=/path/to/non-secret.json" ; exit 1)
python3 tools/security-bootstrap-console/security_bootstrap_console.py \
--metadata " $( METADATA) " \
validate-king-kit
security-bootstrap-approve-custody : ## Approve custody mode metadata: make security-bootstrap-approve-custody METADATA=/tmp/security-bootstrap.json ARGS="--mfa-enrolled-confirmed --mfa-enrollment-source identity-provider --recovery-confirmed --custody-packet-prepared --no-secret-capture-confirmed"
@[ [ -n " $( METADATA) " ] ] || ( echo "Usage: make security-bootstrap-approve-custody METADATA=/path/to/non-secret.json ARGS='--mfa-enrolled-confirmed --mfa-enrollment-source identity-provider --recovery-confirmed --custody-packet-prepared --no-secret-capture-confirmed'" ; exit 1)
python3 tools/security-bootstrap-console/security_bootstrap_console.py \
--metadata " $( METADATA) " \
approve-custody-mode \
--mode " $( if $( MODE) ,$( MODE) ,temporary-single-king) " \
$( ARGS)
security-bootstrap-custody-packet : ## Print a blank offline custody packet template
python3 tools/security-bootstrap-console/security_bootstrap_console.py custody-packet
security-bootstrap-openbao-preflight : ## Show safe OpenBao preflight commands
python3 tools/security-bootstrap-console/security_bootstrap_console.py openbao-preflight \
--railiance-path ../railiance-platform
security-bootstrap-ui : ## Serve local custody approval UI: make security-bootstrap-ui METADATA=/tmp/security-bootstrap.json PORT=8765
python3 tools/security-bootstrap-console/security_bootstrap_console.py \
--metadata " $( if $( METADATA) ,$( METADATA) ,/tmp/net-kingdom-security-bootstrap.json) " \
web-ui \
--host " $( if $( HOST) ,$( HOST) ,127.0.0.1) " \
--port " $( if $( PORT) ,$( PORT) ,8765) "
feat(creds): implement NK-WP-0004 Credential Management Foundation
- .sops.yaml + keys/age.pub: SOPS age encryption for all secrets/ paths
- .gitignore: broad secrets/ catch-all (any depth)
- .githooks/pre-commit: blocks unencrypted secrets/, *.env outside bootstrap/,
and known plaintext patterns (PI_SECRET_KEY=, LLDAP_JWT_SECRET=, etc.)
- Makefile: full credential lifecycle (creds-init/generate/bundle/apply/verify/
status/rotate) + SOPS helpers (sops-setup/edit/encrypt/decrypt/rotate/check-secrets)
+ hooks/hooks-test
- creds-apply.sh: runs create-secrets.sh in dependency order (postgresql → lldap →
authelia → privacyidea), skips keycape with printed instructions, updates state
- creds-verify.sh: checks all K8s secrets exist, updates creds-state.yaml
- creds-status.sh: human-readable state table from creds-state.yaml
- creds-rotate.sh: guided rotation for all 9 secret types with impact descriptions
and atomic multi-component update sequences
- creds-state.yaml: committable state file tracking generation, bundle, KeePassXC
confirmation, per-component apply status, enckey and pi-admin bootstrap flags
NK-WP-0003-T01 unblocked. /creds-bootstrap skill registered separately.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-20 23:39:35 +00:00
.PHONY : help hooks hooks -test sops -setup sops -edit sops -encrypt sops -decrypt sops -rotate \
check-secrets creds-init creds-generate creds-bundle creds-apply creds-verify \
feat(creds): NK-WP-0005 — agent-driven credential bootstrap
Implements all 7 tasks from NK-WP-0005:
T01: creds-state.yaml → schema_version: 2, agent_mode: true
Replaces keepass_confirmed with emergency_bundle_delivered,
adds phase tracking fields for fully automated flow.
T02: creds-bootstrap-agent.sh — single entrypoint for autonomous
bootstrap. 10 phases, idempotent re-runs via state file.
Only human touchpoint: emergency bundle confirmation gate.
T03: emergency-bundle.sh — assembles and displays emergency bundle
(age key + break-glass passwords + ops bundle location).
Writes temp file, shreds on confirmation, clears screen.
Supports --reprint for re-delivery.
T04: ~/.claude/commands/creds-init.md — /creds-init skill replaces
/creds-bootstrap. Fully autonomous execution via the agent.
T05: Makefile — creds-agent-init, creds-agent-status,
creds-emergency-reprint targets.
T06: creds-rotate.sh — --non-interactive flag for agent-driven
rotation. Auto-confirms all gates; tracks last_rotated_<key>
in creds-state.yaml. LLDAP web UI step prints warning in
non-interactive mode.
T07: canon/standards/credential-management_v0.2.md — updated
standard: KeePassXC removed from operational path, agent
bootstrap as Phase 0, emergency bundle section, prohibited
patterns updated.
Also: creds-status.sh handles both schema v1 (legacy) and v2.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:38:52 +00:00
creds-status creds-rotate \
2026-05-22 14:35:31 +02:00
creds-agent-init creds-agent-status creds-emergency-reprint \
2026-05-24 09:26:02 +02:00
iam-profile-conformance-test playbook-contract-test \
security-bootstrap-console security-bootstrap-king-kit \
security-bootstrap-validate-kit security-bootstrap-approve-custody \
security-bootstrap-custody-packet security-bootstrap-openbao-preflight \
security-bootstrap-ui