EMK - OIDC Authentication
Estimated time to read: 10 minutes
This page describes how to configure your Kubernetes cluster with OIDC authentication. This is only available through the YAML configurator and requires EMK access through a service account (SA).
OIDC authentication allows you to integrate your Kubernetes cluster with external identity providers such as Azure AD, Google, Keycloak, Zitadel and Dex. The kube-apiserver validates JWT tokens issued by your identity provider to authenticate users. More information about Kubernetes OIDC authentication can be found on the Kubernetes website, topic Authenticating.
Kubernetes Version Support
- Kubernetes >= 1.30: Use Structured Authentication configuration
Structured Authentication
For clusters with Kubernetes version >= 1.30, kube-apiserver can be provided with Structured Authentication configuration. This method offers more flexibility and supports multiple JWT authenticators.
Example Structured Authentication Configuration
apiVersion: v1
kind: ConfigMap
metadata:
name: oidc-authentication-config
data:
config.yaml: |
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://identity.example.com
audiences:
- 7192d6e6-2b9f-454e-96c3-6863aa0f4050
- 234ff210-2b29-4324-88e2-2fe5974a4c8e
audienceMatchPolicy: MatchAny
claimMappings:
username:
claims: email
prefix: 'oidc:'
groups:
claims: groups
prefix: 'oidc:'
apiVersion: v1
kind: ConfigMap
metadata:
name: multi-oidc-authentication-config
data:
config.yaml: |
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://internal.example.com
audiences:
- 7192d6e6-2b9f-454e-96c3-6863aa0f4050
audienceMatchPolicy: MatchAny
claimMappings:
username:
claims: email
prefix: 'internal:'
groups:
claims: groups
prefix: 'internal:'
- issuer:
url: https://external.example.com
audiences:
- 234ff210-2b29-4324-88e2-2fe5974a4c8e
audienceMatchPolicy: MatchAny
claimMappings:
username:
claims: email
prefix: 'external:'
groups:
claims: groups
prefix: 'external:'
apiVersion: v1
kind: ConfigMap
metadata:
name: advanced-oidc-authentication-config
data:
config.yaml: |
apiVersion: apiserver.config.k8s.io/v1beta1
kind: AuthenticationConfiguration
jwt:
- issuer:
url: https://identity.example.com
audiences:
- 7192d6e6-2b9f-454e-96c3-6863aa0f4050
- 234ff210-2b29-4324-88e2-2fe5974a4c8e
audienceMatchPolicy: MatchAny
claimMappings:
username:
claims: email
prefix: 'oidc:'
groups:
claims: groups
prefix: 'oidc:'
extra:
- key: 'department'
expression: 'claims.department'
- key: 'employee_id'
expression: 'claims.employee_id'
claimValidationRules:
- expression: 'claims.email_verified == true'
message: "email must be verified"
- expression: 'has(claims.groups) && size(claims.groups) > 0'
message: "user must belong to at least one group"
- expression: 'claims.exp - claims.iat <= 3600'
message: "token validity cannot exceed 1 hour"
userValidationRules:
- expression: "!user.username.startsWith('system:')"
message: "username cannot use system: prefix"
- expression: "size(user.username) >= 3"
message: "username must be at least 3 characters"
Structured Authentication Parameters
Issuer Configuration
Parameter |
Required | Description |
|---|---|---|
url |
Yes | URL of the OpenID Connect provider. Must use HTTPS scheme. |
audiences |
Yes | List of acceptable audiences for the JWT token. |
audienceMatchPolicy |
No | How to match audiences. Options: MatchAny (default), MatchAll. |
certificateAuthority |
No | PEM-encoded certificate bundle for verifying the provider's certificate. |
discoveryURL |
No | Override URL for OIDC discovery (default: issuer URL + /.well-known/openid-configuration). |
Claim Mappings
Parameter |
Required | Description |
|---|---|---|
username.expression |
No | CEL expression to extract username from claims. Default: claims.sub. |
username.prefix |
No | Prefix for username. |
groups.expression |
No | CEL expression to extract groups from claims. |
groups.prefix |
No | Prefix for groups. |
uid.expression |
No | CEL expression to extract UID from claims. |
extra |
No | Additional attributes to extract from claims. |
Rolling Out Configuration Changes
EMK will not automatically roll out changes to the authentication ConfigMap to minimize the number of Shoot reconciliations. EMK will pick up changes on the next reconciliation of Shoots referencing the configuration.
If you want to immediately roll out configuration changes, you can manually trigger a Shoot reconciliation as described in triggering an immediate reconciliation.
Configuration Validation
The user is responsible for the validity of the configured JWT authenticators. Invalid configurations may prevent users from accessing the cluster. Always test configurations in a non-production environment first.
User Permissions with RBAC
After configuring OIDC authentication, users can authenticate to your cluster, but they won't have any permissions yet. You need to configure Role-Based Access Control (RBAC) to grant permissions to users and groups extracted from the OIDC token.
Authentication vs Authorization
- Authentication (OIDC configuration): Verifies who the user is
- Authorization (RBAC): Determines what the user can do
Understanding the Flow
- User authenticates with their identity provider (Azure AD, Google, etc.)
- Identity provider issues a JWT token containing user information and groups
- Kubernetes extracts username and groups from the token based on your OIDC configuration
- RBAC checks if the username or any of the user's groups have permissions via RoleBindings/ClusterRoleBindings
Example RoleBindings
The most common approach is to grant permissions to groups rather than individual users. This makes permission management easier and more scalable.
See the following examples:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: oidc-cluster-admins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: Group
name: 'oidc:operations' # match claimMappings.groups.prefix + group name
apiGroup: rbac.authorization.k8s.io
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-developers
namespace: development
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: edit
subjects:
- kind: Group
name: oidc:developers # match claimMappings.groups.prefix + group name
apiGroup: rbac.authorization.k8s.io
You can also grant permissions directly to individual users:
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: oidc-user-viewer
namespace: production
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
subjects:
- kind: User
name: oidc:john@example.com # match claimMappings.username.prefix + email
apiGroup: rbac.authorization.k8s.io
Common Built-in ClusterRoles
Kubernetes provides several built-in ClusterRoles that you can use:
| ClusterRole | Permissions |
|---|---|
cluster-admin |
Full access to all resources in the cluster |
admin |
Full access within a namespace, can manage roles and rolebindings |
edit |
Read/write access to most resources in a namespace |
view |
Read-only access to most resources in a namespace |
Matching Group Names
Make sure the group names in your RoleBindings match what comes from your OIDC provider.
See the following example:
If your Zitadel project is called mycorp and the role is called operations, your AuthenticationConfiguration can have:
And your ClusterRoleBinding subjects should contain:
Group names and other claims from your OIDC provider can be viewd in the OIDC token after a succesful authentication.
Complete Setup Example
In the following example an EMK cluster is configured to use a Zitadel instance for OIDC authentication. Allowing user John, a member of the Operations team at MyCorp, to access and manage the EMK cluster with cluster admin privileges.
Prerequisites:
- EMK Sevice Account, see service account access
- Time limited kubeconfig, see time limited access
- kubectl with kubelogin
- Zitadel OIDC application with PCKE support
Step summary:
- Create AuthenticationConfiguration
- Update Shoot configuration to use AuthenticationConfiguration
- Create RoleBindings to set permissions
- Create kubeconfig with OIDC authentication
- Access cluster using OIDC
Create AuthenticationConfiguration
The following kubectl commands must be executed using your EMK Service Account.
-
Create file
oidc-authentication-config.yamlcontaining a ConfigMap with the AuthenticationConfiguration:apiVersion: v1 kind: ConfigMap metadata: name: oidc-authentication-config namespace: garden-myproject data: config.yaml: | apiVersion: apiserver.config.k8s.io/v1beta1 kind: AuthenticationConfiguration jwt: - issuer: url: https://identity.example.com audiences: - "7192d6e6-2b9f-454e-96c3-6863aa0f4050" audienceMatchPolicy: MatchAny claimMappings: username: claim: "preferred_username" prefix: "oidc:" groups: claim: "roles:mycorp" prefix: "oidc:"Configuration Validation
The user is responsible for the validity of the configured JWT authenticators. Invalid configurations may prevent users from accessing the cluster. Always test configurations in a non-production environment first.
-
Add the ConfigMap to your EMK project namespace:
-
Verify the ConfigMap was created:
Expected output:
-
Update your preferred cluster, mycluster in our example, to use the structured authentication:
kubectl patch shoot mycluster \ --type 'merge' \ --patch '{ "spec": { "kubernetes": { "kubeAPIServer": { "structuredAuthentication": { "configMapName": "oidc-authentication-config" } } } } }'Expected additional configuration to your shoot config:
spec: kubernetes: kubeAPIServer: structuredAuthentication: configMapName: oidc-authentication-configNote
EMK validates the Shoot resource to refer only to existing ConfigMaps containing a valid AuthenticationConfiguration, and rejects the change on failure.
-
Reconcile your cluster for changes to take affect:
Note
EMK will not automatically roll out changes to the authentication ConfigMap to minimize the number of Shoot reconciliations. EMK will pick up changes on the next reconciliation of Shoots referencing the configuration.
Create RoleBindings
The following kubectl commands must be applied in your cluster.
-
Create file
clusterrolebindings.yamlcontaining permissions:apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: oidc-cluster-admins roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: oidc:operations # match claimMappings.groups.prefix + group name EOF -
Apply the create file to create the ClusterRoleBinding in your cluster:
Verifying RBAC Configuration
After creating RoleBindings, verify that users have the expected permissions:
# Check what a specific user can do
kubectl auth can-i --list --as oidc:john@example.com
# Check if a user can perform a specific action
kubectl auth can-i get nodes --as oidc:john@example.com
# Check permissions for a group in a namespace
kubectl auth can-i get pods --as-group oidc:operations -n development
OIDC Authentication with kubectl
To setup OIDC authentication with kubectl, we'll modify a current kubeconfig as it already contains the server address and required certificate information.
-
Download a time limited kubeconfig and save it to
~/.kube/config-oidc.yaml, see time limited access -
List and remove the current user from the kubeconfig:
-
Add the OIDC credential, change the issuer URL and client ID accordingly:
kubectl config set-credentials oidc \ --kubeconfig ~/.kube/config-oidc.yaml \ --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://identity.example.com" \ --exec-arg="--oidc-client-id=7192d6e6-2b9f-454e-96c3-6863aa0f4050" \ --exec-arg="--oidc-extra-scope=groups" \ --exec-arg="--oidc-extra-scope=email" \ --exec-arg="--oidc-extra-scope=name" \ --exec-arg="--oidc-extra-scope=sub" \ --exec-arg="--oidc-extra-scope=email_verified" -
Update the current context with the new credential:
-
Now list a common resource, e.g. nodes:
Note
kubectl will open your default web browser to initiate the OIDC login process. On a succesful authentication, kubectl will list the nodes
Using kubectl OIDC Plugins
For a better user experience, use kubectl plugins for automatic token refresh and interactive login flows.
See How to setup kubelogin for setup instructions.
Troubleshooting
OIDC Authentication
Clean the token cache:
View the ID token claims, crucial when you experince claim mismatches, change the issuer URL and client ID accordingly:
kubectl oidc-login setup \
--oidc-issuer-url=https://identity.example.com \
--oidc-client-id=7192d6e6-2b9f-454e-96c3-6863aa0f4050
Tokens are cached at ~/.kube/cache/oidc-login/ and can be inspected after decoding:
jq -r '.id_token | split(".") | .[0],.[1] | @base64d | fromjson' \
~/.kube/cache/oidc-login/942d484c0518e5fee6cb9ca192421790a9f8c59215e89cd87a37a8d44fc0b956
RBAC
User authenticated but has no permissions:
- Verify the group names in your RoleBindings match exactly with what's in the JWT token (including prefix)
- Check if the user is actually a member of the groups in your identity provider
- Decode the JWT token at jwt.io to see what claims are present
- Use
kubectl auth can-i --list --as <username>to see what permissions the user has
Groups not appearing correctly:
- Ensure
groupsClaimpoints to the correct claim in your JWT token - Verify the
groupsPrefixis included in your RoleBinding subject names - Check that your identity provider is configured to include group information in tokens
Common Issues
Authentication fails with "Unauthorized"
- Verify the issuer
urlis correct and accessible from the cluster - Check that the
audienceslist matches your identity provider configuration - Ensure the JWT token contains the required claims specified in your
claimMappings - Validate your
claimValidationRulesare not blocking valid tokens
Username or groups not mapped correctly
- Verify the CEL expressions in
claimMappingsare correct - Check the claims exist in your JWT token (decode it at jwt.io)
- Test your CEL expressions using the CEL Playground
- Ensure the
prefixvalues match what you use in your RoleBindings
Certificate verification errors
- Provide the CA certificate in
certificateAuthorityif your issuer uses a custom CA - Ensure the certificate is in PEM format
- Verify the certificate chain is complete
Claim validation failures
- Review the
claimValidationRulesexpressions for syntax errors - Test CEL expressions using the CEL Playground
- Check that the
messagefield provides clear guidance for users - Verify the claims referenced in validation rules exist in your tokens
Security Best Practices
Security Recommendations
- Always use HTTPS for
issuer.url - Implement short token expiration times (< 1 hour recommended)
- Use claim validation rules to restrict access
- Regularly rotate client credentials
- Monitor authentication logs for suspicious activity
- Use RBAC to limit permissions after authentication
- For Structured Authentication, validate user input with
userValidationRules