This repository has been archived on 2026-10-18. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
authentik-deployment/install.sh
T

134 lines
6.3 KiB
Bash
Executable File

#!/bin/sh
set -eu
chart_version=2026.8.4
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; do
if ! command -v "$command" >/dev/null 2>&1; then
echo "Required command not found: $command" >&2
exit 1
fi
done
if [ "$(kubectl config current-context)" != 'kubernetes-admin@kubernetes' ]; then
echo 'Expected the homelab Kubernetes context; refusing to install Authentik.' >&2
exit 1
fi
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"
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
sh "$project_dir/update-outpost-images.sh"
# Apply the Dashboard blueprint immediately, then make its provider available
# through the embedded outpost. The relationship is idempotent, but is not
# expressed in the blueprint because replacing the outpost's provider list
# would detach providers owned by other repositories.
dashboard_blueprint_attempt=1
while ! kubectl --namespace authentik exec deployment/authentik-worker -- \
ak apply_blueprint /blueprints/mounted/cm-authentik-blueprints/kubernetes-dashboard.yaml; do
if [ "$dashboard_blueprint_attempt" -ge 5 ]; then
echo "Dashboard blueprint failed after $dashboard_blueprint_attempt attempts." >&2
exit 1
fi
dashboard_blueprint_attempt=$((dashboard_blueprint_attempt + 1))
echo "Dashboard blueprint conflicted; retrying attempt $dashboard_blueprint_attempt/5." >&2
sleep 5
done
kubectl --namespace authentik exec deployment/authentik-worker -- \
ak shell --command \
'from authentik.outposts.models import Outpost; from authentik.providers.proxy.models import ProxyProvider; outpost = Outpost.objects.get(name="authentik Embedded Outpost"); provider = ProxyProvider.objects.get(name="Kubernetes Dashboard"); outpost.providers.add(provider)'
kubectl apply --filename "$project_dir/authentik-outpost-path.yaml"
kubectl apply --filename "$project_dir/authentik-media-redirect.yaml"