generated from coulomb/repo-seed
Replaces the Keycloak+privacyIDEA SSO tier with the lightweight stack built during KEY-WP-0001: Authelia (password frontend), LLDAP (directory), and KeyCape (OIDC orchestration). privacyIDEA is retained as the MFA engine. Stack: kc.coulomb.social — KeyCape OIDC server (stateless, custom Go) auth.coulomb.social — Authelia login portal (password auth → Authelia OIDC → KeyCape) lldap.coulomb.social — LLDAP admin UI (IP-restricted) pink.coulomb.social — privacyIDEA MFA engine (unchanged) Changes: - Remove sso-mfa/k8s/keycloak/ (7 files) - Add sso-mfa/k8s/lldap/ (pvc, deployment, middleware, ingress, create-secrets, README) - Add sso-mfa/k8s/authelia/ (pvc, configmap, deployment, ingress, create-secrets, README) - Add sso-mfa/k8s/keycape/ (deployment, middleware, ingress, create-secrets, create-pi-token, README) - Update network-policies/netpol-sso.yaml for new component topology - Update verify-t05.sh: checks LLDAP + Authelia + KeyCape (23 checks) - Update CONFIG.md: fix CP-NK-004 (KeyCape), add CP-NK-005 (Authelia), CP-NK-006 (LLDAP) - Update bootstrap/gen-secrets.sh: add LLDAP/Authelia/KeyCape sections, remove Keycloak - Update k8s/README.md: network policy table reflects new traffic paths - Add sso-mfa/WORKPLAN.md: resumable task checklist Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
114 lines
5.2 KiB
Bash
114 lines
5.2 KiB
Bash
#!/usr/bin/env bash
|
|
# create-secrets.sh — create the authelia-secrets K8s Secret
|
|
#
|
|
# Usage:
|
|
# ./create-secrets.sh [secrets-dir]
|
|
#
|
|
# <secrets-dir> is the output directory from sso-mfa/bootstrap/gen-secrets.sh
|
|
# (default: ../../bootstrap/secrets).
|
|
#
|
|
# Creates ONE Secret in the sso namespace:
|
|
# authelia-secrets — all Authelia sensitive values as named files:
|
|
# jwt_secret ← AUTHELIA_JWT_SECRET_FILE
|
|
# session_secret ← AUTHELIA_SESSION_SECRET_FILE
|
|
# storage_encryption_key ← AUTHELIA_STORAGE_ENCRYPTION_KEY_FILE
|
|
# ldap_password ← AUTHELIA_AUTHENTICATION_BACKEND_LDAP_PASSWORD_FILE
|
|
# oidc_hmac_secret ← AUTHELIA_IDENTITY_PROVIDERS_OIDC_HMAC_SECRET_FILE
|
|
# oidc_issuer_private_key ← AUTHELIA_IDENTITY_PROVIDERS_OIDC_ISSUER_PRIVATE_KEY_FILE
|
|
# keycape_client_secret_hash ← AUTHELIA_IDENTITY_PROVIDERS_OIDC_CLIENTS_0_SECRET_FILE
|
|
#
|
|
# The keycape_client_secret_hash is the bcrypt hash of AUTHELIA_KEYCAPE_CLIENT_SECRET
|
|
# from secrets/authelia/secrets.env. The plaintext is stored in KeyCape's secret.
|
|
#
|
|
# Requires: kubectl, openssl, python3 (for bcrypt hash generation)
|
|
|
|
set -euo pipefail
|
|
|
|
SECRETS_DIR="${1:-../../bootstrap/secrets}"
|
|
AUTHELIA_ENV="$SECRETS_DIR/authelia/secrets.env"
|
|
LLDAP_ENV="$SECRETS_DIR/lldap/secrets.env"
|
|
|
|
for f in "$AUTHELIA_ENV" "$LLDAP_ENV"; do
|
|
if [[ ! -f "$f" ]]; then
|
|
echo "ERROR: $f not found" >&2
|
|
echo "Run sso-mfa/bootstrap/gen-secrets.sh first." >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
read_env() { bash -c "source '$1' 2>/dev/null; echo \${$2}"; }
|
|
|
|
AUTHELIA_JWT_SECRET=$(read_env "$AUTHELIA_ENV" AUTHELIA_JWT_SECRET)
|
|
AUTHELIA_SESSION_SECRET=$(read_env "$AUTHELIA_ENV" AUTHELIA_SESSION_SECRET)
|
|
AUTHELIA_STORAGE_ENCRYPTION_KEY=$(read_env "$AUTHELIA_ENV" AUTHELIA_STORAGE_ENCRYPTION_KEY)
|
|
AUTHELIA_OIDC_HMAC_SECRET=$(read_env "$AUTHELIA_ENV" AUTHELIA_OIDC_HMAC_SECRET)
|
|
AUTHELIA_OIDC_PRIVATE_KEY=$(read_env "$AUTHELIA_ENV" AUTHELIA_OIDC_PRIVATE_KEY_FILE)
|
|
AUTHELIA_KEYCAPE_CLIENT_SECRET=$(read_env "$AUTHELIA_ENV" AUTHELIA_KEYCAPE_CLIENT_SECRET)
|
|
LLDAP_LDAP_USER_PASS=$(read_env "$LLDAP_ENV" LLDAP_LDAP_USER_PASS)
|
|
|
|
# Validate required values
|
|
REQUIRED=(AUTHELIA_JWT_SECRET AUTHELIA_SESSION_SECRET AUTHELIA_STORAGE_ENCRYPTION_KEY
|
|
AUTHELIA_OIDC_HMAC_SECRET AUTHELIA_KEYCAPE_CLIENT_SECRET LLDAP_LDAP_USER_PASS)
|
|
for var in "${REQUIRED[@]}"; do
|
|
if [[ -z "${!var}" ]]; then
|
|
echo "ERROR: $var is empty — re-run gen-secrets.sh" >&2
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
# The OIDC issuer private key is stored as a file path in secrets.env.
|
|
# Read the actual PEM content from the path referenced there.
|
|
AUTHELIA_OIDC_PRIVATE_KEY_PATH=$(read_env "$AUTHELIA_ENV" AUTHELIA_OIDC_PRIVATE_KEY_FILE)
|
|
if [[ -z "$AUTHELIA_OIDC_PRIVATE_KEY_PATH" ]]; then
|
|
# Fall back: generate a new RSA key on the fly (dev only)
|
|
echo "WARN: AUTHELIA_OIDC_PRIVATE_KEY_FILE not set — generating a new RSA-2048 key."
|
|
echo " Store the generated key in KeePassXC as net-kingdom/Authelia/oidc-private-key."
|
|
TMP_KEY=$(mktemp)
|
|
openssl genrsa -out "$TMP_KEY" 2048 2>/dev/null
|
|
AUTHELIA_OIDC_PRIVATE_KEY_CONTENT=$(cat "$TMP_KEY")
|
|
shred -u "$TMP_KEY"
|
|
elif [[ -f "$AUTHELIA_OIDC_PRIVATE_KEY_PATH" ]]; then
|
|
AUTHELIA_OIDC_PRIVATE_KEY_CONTENT=$(cat "$AUTHELIA_OIDC_PRIVATE_KEY_PATH")
|
|
else
|
|
echo "ERROR: OIDC private key file not found: $AUTHELIA_OIDC_PRIVATE_KEY_PATH" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Hash the Authelia-KeyCape client secret with bcrypt (cost 12).
|
|
# Authelia requires the bcrypt hash for OIDC client secrets.
|
|
echo "Hashing KeyCape client secret with bcrypt (this may take a moment)..."
|
|
if command -v python3 &>/dev/null && python3 -c "import bcrypt" 2>/dev/null; then
|
|
KEYCAPE_CLIENT_SECRET_HASH=$(python3 -c "
|
|
import bcrypt, sys
|
|
pw = sys.argv[1].encode()
|
|
print(bcrypt.hashpw(pw, bcrypt.gensalt(rounds=12)).decode())
|
|
" "$AUTHELIA_KEYCAPE_CLIENT_SECRET")
|
|
elif command -v htpasswd &>/dev/null; then
|
|
KEYCAPE_CLIENT_SECRET_HASH=$(htpasswd -nbBC 12 "" "$AUTHELIA_KEYCAPE_CLIENT_SECRET" | cut -d: -f2)
|
|
else
|
|
echo "ERROR: bcrypt hash generation requires python3+bcrypt or apache2-utils (htpasswd)" >&2
|
|
echo " Install: pip3 install bcrypt OR apt install apache2-utils" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "Creating K8s Secret: authelia-secrets (namespace: sso)"
|
|
kubectl create secret generic authelia-secrets \
|
|
--namespace=sso \
|
|
--from-literal=jwt_secret="$AUTHELIA_JWT_SECRET" \
|
|
--from-literal=session_secret="$AUTHELIA_SESSION_SECRET" \
|
|
--from-literal=storage_encryption_key="$AUTHELIA_STORAGE_ENCRYPTION_KEY" \
|
|
--from-literal=ldap_password="$LLDAP_LDAP_USER_PASS" \
|
|
--from-literal=oidc_hmac_secret="$AUTHELIA_OIDC_HMAC_SECRET" \
|
|
--from-literal=oidc_issuer_private_key="$AUTHELIA_OIDC_PRIVATE_KEY_CONTENT" \
|
|
--from-literal=keycape_client_secret_hash="$KEYCAPE_CLIENT_SECRET_HASH" \
|
|
--dry-run=client -o yaml | kubectl apply -f -
|
|
|
|
echo ""
|
|
echo "Done. Secret authelia-secrets created in namespace: sso"
|
|
echo ""
|
|
echo "Next:"
|
|
echo " Apply deployment.yaml, then ingress.yaml."
|
|
echo " The plaintext Authelia-KeyCape client secret is in secrets/authelia/secrets.env"
|
|
echo " as AUTHELIA_KEYCAPE_CLIENT_SECRET. This value goes into keycape/create-secrets.sh"
|
|
echo " as the authelia.clientSecret in the KeyCape config."
|