diff --git a/slides/k8s/bootstrap.md b/slides/k8s/bootstrap.md new file mode 100644 index 00000000..70989ea0 --- /dev/null +++ b/slides/k8s/bootstrap.md @@ -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 +
(example: `whd3pq.d1ushuf6ccisjacu`) + + - it gives access to groups `system:bootstrap:` 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` +
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))