#!/bin/sh set -eu chart_version=2026.8.0 chart_sha256=fb51f1ab970a15e37f8d2b4fe2767cea33afe7e6f84bee646d2690f31fcde7e5 project_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd) secret_file=${AUTHENTIK_SECRET_FILE:-$project_dir/authentik-secrets.env} for command in helm kubectl openssl base64 curl sha256sum; do if ! command -v "$command" >/dev/null 2>&1; then echo "Required command not found: $command" >&2 exit 1 fi done kubectl apply --filename "$project_dir/authentik-namespace.yaml" if [ -f "$secret_file" ]; then if grep -q 'REPLACE_WITH_' "$secret_file"; then echo "Refusing placeholder secrets in $secret_file" >&2 exit 1 fi if kubectl --namespace authentik get secret authentik-postgresql >/dev/null 2>&1; then candidate_password=$(awk -F= '$1 == "AUTHENTIK_POSTGRESQL__PASSWORD" {sub(/^[^=]*=/, ""); print; exit}' "$secret_file") candidate_password_base64=$(printf '%s' "$candidate_password" | base64 | tr -d '\n') existing_password_base64=$(kubectl --namespace authentik get secret authentik-postgresql --output='jsonpath={.data.password}') if [ -z "$candidate_password" ] || [ "$candidate_password_base64" != "$existing_password_base64" ]; then echo "Refusing an uncoordinated PostgreSQL password change; rotate the database role and both Secrets during maintenance." >&2 exit 1 fi unset candidate_password candidate_password_base64 existing_password_base64 fi kubectl --namespace authentik create secret generic authentik-runtime \ --from-env-file="$secret_file" --dry-run=client --output=yaml \ | kubectl apply --filename=- elif kubectl --namespace authentik get secret authentik-runtime >/dev/null 2>&1; then echo "Keeping existing authentik/authentik-runtime Secret." else echo "Missing $secret_file and authentik/authentik-runtime Secret." >&2 exit 1 fi oidc_secret_file=$(mktemp) oidc_patch_file=$(mktemp) postgres_password_file=$(mktemp) trap 'rm -f "$oidc_secret_file" "$oidc_patch_file" "$postgres_password_file"' EXIT HUP INT TERM chmod 600 "$oidc_secret_file" "$oidc_patch_file" "$postgres_password_file" runtime_password_base64=$(kubectl --namespace authentik get secret authentik-runtime \ --output='jsonpath={.data.AUTHENTIK_POSTGRESQL__PASSWORD}') if [ -z "$runtime_password_base64" ]; then echo "authentik-runtime is missing AUTHENTIK_POSTGRESQL__PASSWORD." >&2 exit 1 fi if kubectl --namespace authentik get secret authentik-postgresql >/dev/null 2>&1; then existing_password_base64=$(kubectl --namespace authentik get secret authentik-postgresql \ --output='jsonpath={.data.password}') if [ "$runtime_password_base64" != "$existing_password_base64" ]; then echo "The Authentik runtime and PostgreSQL Secret passwords differ; refusing to restart workloads." >&2 exit 1 fi else printf '%s' "$runtime_password_base64" | base64 --decode > "$postgres_password_file" kubectl --namespace authentik create secret generic authentik-postgresql \ --from-file="password=$postgres_password_file" \ --from-file="postgres-password=$postgres_password_file" fi unset runtime_password_base64 existing_password_base64 oidc_secret_base64=$(kubectl --namespace authentik get secret authentik-runtime \ --output='jsonpath={.data.ARGOCD_OIDC_CLIENT_SECRET}') if [ -n "$oidc_secret_base64" ]; then printf '%s' "$oidc_secret_base64" | base64 --decode > "$oidc_secret_file" else oidc_secret=$(openssl rand -hex 32) printf '%s' "$oidc_secret" > "$oidc_secret_file" oidc_secret_base64=$(printf '%s' "$oidc_secret" | base64 | tr -d '\n') printf '{"data":{"ARGOCD_OIDC_CLIENT_SECRET":"%s"}}\n' \ "$oidc_secret_base64" > "$oidc_patch_file" kubectl --namespace authentik patch secret authentik-runtime \ --type=merge --patch-file="$oidc_patch_file" >/dev/null unset oidc_secret fi kubectl create namespace argocd --dry-run=client --output=yaml \ | kubectl apply --filename=- kubectl --namespace argocd create secret generic argocd-oidc \ --from-file="client-secret=$oidc_secret_file" \ --dry-run=client --output=yaml \ | kubectl apply --filename=- kubectl --namespace argocd label secret argocd-oidc \ app.kubernetes.io/part-of=argocd --overwrite >/dev/null # The Authentik-aware error context and public authentik middleware chain are # managed by ../traefik-error-pages so their diagnostics stay in one project. kubectl apply --filename "$project_dir/ceph-object-user.yaml" chart_archive=$(mktemp) trap 'rm -f "$oidc_secret_file" "$oidc_patch_file" "$postgres_password_file" "$chart_archive"' EXIT HUP INT TERM curl --fail --silent --show-error --location --output "$chart_archive" \ "https://github.com/goauthentik/helm/releases/download/authentik-${chart_version}/authentik-${chart_version}.tgz" printf '%s %s\n' "$chart_sha256" "$chart_archive" | sha256sum --check >&2 helm upgrade --install --namespace authentik authentik "$chart_archive" \ --version "$chart_version" --values "$project_dir/values.yaml" \ --take-ownership --force-conflicts --rollback-on-failure \ --wait --timeout 15m kubectl apply --filename "$project_dir/authentik-outpost-path.yaml" kubectl apply --filename "$project_dir/authentik-media-redirect.yaml"