OpenID Connect Webhook Authenticator for Kubernetes
Table of content
Overview
The OpenID Connect Webhook Authenticator allows Kubernetes cluster administrators to dynamically register new OpenID Connect providers in their clusters to use for kube-apiserver authentication.
Note: This repository still in alpha
stage and in active development. It should not be used in production. The API can change without any backwards compatibility.
Background
In Kubernetes, only a single OpenID Connect authenticator can be used for end-users to authenticate.
To workaround this limitations, a Webhook Token Authentication can be configured. The Kube APIServer then sends the Bearer Tokens (id_token) to an external webhook for validation:
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"spec": {
"token": "(BEARERTOKEN)"
}
}
Where upon verification, the remote webhook returns the identity of the user (if authentication succeeds):
{
"apiVersion": "authentication.k8s.io/v1beta1",
"kind": "TokenReview",
"status": {
"authenticated": true,
"user": {
"username": "janedoe@example.com",
"uid": "42",
"groups": [
"developers",
"qa"
],
"extra": {
"extrafield1": [
"extravalue1",
"extravalue2"
]
}
}
}
}
This repository is the out-of tree implementation of Dynamic Authentication KEP.
Use cases
How it works
This webhook is a Kubernetes controller that acts on OpenIDConnect
resources e.g:
apiVersion: authentication.gardener.cloud/v1alpha1
kind: OpenIDConnect
metadata:
name: foo
spec:
issuerURL: https://foo.bar
clientID: some-client-id
usernameClaim: email
usernamePrefix: "test-"
groupsClaim: groups
groupsPrefix: "baz-"
supportedSigningAlgs:
- RS256
requiredClaims:
baz: bar
caBundle: LS0tLS1CRUdJTiBDRVJU...base64-encoded CA certs for issuerURL.
Note: The fields in the specification corresponds to the kube-apiserver OIDC flags.
Registration of a new OpenID Connect provider
The flow is following:
- Admin adds a new
OpenIDConnect
to the cluster.
- The webhook controller watches for changes on this resource and does OIDC discovery. The OIDC provider's configuration has to be accessible under the
spec.issuerURL
with a well-known path (.well-known/openid-configuration).
- The webhook controller uses the
jwks_uri
obtained from the OIDC providers configuration, to fetch the OIDC provider's public keys from that endpoint.
- The webhook controller uses those keys, issuer, client_id and other settings to add OIDC authenticator to a in-memory list of Token Authenticators.
An overview of the controller:
End-user authentication via new OpenIDConnect IDP
When a user wants to authenticate to the kube-apiserver
via this new Custom OpenIDConnect IDP:
-
The user authenticates in Custom IDP.
-
id_token
is obtained from Custom IDP (e.g. ddeewfwef...
).
-
The user uses id_token
to perform an API call to Kube APIServer.
-
As the id_token
is not matched by any build-in or configured authenticators in the Kube APIServer, it is send to OpenID Connect Webhook Authenticator for validation.
{
"TokenReview": {
"kind": "TokenReview",
"apiVersion": "authentication.k8s.io/v1",
"spec": {
"token": "ddeewfwef..."
}
}
}
-
The webhook then iterates over all registered OpenIDConnect
Token authenticators and tries to validate the token.
-
Upon a successful validation it returns the TokenReview
with user, groups and extra parameters:
{
"TokenReview": {
"kind": "TokenReview",
"apiVersion": "authentication.k8s.io/v1",
"spec": {
"token": "ddeewfwef..."
},
"status": {
"authenticated": true,
"user": {
"username": "test-admin@example.com",
"groups": [
"test-some-group"
],
"extra": {
"gardener.cloud/authenticator/name": [
"gardener"
],
"gardener.cloud/authenticator/uid": [
"e5062528-e5a4-4b97-ad83-614d015b0979"
]
}
}
}
}
}
It adds the following extra information, that can be used by custom authorizers later on:
gardener.cloud/authenticator/name
contains the name of the OpenIDConnect
authenticator which was used.
gardener.cloud/authenticator/uid
contains the UID of the OpenIDConnect
authenticator which was used.
Docker images
Docker images are available here or you can choose to pull the latest pre-release version with the following command:
docker pull europe-docker.pkg.dev/gardener-project/public/gardener/oidc-webhook-authenticator:latest
Local development
For this setup the following components are needed:
The API server is started with --authentication-token-webhook-config-file
with kubeconfig
file pointing to the Webhook.
mkdir -p ~/.minikube/files/var/lib/minikube/certs
cp config/samples/minikube-webhook-kubeconfig.yaml ~/.minikube/files/var/lib/minikube/certs/minikube-webhook-kubeconfig.yaml
minikube start \
--extra-config=apiserver.authentication-token-webhook-config-file=/var/lib/minikube/certs/minikube-webhook-kubeconfig.yaml \
--extra-config=apiserver.authentication-token-webhook-cache-ttl=10s
To allow easy communication between the kube-apiserver
and the oidc-webhook-authenticator
minikube IP is added as control-plane.minikube.internal
in /etc/hosts
sudo sed -ie '/control-plane.minikube.internal/d' /etc/hosts
echo "$(minikube ip) control-plane.minikube.internal" | sudo tee -a /etc/hosts
Add the CRD:
kubectl apply -f config/crd/bases/authentication.gardener.cloud_openidconnects.yaml
Build the image, so it's accessible by minikube
:
minikube image build -t oidc-webhook-authenticator .
Deploy the oidc webhook authenticator.
kubectl apply -f config/samples/deployment.yaml
Create an OpenIDConnect
resource configured with your identity provider's settings (see an example here). Get a token from your identity provider. You can now authenticate against the minikube cluster.
curl -k -H "Authorization: Bearer $MY_TOKEN" $(k config view -o=jsonpath="{.clusters[?(@.name=='minikube')].cluster.server}")
Alternatively you can also use a token kubeconfig or the kubelogin plugin and configure an OIDC kubeconfig.