Cluster OIDC Login (Authentik)
The service-cluster kube-apiserver trusts Authentik as an OIDC provider, so
operators authenticate to kubectl with their Authentik account instead of the
static admin kubeconfig. Authentik group membership drives Kubernetes RBAC.
How it fits together
Section titled “How it fits together”- Issuer:
https://login.homelab.muehlena.de/application/o/kubernetes/(Let’s Encrypt cert viale-issuer-dns-homelab, so the API server trusts it with nooidc-ca-file). - Username claim:
email→ surfaces to RBAC asoidc:<email>. - Groups claim:
groups→ surfaces asoidc:<group-name>.
API-server configuration (Talos)
Section titled “API-server configuration (Talos)”Set on the control plane via cluster.apiServer.extraArgs in
tf/modules/pve-talos-k8s-cluster/talos.tf:
| Flag | Value |
|---|---|
oidc-issuer-url | https://login.homelab.muehlena.de/application/o/kubernetes/ |
oidc-client-id | kubernetes |
oidc-username-claim | email |
oidc-username-prefix | oidc: |
oidc-groups-claim | groups |
oidc-groups-prefix | oidc: |
These
--oidc-*flags are deprecated in Kubernetes 1.34 but functional. Once the cluster moves to the native TalosAuthenticationConfigmachine-config object, migrate to a structuredAuthenticationConfiguration.
Apply with a control-plane terraform apply on
tf/infrastructure/k8s.services.homelab.muehlena.de. This rewrites the
kube-apiserver static-pod manifest and rolls it — expect a brief API blip per
control-plane node.
Authentik setup (Terraform-managed)
Section titled “Authentik setup (Terraform-managed)”The provider, application, and groups are declared in
tf/application-parameters/authentik/ — no manual UI steps:
applications.tf—module "application_kubernetes"creates the OAuth2 provider + application (slugkubernetes, which is what makes the issuer path.../o/kubernetes/). It pins a fixedclient_id = "kubernetes"(theoauth2module gained an optionalclient_idoverride for exactly this — the apiserver’s--oidc-client-idmust match) and setsclient_type = "public", so there is no client secret — kubelogin and Nautik are public clients and prove the exchange with PKCE. Redirect URIs cover kubelogin’s local callback via a regex (http://(localhost|127.0.0.1):(8000|18000)(/.*)?) and the Nautik desktop client (io.nautik.Nautik:/oauth2redirect). Default scopes (openid,email,profile,offline_access) already carry thegroupsclaim through Authentik’s defaultprofilemapping.groups.tf—homelab__k8s(access gate) andhomelab__k8s__admin(→cluster-admin), mirroring thehomelab__grafana__adminconvention.- An
authentik_policy_bindinggates the application tohomelab__k8smembers.
Apply with terraform apply in tf/application-parameters/authentik/. Because
this is a public client, operators need no secret — only the client_id
(kubernetes) and the issuer URL, both public.
Add operators to homelab__k8s__admin (which implies homelab__k8s) to grant
cluster-admin.
k8s/manifests/base/cluster-services/cluster-oidc-rbac/cluster-admin-crb.yaml
binds the Authentik group to cluster-admin:
kind: ClusterRoleBindingmetadata: name: oidc-homelab-k8s-adminsubjects: - kind: Group name: oidc:homelab__k8s__admin # oidc-groups-prefix + Authentik group name apiGroup: rbac.authorization.k8s.ioroleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.ioWired through the tenant overlay + Flux Kustomization
cluster-services-cluster-oidc-rbac. To grant narrower access, add a group
(e.g. homelab__k8s__viewer) and bind oidc:homelab__k8s__viewer to a lesser
ClusterRole.
Client setup (kubelogin)
Section titled “Client setup (kubelogin)”Install the plugin (kubectl oidc-login):
brew install int128/kubelogin/kubelogin # or: krew install oidc-loginAdd a user + context to your kubeconfig (API endpoint is the VIP on
k8s.services.homelab.muehlena.de:6443). This is a public client, so there
is no secret — --oidc-use-pkce proves the exchange:
kubectl config set-credentials oidc \ --exec-api-version=client.authentication.k8s.io/v1 \ --exec-interactive-mode=Never \ --exec-command=kubectl \ --exec-arg=oidc-login \ --exec-arg=get-token \ --exec-arg=--oidc-issuer-url=https://login.homelab.muehlena.de/application/o/kubernetes/ \ --exec-arg=--oidc-client-id=kubernetes \ --exec-arg=--oidc-extra-scope=email \ --exec-arg=--oidc-extra-scope=profile \ --exec-arg=--oidc-extra-scope=offline_access
kubectl config set-context k8s-oidc \ --cluster=<your-cluster-entry> --user=oidckubectl config use-context k8s-oidcFirst kubectl call opens a browser to Authentik; on success the token is
cached under ~/.kube/cache/oidc-login/ and refreshed via offline_access.
Verify the identity the API server sees:
kubectl auth whoami# Username oidc:you@muehlena.de# Groups oidc:homelab__k8s__admin, system:authenticatedTroubleshooting
Section titled “Troubleshooting”Unauthorized: theid_tokenaudmust equalkubernetes; confirm the Authentik Client ID and--oidc-client-idmatch.- Authenticated but
Forbidden: identity works, RBAC doesn’t — checkkubectl auth whoamigroups against theClusterRoleBindingsubject (remember theoidc:prefix). oidc: could not verify token: issuer did not match: the--oidc-issuer-url(trailing slash included) must exactly equal theissuerfield inhttps://login.homelab.muehlena.de/application/o/kubernetes/.well-known/openid-configuration.- No
groups: ensure the user is inhomelab__k8s__adminand the provider includes the defaultprofilescope mapping.