mirror of
https://github.com/jpetazzo/container.training.git
synced 2026-05-06 00:46:56 +00:00
Add TLS bootstrap
This commit is contained in:
259
slides/k8s/bootstrap.md
Normal file
259
slides/k8s/bootstrap.md
Normal file
@@ -0,0 +1,259 @@
|
||||
# TLS bootstrap
|
||||
|
||||
- kubelet needs TLS keys and certificates to communicate with the control plane
|
||||
|
||||
- How do we generate this information?
|
||||
|
||||
- How do we make it available to kubelet?
|
||||
|
||||
---
|
||||
|
||||
## Option 1: push
|
||||
|
||||
- When we want to provision a node:
|
||||
|
||||
- generate its keys, certificate, and sign centrally
|
||||
|
||||
- push the files to the node
|
||||
|
||||
- OK for "traditional", on-premises deployments
|
||||
|
||||
- Not OK for cloud deployments with auto-scaling
|
||||
|
||||
---
|
||||
|
||||
## Option 2: poll + push
|
||||
|
||||
- Discover nodes when they are created
|
||||
|
||||
(e.g. with cloud API)
|
||||
|
||||
- When we detect a new node, push TLS material to the node
|
||||
|
||||
(like in option 1)
|
||||
|
||||
- It works, but:
|
||||
|
||||
- discovery code is specific to each provider
|
||||
|
||||
- relies heavily on the cloud provider API
|
||||
|
||||
- doesn't work on-premises
|
||||
|
||||
- doesn't scale
|
||||
|
||||
---
|
||||
|
||||
## Option 3: bootstrap tokens + CSR API
|
||||
|
||||
- Since Kubernetes 1.4, the Kubernetes API supports CSR
|
||||
|
||||
(Certificate Signing Requests)
|
||||
|
||||
- This is similar to the protocol used to obtain e.g. HTTPS certifiates:
|
||||
|
||||
- subject (here, kubelet) generates TLS keys and CSR
|
||||
|
||||
- subject submits CSR to CA
|
||||
|
||||
- CA validates (or not) the CSR
|
||||
|
||||
- CA sends back signed certificate to subject
|
||||
|
||||
- This is combined with *bootstrap tokens*
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap tokens
|
||||
|
||||
- A [bootstrap token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) is an API access token
|
||||
|
||||
- it is a Secret with type `bootstrap.kubernetes.io/token`
|
||||
|
||||
- it is 6 public characters (ID) + 16 secret characters
|
||||
<br/>(example: `whd3pq.d1ushuf6ccisjacu`)
|
||||
|
||||
- it gives access to groups `system:bootstrap:<ID>` and `system:bootstrappers`
|
||||
|
||||
- additional groups can be specified in the Secret
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap tokens with kubeadm
|
||||
|
||||
- kubeadm automatically creates a bootstrap token
|
||||
|
||||
(it is shown at the end of `kubeadm init`)
|
||||
|
||||
- That token adds the group `system:bootstrappers:kubeadm:default-node-token`
|
||||
|
||||
- kubeadm also creates a ClusterRoleBinding `kubeadm:kubelet-bootstrap`
|
||||
<br/>binding `...:default-node-token` to ClusterRole `system:node-bootstrapper`
|
||||
|
||||
- That ClusterRole gives create/get/list/watch permissions on the CSR API
|
||||
|
||||
---
|
||||
|
||||
## Bootstrap tokens in practice
|
||||
|
||||
- Let's list our bootstrap tokens on a cluster created with kubeadm
|
||||
|
||||
.exercise[
|
||||
|
||||
- Log into node `test1`
|
||||
|
||||
- View bootstrap tokens:
|
||||
```bash
|
||||
sudo kubeadm token list
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
- Tokens are short-lived
|
||||
|
||||
- We can create new tokens with `kubeadm` if necessary
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Retrieving bootstrap tokens with kubectl
|
||||
|
||||
- Bootstrap tokens are Secrets with type `bootstrap.kubernetes.io/token`
|
||||
|
||||
- Token ID and secret are in data fields `token-id` and `token-secret`
|
||||
|
||||
- In Secrets, data fields are encoded with Base64
|
||||
|
||||
- This "very simple" command will show us the tokens:
|
||||
|
||||
```
|
||||
kubectl -n kube-system get secrets -o json |
|
||||
jq -r '.items[]
|
||||
| select(.type=="bootstrap.kubernetes.io/token")
|
||||
| ( .data["token-id"] + "Lg==" + .data["token-secret"] + "Cg==")
|
||||
' | base64 -d
|
||||
```
|
||||
|
||||
(On recent versions of `jq`, you can simplify by using filter `@base64d`.)
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Using a bootstrap token
|
||||
|
||||
- The token we need to use has the form `abcdef.1234567890abcdef`
|
||||
|
||||
.exercise[
|
||||
|
||||
- Check that it is accepted by the API server:
|
||||
```bash
|
||||
curl -k -H "Authorization: Bearer abcdef.1234567890abcdef"
|
||||
```
|
||||
|
||||
- We should see that we are *authenticated* but not *authorized*:
|
||||
```
|
||||
User \"system:bootstrap:abcdef\" cannot get path \"/\""
|
||||
```
|
||||
|
||||
- Check that we can access the CSR API:
|
||||
```bash
|
||||
curl -k -H "Authorization: Bearer abcdef.1234567890abcdef" \
|
||||
https://10.96.0.1/apis/certificates.k8s.io/v1beta1/certificatesigningrequests
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
---
|
||||
|
||||
## The cluster-info ConfigMap
|
||||
|
||||
- Before we can talk to the API, we need:
|
||||
|
||||
- the API server address (obviously!)
|
||||
|
||||
- the cluster CA certificate
|
||||
|
||||
- That information is stored in a public ConfigMap
|
||||
|
||||
.exercise[
|
||||
|
||||
- Retrieve that ConfigMap:
|
||||
```bash
|
||||
curl -k https://10.96.0.1/api/v1/namespaces/kube-public/configmaps/cluster-info
|
||||
```
|
||||
|
||||
]
|
||||
|
||||
*Extracting the kubeconfig file is left as an exercise for the reader.*
|
||||
|
||||
---
|
||||
|
||||
class: extra-details
|
||||
|
||||
## Signature of the config-map
|
||||
|
||||
- You might have noticed a few `jws-kubeconfig-...` fields
|
||||
|
||||
- These are config-map signatures
|
||||
|
||||
(so that the client can protect against MITM attacks)
|
||||
|
||||
- These are JWS signatures using HMAC-SHA256
|
||||
|
||||
(see [here](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/#configmap-signing) for more details)
|
||||
|
||||
---
|
||||
|
||||
## Putting it all together
|
||||
|
||||
This is the TLS bootstrap mechanism, step by step.
|
||||
|
||||
- The node uses the cluster-info ConfigMap to get the cluster CA certificate
|
||||
|
||||
- The node generates its keys and CSR
|
||||
|
||||
- Using the bootstrap token, the node creates a CertificateSigningRequest object
|
||||
|
||||
- The node watches the CSR object
|
||||
|
||||
- The CSR object is accepted (automatically or by an admin)
|
||||
|
||||
- The node gets notified, and retrives the certificate
|
||||
|
||||
- The node can now join the cluster
|
||||
|
||||
---
|
||||
|
||||
## Bottom line
|
||||
|
||||
- If you paid attention, we still need a way to:
|
||||
|
||||
- either safely get the bootstrap token to the nodes
|
||||
|
||||
- or disable auto-approval and manually approve the nodes when they join
|
||||
|
||||
- The goal of the TLS bootstrap mechanism is *not* to solve this
|
||||
|
||||
(in terms of information knowledge, it's fundamentally impossible!)
|
||||
|
||||
- But it reduces the differences between environments, infrastructures, providers ...
|
||||
|
||||
- It gives a mechanism that is easier to use, and flexible enough, for most scenarios
|
||||
|
||||
---
|
||||
|
||||
## More information
|
||||
|
||||
- As always, the Kubernetes documentation has extra details:
|
||||
|
||||
- [TLS management](https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/)
|
||||
|
||||
- [Authenticating with bootstrap tokens](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/)
|
||||
|
||||
- [TLS bootstrapping](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/)
|
||||
|
||||
- [kubeadm token](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-token/) command
|
||||
|
||||
- [kubeadm join](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/) command (has details about [the join workflow](https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/#join-workflow))
|
||||
Reference in New Issue
Block a user