feat(sso-mfa): T02/T03 live apply — age-encrypted secrets, CNPG cluster (NK-WP-0001-T02/T03)

- Add encrypt-secrets.sh / decrypt-secrets.sh: age-based secrets workflow
  replaces KeePassXC dependency; encrypted .env.age files committed to repo
- Add bootstrap/secrets.enc/: all component secrets encrypted to age pubkey
- Fix .gitignore: allow secrets.enc/**/*.age while blocking plaintext
- Fix verify-t02.sh: update netpol names for Authelia+LLDAP+KeyCape stack
- Fix verify-t03.sh: remove keycloak_db/role checks; fix ((PASS++)) set-e bug
- Update postgresql/cluster.yaml: drop keycloak_db, bootstrap privacyidea_db only
- Update postgresql/create-secrets.sh: remove keycloak secret
- Fix netpol-databases.yaml: add port 8000 for CNPG instance manager HTTP API
- T02 COMPLETE: namespaces, network policies, cert-manager issuers applied
- T03 COMPLETE: CNPG operator installed, net-kingdom-pg cluster healthy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 02:57:41 +00:00
parent 0d5d12cb67
commit 6d25d088d7
14 changed files with 181 additions and 66 deletions

View File

@@ -0,0 +1,62 @@
#!/usr/bin/env bash
# decrypt-secrets.sh — decrypt secrets.enc/ to secrets/ using age
#
# Usage:
# ./decrypt-secrets.sh [OUTPUT_DIR] [AGE_KEY_FILE]
#
# OUTPUT_DIR where to write plaintext secrets (default: ./secrets)
# AGE_KEY_FILE age private key file (default: ~/.config/net-kingdom/age.key)
#
# Decrypts all *.age files in secrets.enc/ to OUTPUT_DIR for use by
# create-secrets.sh scripts. Shred OUTPUT_DIR when done:
# find secrets/ -type f -exec shred -u {} \; && rm -rf secrets/
#
# The age key must be present on the machine. Keep it outside the repo:
# ~/.config/net-kingdom/age.key
set -euo pipefail
OUTPUT_DIR="${1:-./secrets}"
AGE_KEY="${2:-$HOME/.config/net-kingdom/age.key}"
ENC_DIR="$(dirname "$OUTPUT_DIR")/secrets.enc"
if [[ ! -d "$ENC_DIR" ]]; then
echo "ERROR: encrypted secrets directory not found: $ENC_DIR" >&2
echo "Expected secrets.enc/ next to the output directory." >&2
exit 1
fi
if [[ ! -f "$AGE_KEY" ]]; then
echo "ERROR: age key not found: $AGE_KEY" >&2
echo "Copy your age key to $AGE_KEY or pass the path as the second argument." >&2
exit 1
fi
if [[ -e "$OUTPUT_DIR" ]]; then
echo "ERROR: $OUTPUT_DIR already exists. Remove it first or choose a different path." >&2
exit 1
fi
echo "Decrypting $ENC_DIR$OUTPUT_DIR/"
echo ""
count=0
for component_dir in "$ENC_DIR"/*/; do
component=$(basename "$component_dir")
mkdir -p "$OUTPUT_DIR/$component"
for f in "$component_dir"*.age; do
[[ -f "$f" ]] || continue
fname=$(basename "${f%.age}")
out="$OUTPUT_DIR/$component/$fname"
age -d -i "$AGE_KEY" -o "$out" "$f"
echo " decrypted: secrets.enc/$component/$(basename "$f")$component/$fname"
count=$((count + 1))
done
done
echo ""
echo "$count file(s) decrypted to $OUTPUT_DIR/"
echo ""
echo "Use create-secrets.sh scripts, then shred:"
echo " find $OUTPUT_DIR -type f -exec shred -u {} \\; && rm -rf $OUTPUT_DIR"

View File

@@ -0,0 +1,79 @@
#!/usr/bin/env bash
# encrypt-secrets.sh — encrypt secrets/ directory to secrets.enc/ using age
#
# Usage:
# ./encrypt-secrets.sh [SECRETS_DIR] [AGE_KEY_FILE]
#
# SECRETS_DIR plaintext secrets directory (default: ./secrets)
# AGE_KEY_FILE age private key file (default: ~/.config/net-kingdom/age.key)
#
# Reads the public key from the age key file and encrypts each *.env file
# (and pi.enc if present) to secrets.enc/<component>/<filename>.age.
#
# After a successful encrypt, shreds the plaintext secrets directory unless
# --no-shred is passed.
#
# Run after gen-secrets.sh to store secrets safely in the repo.
# Commit secrets.enc/ to git.
set -euo pipefail
SECRETS_DIR="${1:-./secrets}"
AGE_KEY="${2:-$HOME/.config/net-kingdom/age.key}"
NO_SHRED=false
for arg in "$@"; do [[ "$arg" == "--no-shred" ]] && NO_SHRED=true; done
if [[ ! -d "$SECRETS_DIR" ]]; then
echo "ERROR: secrets directory not found: $SECRETS_DIR" >&2
echo "Run gen-secrets.sh first." >&2
exit 1
fi
if [[ ! -f "$AGE_KEY" ]]; then
echo "ERROR: age key not found: $AGE_KEY" >&2
echo "Generate with: age-keygen -o $AGE_KEY" >&2
exit 1
fi
# Extract public key from the private key file
PUBKEY=$(grep 'public key:' "$AGE_KEY" | awk '{print $NF}')
if [[ -z "$PUBKEY" ]]; then
echo "ERROR: could not read public key from $AGE_KEY" >&2
exit 1
fi
ENC_DIR="$(dirname "$SECRETS_DIR")/secrets.enc"
mkdir -p "$ENC_DIR"
echo "Encrypting secrets → $ENC_DIR/"
echo "Recipient: $PUBKEY"
echo ""
count=0
for component_dir in "$SECRETS_DIR"/*/; do
component=$(basename "$component_dir")
mkdir -p "$ENC_DIR/$component"
for f in "$component_dir"*; do
[[ -f "$f" ]] || continue
fname=$(basename "$f")
out="$ENC_DIR/$component/$fname.age"
age -r "$PUBKEY" -o "$out" "$f"
echo " encrypted: $component/$fname → secrets.enc/$component/$fname.age"
count=$((count + 1))
done
done
echo ""
echo "$count file(s) encrypted to $ENC_DIR/"
echo ""
if [[ "$NO_SHRED" == false ]]; then
echo "Shredding plaintext secrets..."
find "$SECRETS_DIR" -type f -exec shred -u {} \;
rm -rf "$SECRETS_DIR"
echo "Done. Plaintext secrets shredded."
else
echo "(--no-shred: plaintext kept at $SECRETS_DIR)"
fi
echo ""
echo "Next: commit secrets.enc/ to git."

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,7 @@
age-encryption.org/v1
-> X25519 yR2D3J78/vw1ohcdXCLy5IOoIuG+FtRs7Eiswk3gKyo
c9axBYTsFS4Gqb3Zdv5Gtk+/yEtKNH21iFLU1U3mxNs
--- Kc/0n9icRSyEEcAHJJdx2Vcv5CgjLucU8FdZArV3C2U
ìÏ9ÍôeY<EFBFBD>œ·ŒdT -GÄëÊ%½0xž ày=„0úOñî—Ö«ü豃־Qÿ"ú-[gßÁóÐ3eýœV3”<33>wt1½º<>“Cä$rj2\zû=IW ï7>=Ž<4B>ª8JUT¡G†læ"bv{g3@þ-¡â:Ƚ™2£;ÖÍPrÕUH<55>­Aö-Æë<C386>°ZØÌx¦„«.ïÑx}@EMž“+©ÚHÐ
€Óš´$¤Î;”¤¶>iûáÕe˜ò1xtCÌU¡4¹àÜÒO®¦zÃ
Žý<EFBFBD>O{qãÔ<C3A3>qE¬Ù¡?àS<ÂsµÎg©XL<58>¬ÎÂþy«í'‚¶Ùñ «f[넪Òü6<C3BC>°W£@C{‡¢#ö<>ñƒÅ9<C385>÷Î ¤%ò2³~ªyQ™(¥c ;¿ìùÄ͆’«#l`}uNÖ»Ž

Binary file not shown.