#!/usr/bin/env bash # create-secrets.sh — create the authelia-secrets K8s Secret # # Usage: # ./create-secrets.sh [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."