Runtime.Diaz

K8s Access Control: Authentication with OIDC

Why it matters

Kubernetes does not have objects in the API sever which represent normal user accounts or groups. There are no objects called “User” or “Group.” It relies on external authentication mechanisms to identify users and grant them permissions. This means you'll need to integrate your cluster with an identity provider (IdP) to authenticate users and assign them roles. The specific method of authentication and authorization will depend on your chosen IdP and configuration.

OIDC authentication enhances Kubernetes security and streamlines external user and group management, making it a ideal choice to control access to your clusters.

The big picture

The K8s documentation Controlling Access to the Kubernetes API, does a great job introducing the stages a request traverses within the API server.

The interest here is the Authentication modules (or I've seen it referred as plugins as well) and how to enable a method for user authentication. Particularly the OpenID Connect (OIDC) Token authentication strategy.

OpenID Connect (OIDC - built on the OAuth 2.0 specification) allows seamless integration with identity providers for Kubernetes authentication. This means you can use your existing identity management system to control access to your Kubernetes resources.

The OIDC ID Token is a JSON Web Token that encapsulates the user identity, including a uique identifier, groups, and expiration information about the user that the API server can use to authorize their access. The JWT is signed by an identity provider’s certificate and can be verified by Kubernetes, simply by checking the JWT’s signature. This is the token passed to Kubernetes for each request to authenticate the external user.

Key components

To implement OIDC authentication in Kubernetes, you need to focus on three main areas:

  1. kube-apiserver oidc flags
  2. kubeconfig setup
  3. identity provider integration

How it works

Prerequisites

Kube-apiserver configuration

Add the OIDC flags to the API server and provide the OIDC certificate so that the API will trust the OIDC provider.

--oidc-issuer-url="https://amapiano-idp.us.auth0.com/"
--oidc-client-id="jLe7RD4MqaW6fFgRwOXa8B0n3OVFt4Z7"
--oidc-username-claim="email"
--oidc-groups-claim="groups"
--oidc-ca-file="/auth0/auth0-ca.crt"

One key technical detail is that the Kubernetes API server's built-in OpenID Connect authenticator supports the OIDC discovery mechanism, which helps the server find the configuration it needs to authenticate users.

For this exercise:

For the oidc-ca-file, I obtained the certificate for the CA that signed Auth0 identity provider's web certificate by using the following tooling:

openssl s_client -showcerts -connect amapiano-idp.us.auth0.com:443 -servername amapiano-idp.us.auth0.com </dev/null | openssl x509 -out /tmp/auth0-ca.crt

Troubleshooting

Api server:

# Kubectl messaged received
$ kubectl --user=demo auth whoami
error: You must be logged in to the server (Unauthorized)

# Kube-apiserver error log
E1205 00:30:40.127319       1 authentication.go:73] "Unable to authenticate the request" err="[invalid bearer token, oidc: email not verified]

Kubeconfig configuration

I purposely focused on obtaining tokens using the Resource Owner Password Credentials grant type. While this grant type is not widely recommended due to its security implications, it's a straightforward way to acquire tokens without browser based flows, making it ideal for learning and demonstration purposes. I've packaged a bash script (k8s_user_auth_init.sh) within the Iximiuz playground home directory to streamline this process.

The script will mint an ID token, configure a kubeconfig file with the token for user authentication, and then grant the user the cluster-admin role for simplicity.

One key detail when obtaining the id_token is the scope to include in the OAuth 2.0 request. For example, in the bash script (available in the playground), it includes the openid and email scopes.

curl -s --request POST \                                            
  --url 'https://amapiano-idp.us.auth0.com/oauth/token' \                      
  --header 'content-type: application/x-www-form-urlencoded' \                 
  --data grant_type=password \                                                 
  --data "username=${username}" \                                              
  --data "password=${password}" \                                              
  --data scope='openid email' \                                                
  --data "client_id=${client_id}" \                                            
  --data "client_secret=${client_secret}")

This Start Playground will fire up the custom Kind Cluster: OIDC AuthN playground. Playground initialization should take roughly one minute. Once fully initialized, execute the scripts below from the home directory. Once executed, the script's output will provide further instruction.

laborant@docker-01:~$ ./k8s_user_auth_init.sh

A note on Iximiuz Labs playgrounds:

Feel free to explore the Kind configuration, makefile and script for more details on the playground setup.

OIDC identity provider integration

I ended up leveraging Auth0.com's free development IdP instance which provided enough of the necessary OAuth 2.0/OIDC and custom user management configuration.

Here is the well-known public endpoint for reference:

#authentication #kubernetes #oauth2 #oidc