mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-07-27 16:51:21 +00:00
380 lines
8.7 KiB
Markdown
380 lines
8.7 KiB
Markdown
# OpenID Connect
|
|
|
|
- The Kubernetes API server can perform authentication with OpenID connect
|
|
|
|
- This requires an *OpenID provider*
|
|
|
|
(external authorization server using the OAuth 2.0 protocol)
|
|
|
|
- We can use a third-party provider (e.g. Google) or run our own (e.g. Dex)
|
|
|
|
- We are going to give an overview of the protocol
|
|
|
|
- We will show it in action (in a simplified scenario)
|
|
|
|
---
|
|
|
|
## Workflow overview
|
|
|
|
- We want to access our resources (a Kubernetes cluster)
|
|
|
|
- We authenticate with the OpenID provider
|
|
|
|
- we can do this directly (e.g. by going to https://accounts.google.com)
|
|
|
|
- or maybe a kubectl plugin can open a browser page on our behalf
|
|
|
|
- After authenticating us, the OpenID provider gives us:
|
|
|
|
- an *id token* (a short-lived signed JSON Web Token, see next slide)
|
|
|
|
- a *refresh token* (to renew the *id token* when needed)
|
|
|
|
- We can now issue requests to the Kubernetes API with the *id token*
|
|
|
|
- The API server will verify that token's content to authenticate us
|
|
|
|
---
|
|
|
|
## JSON Web Tokens
|
|
|
|
- A JSON Web Token (JWT) has three parts:
|
|
|
|
- a header specifying algorithms and token type
|
|
|
|
- a payload (indicating who issued the token, for whom, which purposes...)
|
|
|
|
- a signature generated by the issuer (the issuer = the OpenID provider)
|
|
|
|
- Anyone can verify a JWT without contacting the issuer
|
|
|
|
(except to obtain the issuer's public key)
|
|
|
|
- Pro tip: we can inspect a JWT with https://jwt.io/
|
|
|
|
---
|
|
|
|
## How the Kubernetes API uses JWT
|
|
|
|
- Server side
|
|
|
|
- enable OIDC authentication
|
|
|
|
- indicate which issuer (provider) should be allowed
|
|
|
|
- indicate which audience (or "client id") should be allowed
|
|
|
|
- optionally, map or prefix user and group names
|
|
|
|
- Client side
|
|
|
|
- obtain JWT as described earlier
|
|
|
|
- pass JWT as authentication token
|
|
|
|
- renew JWT when needed (using the refresh token)
|
|
|
|
---
|
|
|
|
## Demo time!
|
|
|
|
- We will use [Google Accounts](https://accounts.google.com) as our OpenID provider
|
|
|
|
- We will use the [Google OAuth Playground](https://developers.google.com/oauthplayground) as the "audience" or "client id"
|
|
|
|
- We will obtain a JWT through Google Accounts and the OAuth Playground
|
|
|
|
- We will enable OIDC in the Kubernetes API server
|
|
|
|
- We will use the JWT to authenticate
|
|
|
|
.footnote[If you can't or won't use a Google account, you can try to adapt this to another provider.]
|
|
|
|
---
|
|
|
|
## Checking the API server logs
|
|
|
|
- The API server logs will be particularly useful in this section
|
|
|
|
(they will indicate e.g. why a specific token is rejected)
|
|
|
|
- Let's keep an eye on the API server output!
|
|
|
|
.exercise[
|
|
|
|
- Tail the logs of the API server:
|
|
```bash
|
|
kubectl logs kube-apiserver-node1 --follow --namespace=kube-system
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Authenticate with the OpenID provider
|
|
|
|
- We will use the Google OAuth Playground for convenience
|
|
|
|
- In a real scenario, we would need our own OAuth client instead of the playground
|
|
|
|
(even if we were still using Google as the OpenID provider)
|
|
|
|
.exercise[
|
|
|
|
- Open the Google OAuth Playground:
|
|
```
|
|
https://developers.google.com/oauthplayground/
|
|
```
|
|
|
|
- Enter our own custom scope in the text field:
|
|
```
|
|
https://www.googleapis.com/auth/userinfo.email
|
|
```
|
|
|
|
- Click on "Authorize APIs" and allow the playground to access our email address
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Obtain our JSON Web Token
|
|
|
|
- The previous step gave us an "authorization code"
|
|
|
|
- We will use it to obtain tokens
|
|
|
|
.exercise[
|
|
|
|
- Click on "Exchange authorization code for tokens"
|
|
|
|
]
|
|
|
|
- The JWT is the very long `id_token` that shows up on the right hand side
|
|
|
|
(it is a base64-encoded JSON object, and should therefore start with `eyJ`)
|
|
|
|
---
|
|
|
|
## Using our JSON Web Token
|
|
|
|
- We need to create a context (in kubeconfig) for our token
|
|
|
|
(if we just add the token or use `kubectl --token`, our certificate will still be used)
|
|
|
|
.exercise[
|
|
|
|
- Create a new authentication section in kubeconfig:
|
|
```bash
|
|
kubectl config set-credentials myjwt --token=eyJ...
|
|
```
|
|
|
|
- Try to use it:
|
|
```bash
|
|
kubectl --user=myjwt get nodes
|
|
```
|
|
|
|
]
|
|
|
|
We should get an `Unauthorized` response, since we haven't enabled OpenID Connect in the API server yet. We should also see `invalid bearer token` in the API server log output.
|
|
|
|
---
|
|
|
|
## Enabling OpenID Connect
|
|
|
|
- We need to add a few flags to the API server configuration
|
|
|
|
- These two are mandatory:
|
|
|
|
`--oidc-issuer-url` → URL of the OpenID provider
|
|
|
|
`--oidc-client-id` → app requesting the authentication
|
|
<br/>(in our case, that's the ID for the Google OAuth Playground)
|
|
|
|
- This one is optional:
|
|
|
|
`--oidc-username-claim` → which field should be used as user name
|
|
<br/>(we will use the user's email address instead of an opaque ID)
|
|
|
|
- See the [API server documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#configuring-the-api-server
|
|
) for more details about all available flags
|
|
|
|
---
|
|
|
|
## Updating the API server configuration
|
|
|
|
- The instructions below will work for clusters deployed with kubeadm
|
|
|
|
(or where the control plane is deployed in static pods)
|
|
|
|
- If your cluster is deployed differently, you will need to adapt them
|
|
|
|
.exercise[
|
|
|
|
- Edit `/etc/kubernetes/manifests/kube-apiserver.yaml`
|
|
|
|
- Add the following lines to the list of command-line flags:
|
|
```yaml
|
|
- --oidc-issuer-url=https://accounts.google.com
|
|
- --oidc-client-id=407408718192.apps.googleusercontent.com
|
|
- --oidc-username-claim=email
|
|
```
|
|
]
|
|
|
|
---
|
|
|
|
## Restarting the API server
|
|
|
|
- The kubelet monitors the files in `/etc/kubernetes/manifests`
|
|
|
|
- When we save the pod manifest, kubelet will restart the corresponding pod
|
|
|
|
(using the updated command line flags)
|
|
|
|
.exercise[
|
|
|
|
- After making the changes described on the previous slide, save the file
|
|
|
|
- Issue a simple command (like `kubectl version`) until the API server is back up
|
|
|
|
(it might take between a few seconds and one minute for the API server to restart)
|
|
|
|
- Restart the `kubectl logs` command to view the logs of the API server
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## Using our JSON Web Token
|
|
|
|
- Now that the API server is set up to recognize our token, try again!
|
|
|
|
.exercise[
|
|
|
|
- Try an API command with our token:
|
|
```bash
|
|
kubectl --user=myjwt get nodes
|
|
kubectl --user=myjwt get pods
|
|
```
|
|
|
|
]
|
|
|
|
We should see a message like:
|
|
```
|
|
Error from server (Forbidden): nodes is forbidden: User "jean.doe@gmail.com"
|
|
cannot list resource "nodes" in API group "" at the cluster scope
|
|
```
|
|
|
|
→ We were successfully *authenticated*, but not *authorized*.
|
|
|
|
---
|
|
|
|
## Authorizing our user
|
|
|
|
- As an extra step, let's grant read access to our user
|
|
|
|
- We will use the pre-defined ClusterRole `view`
|
|
|
|
.exercise[
|
|
|
|
- Create a ClusterRoleBinding allowing us to view resources:
|
|
```bash
|
|
kubectl create clusterrolebinding i-can-view \
|
|
--user=`jean.doe@gmail.com` --clusterrole=view
|
|
```
|
|
|
|
(make sure to put *your* Google email address there)
|
|
|
|
- Confirm that we can now list pods with our token:
|
|
```bash
|
|
kubectl --user=myjwt get pods
|
|
```
|
|
|
|
]
|
|
|
|
---
|
|
|
|
## From demo to production
|
|
|
|
.warning[This was a very simplified demo! In a real deployment...]
|
|
|
|
- We wouldn't use the Google OAuth Playground
|
|
|
|
- We *probably* wouldn't even use Google at all
|
|
|
|
(it doesn't seem to provide a way to include groups!)
|
|
|
|
- Some popular alternatives:
|
|
|
|
- [Dex](https://github.com/dexidp/dex),
|
|
[Keycloak](https://www.keycloak.org/)
|
|
(self-hosted)
|
|
|
|
- [Okta](https://developer.okta.com/docs/how-to/creating-token-with-groups-claim/#step-five-decode-the-jwt-to-verify)
|
|
(SaaS)
|
|
|
|
- We would use a helper (like the [kubelogin](https://github.com/int128/kubelogin) plugin) to automatically obtain tokens
|
|
|
|
---
|
|
|
|
class: extra-details
|
|
|
|
## Service Account tokens
|
|
|
|
- The tokens used by Service Accounts are JWT tokens as well
|
|
|
|
- They are signed and verified using a special service account key pair
|
|
|
|
.exercise[
|
|
|
|
- Extract the token of a service account in the current namespace:
|
|
```bash
|
|
kubectl get secrets -o jsonpath={..token} | base64 -d
|
|
```
|
|
|
|
- Copy-paste the token to a verification service like https://jwt.io
|
|
|
|
- Notice that it says "Invalid Signature"
|
|
|
|
]
|
|
|
|
---
|
|
|
|
class: extra-details
|
|
|
|
## Verifying Service Account tokens
|
|
|
|
- JSON Web Tokens embed the URL of the "issuer" (=OpenID provider)
|
|
|
|
- The issuer provides its public key through a well-known discovery endpoint
|
|
|
|
(similar to https://accounts.google.com/.well-known/openid-configuration)
|
|
|
|
- There is no such endpoint for the Service Account key pair
|
|
|
|
- But we can provide the public key ourselves for verification
|
|
|
|
---
|
|
|
|
class: extra-details
|
|
|
|
## Verifying a Service Account token
|
|
|
|
- On clusters provisioned with kubeadm, the Service Account key pair is:
|
|
|
|
`/etc/kubernetes/pki/sa.key` (used by the controller manager to generate tokens)
|
|
|
|
`/etc/kubernetes/pki/sa.pub` (used by the API server to validate the same tokens)
|
|
|
|
.exercise[
|
|
|
|
- Display the public key used to sign Service Account tokens:
|
|
```bash
|
|
sudo cat /etc/kubernetes/pki/sa.pub
|
|
```
|
|
|
|
- Copy-paste the key in the "verify signature" area on https://jwt.io
|
|
|
|
- It should now say "Signature Verified"
|
|
|
|
]
|