Configuring OIDC Authentication for Browserless Clients
5 minute read
Follow these steps to configure OpenID Connect (OIDC) authentication for a shoot cluster. This guide covers creating the necessary API Server authentication configuration, installing the OIDC login plugin, and granting cluster access to OIDC users / clients.
Prerequisites
Before proceeding, ensure you have the following access and credentials:
- Project Kubeconfig: Download this from the PSKE dashboard under Members > Service Accounts.
- Shoot Kubeconfig: Download this from the shoot overview page in the PSKE dashboard.
- OIDC Credentials: Retrieve the issuer URL, client ID, and client secret from your OIDC identity provider.
1. Create the Authentication ConfigMap
Create a file named kube-apiserver-auth-config.yaml and paste the following content. Ensure you replace the placeholder issuer URL and client ID values. For more information, refer to the OIDC configuration documentation: OIDC / Structured Authentication.
# kube-apiserver-auth-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: kube-apiserver-auth-config
data:
config.yaml: |-
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://example.com
audiences:
- example-client-id
claimMappings:
username:
claim: sub
prefix: https://example.com#
Deploy the ConfigMap to your project using the project kubeconfig:
export KUBECONFIG=kubeconfig-project.yaml
kubectl apply --filename=kube-apiserver-auth-config.yaml
Note: If you update this ConfigMap later, remember to manually reconcile the Shoot (see Step 2 note).
2. Attach the ConfigMap to the Shoot
Edit the Shoot resource either in the PSKE dashboard (switch the cluster overview from Overview to YAML) or using kubectl edit (using the project kubeconfig). Add the following configuration under the spec.kubernetes.kubeAPIServer.structuredAuthentication section:
apiVersion: core.gardener.cloud/v1beta1
kind: Shoot
spec:
kubernetes:
kubeAPIServer:
structuredAuthentication:
configMapName: kube-apiserver-auth-config
Note: Gardener automatically rolls out the AuthenticationConfiguration when the structuredAuthentication in the Shoot spec changes. If you update the content of the ConfigMap without changing its name, the Shoot will not auto-reconcile. To apply the new configuration, manually trigger a reconciliation.
3. Install the OIDC Login Plugin
Install the kubectl oidc-login plugin. Follow the official installation guide here:
https://github.com/int128/kubelogin#setup
4. Obtain an OIDC Token with Client Credentials Grant
Verify that the OIDC issuer URL and credentials are correct by running the following command:
kubectl oidc-login setup --oidc-issuer-url=https://example.com --oidc-client-id=example-client-id --oidc-client-secret=example-client-secret --grant-type=client-credentials
If the command executes successfully, it prints the token claims. The output will resemble the following:
Authentication in progress...
## Authenticated with the OpenID Connect Provider
You got the token with the following claims:
```
{
"iss": "https://example.com",
"sub": "example-sub",
"aud": "example-client-id",
"exp": 1767312000,
"iat": 1767225600,
"jti": "3e9a0664-fa50-46de-85a2-d2b0c060b19c",
"at_hash": "uav9UWBowQlxgbIzgsvCpE",
}
```
Take note of the value of the sub claim. You will need this value in the next step.
5. Grant Access to the Shoot
Use the sub claim value obtained in the previous step to create a ClusterRoleBinding that maps the OIDC subject to the view role in the shoot cluster:
# oidc-user.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-cluster-viewer
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: https://example.com#example-sub
Apply the binding using your shoot kubeconfig:
export KUBECONFIG=kubeconfig-shoot.yaml
kubectl apply --filename=oidc-user.yaml
6. Add the OIDC User to the Shoot Kubeconfig
Generate an OIDC user entry in your kubeconfig using the following command. This configures kubectl to retrieve tokens automatically for this user:
export KUBECONFIG=kubeconfig-shoot.yaml
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://example.com" \
--exec-arg="--oidc-client-id=example-client-id" \
--exec-arg="--oidc-client-secret=example-client-secret" \
--exec-arg="--grant-type=client-credentials"
7. Verify Access
With the shoot kubeconfig updated, you can now execute kubectl commands against the shoot using the new OIDC credentials. For example:
kubectl --kubeconfig=kubeconfig-shoot.yaml --user=oidc get nodes
Setup Dex as OIDC Identity Provider for Browserless Clients
This guide is designed for development and testing environments:
- nip.io: The dynamic DNS service nip.io is used for auto-generated domains. Do not use it for production.
- Secrets: Never hardcode client secrets or passwords in configuration files.
Prerequisites
- Tools: helm, kubectl, kubelogin, openssl and a shell (e.g. bash)
- Shoot Kubeconfig: Download this from the shoot overview page in the PSKE dashboard.
1. Install Traefik
helm repo add traefik https://traefik.github.io/charts
helm repo update traefik
helm upgrade traefik traefik/traefik --create-namespace --install --namespace=traefik
2. Install Cert Manager
helm repo add jetstack https://charts.jetstack.io
helm repo update jetstack
helm upgrade cert-manager jetstack/cert-manager --create-namespace --install --namespace=cert-manager \
--set=crds.enabled=true
Create a cluster-issuers.yaml file with the following content:
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-http01
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-http01
solvers:
- http01:
ingress:
ingressClassName: traefik
---
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-http01-staging
spec:
acme:
server: https://acme-staging-v02.api.letsencrypt.org/directory
privateKeySecretRef:
name: letsencrypt-http01-staging
solvers:
- http01:
ingress:
ingressClassName: traefik
Apply it:
kubectl --namespace=cert-manager apply --filename=cluster-issuers.yaml
3. Install Dex
Create a dex-values.yaml file:
config:
enablePasswordDB: true
issuer: https://localhost # placeholder, overridden by helm --set
oauth2:
passwordConnector: local
staticClients:
- id: example-client-id
secret: example-client-secret
name: example-client-name
staticPasswords:
- username: admin
email: admin@example.com
# password: admin (echo admin | htpasswd -Bin admin | cut -d: -f2)
hash: $2y$05$T0abwx/OUS0EZNkDZgznd.rTsUJZrH1QSlRDHirCGQEMYko7LJbgK
storage:
type: memory
env:
DEX_CLIENT_CREDENTIAL_GRANT_ENABLED_BY_DEFAULT: true
image:
# even the latest release v2.45.1 does not support the grant type "client credentials"
tag: latest@sha256:bb4835dd1e71986cae4f6b8565e7d79c48f77460098b9e40ad5c117b4ad11465
ingress:
enabled: true
annotations:
cert-manager.io/cluster-issuer: letsencrypt-http01-staging # use staging initially
traefik.ingress.kubernetes.io/router.entrypoints: websecure
hosts:
- host: localhost # placeholder, overridden by helm --set
paths:
- path: /
pathType: Prefix
tls:
- hosts:
- localhost # placeholder, overridden by helm --set
secretName: dex-tls
Retrieve the load balancer’s external IP address and generate a dynamic nip.io domain:
IP=$(kubectl --namespace=traefik get service/traefik --output=jsonpath='{.status.loadBalancer.ingress[0].ip}')
echo "IP=${IP:?}"
# replace dots with dashes (e.g. 1.2.3.4 -> 1-2-3-4) for the subdomain part
HOST=${IP//./-}.nip.io
echo "HOST=${HOST:?}"
Install Dex:
helm repo add dex https://charts.dexidp.io
helm repo update dex
helm upgrade dex dex/dex --create-namespace --install --namespace=dex \
--values=dex-values.yaml \
--set="config.issuer=https://${HOST:?}" \
--set="ingress.hosts[0].host=${HOST:?}" \
--set="ingress.tls[0].hosts[0]=${HOST:?}"
4. Verify Certificate Issuance
Dex will initially use the staging certificate to avoid rate limits. Verify it is issued by the staging provider:
openssl s_client -showcerts -connect "${HOST:?}:443" </dev/null 2>/dev/null |
openssl x509 -noout -issuer -nameopt lname | tee -a /dev/stderr |
grep -F "organizationName=(STAGING) Let's Encrypt,"
Once verified, update Dex’s ingress to use the production Let’s Encrypt issuer:
helm upgrade dex dex/dex --create-namespace --install --namespace=dex --reuse-values \
--set="ingress.annotations.\"cert-manager.io/cluster-issuer\"=letsencrypt-http01"
Verify the certificate:
openssl s_client -showcerts -connect "${HOST:?}:443" </dev/null 2>/dev/null |
openssl x509 -noout -issuer -nameopt lname | tee -a /dev/stderr |
grep -F "organizationName=Let's Encrypt,"
5. Test OIDC Authentication
kubectl oidc-login setup \
--oidc-issuer-url="https://${HOST:?}" \
--oidc-client-id=example-client-id \
--oidc-client-secret=example-client-secret \
--grant-type=client-credentials
Alternatively, the tutorial PSKE – How to Setup OIDC/2FA on PSKE is also available.