Set Up OAuth Authentication#
This guide walks through securing cluster services with oauth2-proxy and GitHub as the OIDC provider.
Architecture#
Browser → ingress-nginx → oauth2-proxy (auth check) → backend service
↕
GitHub OAuth (OIDC login)
oauth2-proxy acts as an authentication middleware. When a user visits a protected service, nginx checks with oauth2-proxy before forwarding the request. If the user is not authenticated, they are redirected to GitHub to log in.
Why oauth2-proxy? Authentik and Keycloak each need ~2 GB of RAM — too heavy for a small ARM cluster. oauth2-proxy uses ~128 Mi and integrates directly with the existing ingress-nginx annotations.
Prerequisites#
A working cluster with ingress-nginx and cert-manager
A GitHub account (the OAuth provider)
kubesealinstalled locally (see Manage Sealed Secrets)The
oauth2-proxynamespace must exist (ArgoCD creates it automatically viaCreateNamespace=true)
Step 1: Create a GitHub OAuth App#
Click New OAuth App.
Fill in the form:
Field |
Value |
|---|---|
Application name |
|
Homepage URL |
|
Authorization callback URL |
|
Click Register application.
Copy the Client ID.
Click Generate a new client secret and copy it immediately.
Step 3: Create and seal the credentials#
printf 'GitHub Client ID: ' && read -r CLIENT_ID
printf 'GitHub Client Secret: ' && read -rs CLIENT_SECRET && echo
printf 'Cookie Secret: ' && read -rs COOKIE_SECRET && echo
kubectl create secret generic oauth2-proxy-credentials \
--namespace oauth2-proxy \
--from-literal=client-id="$CLIENT_ID" \
--from-literal=client-secret="$CLIENT_SECRET" \
--from-literal=cookie-secret="$COOKIE_SECRET" \
--dry-run=client -o yaml | \
kubeseal --controller-name sealed-secrets --controller-namespace kube-system -o yaml > \
kubernetes-services/additions/oauth2-proxy/oauth2-proxy-secret.yaml
unset CLIENT_ID CLIENT_SECRET COOKIE_SECRET
Commit and push:
git add kubernetes-services/additions/oauth2-proxy/oauth2-proxy-secret.yaml
git commit -m "Add oauth2-proxy credentials SealedSecret"
git push
Step 4: Add a DNS record#
Add a grey-cloud (DNS-only) A record in the Cloudflare dashboard for the oauth2-proxy ingress:
Type |
Name |
Content |
Proxy status |
|---|---|---|---|
A |
|
|
DNS only |
Use one of your worker node IPs. This is the same pattern as other LAN-only services (see Set Up DNS, TLS & Cloudflare Tunnel Part 3).
Step 5: Deploy oauth2-proxy#
oauth2-proxy is deployed as an ArgoCD Application defined in
kubernetes-services/templates/oauth2-proxy.yaml. After pushing the
SealedSecret, ArgoCD syncs automatically.
Verify the deployment:
kubectl rollout status deployment/oauth2-proxy -n oauth2-proxy
kubectl get ingress -n oauth2-proxy
Visit https://oauth2.<your-domain> — you should see a GitHub login page.
Step 6: Enable the OAuth toggle#
Now that oauth2-proxy is running, enable it for all protected services by editing
kubernetes-services/values.yaml:
enable_oauth2_proxy: true
Commit and push:
git add kubernetes-services/values.yaml
git commit -m "Enable OAuth2 proxy for cluster services"
git push
ArgoCD will pick up the change and add OAuth annotations to all protected ingresses.
Step 7: How OAuth is wired to services#
Services use the shared ingress template at
kubernetes-services/additions/ingress/templates/ingress.yaml. To protect
a service with OAuth, set oauth2_proxy: true in its ingress values:
# In the ArgoCD Application template (e.g. dashboard.yaml)
helm:
valuesObject:
name: headlamp
cluster_domain: example.com
service_name: headlamp
service_port: 80
oauth2_proxy: true # ← enables OAuth
This adds nginx auth annotations that redirect unauthenticated requests to oauth2-proxy.
Services currently protected by OAuth:
Grafana (
grafana.yaml) — native login after OAuth gatewayLonghorn (
longhorn.yaml) — no native auth, OAuth is the only layerHeadlamp (
dashboard.yaml) — requires a service account token after OAuthOpen WebUI (
open-webui.yaml) — native login after OAuth gatewayArgoCD (
argo-cd/ingress.yaml) — uses TLS passthrough with its own login (managed by Ansible, not the shared ingress template)
Step 8: Restrict access (optional)#
To restrict access to members of a specific GitHub organisation, add the
github-org flag in kubernetes-services/templates/oauth2-proxy.yaml:
extraArgs:
github-org: "your-org-name"
To restrict to specific email addresses, replace email-domain: "*" with
a comma-separated list of allowed emails.
Integrating with Cloudflare Access#
For services exposed via the Cloudflare tunnel (e.g. Open WebUI), you can add a second authentication layer using Cloudflare Access at zero cluster overhead. See Set Up a Cloudflare SSH Tunnel for Remote Cluster Access for how Access Applications work, and apply the same pattern to any tunnelled service.
Troubleshooting#
Redirect loop after enabling OAuth#
The oauth2-proxy ingress must not itself be protected by OAuth. Check
that oauth2-proxy.yaml does not set oauth2_proxy: true on its own
ingress values.
403 after GitHub login#
Check the email-domain or github-org restrictions in the oauth2-proxy
configuration. Verify that the authenticated email matches the allowed
list.
OAuth not enforced (service loads without login)#
Verify the ingress annotations are present:
kubectl get ingress -n <namespace> <service>-ingress -o yaml | grep auth
You should see auth-url and auth-signin annotations pointing to
oauth2.<your-domain>.