405 lines
12 KiB
Markdown
405 lines
12 KiB
Markdown
# TLS Bootstrapping Worker Nodes
|
|
|
|
In the previous step we configured a worker node by
|
|
- Creating a set of key pairs for the worker node by ourself
|
|
- Getting them signed by the CA by ourself
|
|
- Creating a kube-config file using this certificate by ourself
|
|
- Everytime the certificate expires we must follow the same process of updating the certificate by ourself
|
|
|
|
This is not a practical approach when you have 1000s of nodes in the cluster, and nodes dynamically being added and removed from the cluster. With TLS boostrapping:
|
|
|
|
- The Nodes can generate certificate key pairs by themselves
|
|
- The Nodes can generate certificate signing request by themselves
|
|
- The Nodes can submit the certificate signing request to the Kubernetes CA (Using the Certificates API)
|
|
- The Nodes can retrieve the signed certificate from the Kubernetes CA
|
|
- The Nodes can generate a kube-config file using this certificate by themselves
|
|
- The Nodes can start and join the cluster by themselves
|
|
- The Nodes can renew certificates when they expire by themselves
|
|
|
|
So let's get started!
|
|
|
|
# What is required for TLS Bootstrapping
|
|
|
|
**Certificates API:** The Certificate API (as discussed in the lecture) provides a set of APIs on Kubernetes that can help us manage certificates (Create CSR, Get them signed by CA, Retrieve signed certificate etc). The worker nodes (kubelets) have the ability to use this API to get certificates signed by the Kubernetes CA.
|
|
|
|
# Pre-Requisite
|
|
|
|
**kube-apiserver** - Ensure bootstrap token based authentication is enabled on the kube-apiserver.
|
|
|
|
`--enable-bootstrap-token-auth=true`
|
|
|
|
**kube-controller-manager** - The certificate requests are signed by the kube-controller-manager ultimately. The kube-controller-manager requires the CA Certificate and Key to perform these operations.
|
|
|
|
```
|
|
--cluster-signing-cert-file=/var/lib/kubernetes/ca.crt \\
|
|
--cluster-signing-key-file=/var/lib/kubernetes/ca.key
|
|
```
|
|
|
|
> Note: We have already configured these in our setup in this course
|
|
|
|
Copy the ca certificate to the worker node:
|
|
|
|
```
|
|
scp ca.crt worker-2:~/
|
|
```
|
|
|
|
## Step 1 Configure the Binaries on the Worker node
|
|
|
|
### Download and Install Worker Binaries
|
|
|
|
```
|
|
wget -q --show-progress --https-only --timestamping \
|
|
https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubectl \
|
|
https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kube-proxy \
|
|
https://storage.googleapis.com/kubernetes-release/release/v1.13.0/bin/linux/amd64/kubelet
|
|
```
|
|
|
|
Create the installation directories:
|
|
|
|
```
|
|
sudo mkdir -p \
|
|
/etc/cni/net.d \
|
|
/opt/cni/bin \
|
|
/var/lib/kubelet \
|
|
/var/lib/kube-proxy \
|
|
/var/lib/kubernetes \
|
|
/var/run/kubernetes
|
|
```
|
|
|
|
Install the worker binaries:
|
|
|
|
```
|
|
{
|
|
chmod +x kubectl kube-proxy kubelet
|
|
sudo mv kubectl kube-proxy kubelet /usr/local/bin/
|
|
}
|
|
```
|
|
### Move the ca certificate
|
|
|
|
`sudo mv ca.crt /var/lib/kubernetes/`
|
|
|
|
# Step 1 Create the Boostrap Token to be used by Nodes(Kubelets) to invoke Certificate API
|
|
|
|
For the workers(kubelet) to access the Certificates API, they need to authenticate to the kubernetes api-server first. For this we create a [Bootstrap Token](https://kubernetes.io/docs/reference/access-authn-authz/bootstrap-tokens/) to be used by the kubelet
|
|
|
|
Bootstrap Tokens take the form of a 6 character token id followed by 16 character token secret separated by a dot. Eg: abcdef.0123456789abcdef. More formally, they must match the regular expression [a-z0-9]{6}\.[a-z0-9]{16}
|
|
|
|
Bootstrap Tokens are created as a secret in the kube-system namespace.
|
|
|
|
```
|
|
cat > bootstrap-token-07401b.yaml <<EOF
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
# Name MUST be of form "bootstrap-token-<token id>"
|
|
name: bootstrap-token-07401b
|
|
namespace: kube-system
|
|
|
|
# Type MUST be 'bootstrap.kubernetes.io/token'
|
|
type: bootstrap.kubernetes.io/token
|
|
stringData:
|
|
# Human readable description. Optional.
|
|
description: "The default bootstrap token generated by 'kubeadm init'."
|
|
|
|
# Token ID and secret. Required.
|
|
token-id: 07401b
|
|
token-secret: f395accd246ae52d
|
|
|
|
# Expiration. Optional.
|
|
expiration: 2021-03-10T03:22:11Z
|
|
|
|
# Allowed usages.
|
|
usage-bootstrap-authentication: "true"
|
|
usage-bootstrap-signing: "true"
|
|
|
|
# Extra groups to authenticate the token as. Must start with "system:bootstrappers:"
|
|
auth-extra-groups: system:bootstrappers:worker
|
|
EOF
|
|
|
|
|
|
kubectl create -f bootstrap-token-07401b.yaml
|
|
|
|
```
|
|
|
|
Things to note:
|
|
- **expiration** - make sure its set to a date in the future.
|
|
- **auth-extra-groups** - this is the group the worker nodes are part of. It must start with "system:bootstrappers:" This group does not exist already. This group is associated with this token.
|
|
|
|
Once this is created the token to be used for authentication is `07401b.f395accd246ae52d`
|
|
|
|
|
|
## Step 2 Authorize workers(kubelets) to create CSR
|
|
|
|
Next we associate the group we created before to the system:node-bootstrapper ClusterRole. This ClusterRole gives the group enough permissions to bootstrap the kubelet
|
|
|
|
```
|
|
kubectl create clusterrolebinding create-csrs-for-bootstrapping --clusterrole=system:node-bootstrapper --group=system:bootstrappers
|
|
|
|
--------------- OR ---------------
|
|
|
|
cat > csrs-for-bootstrapping.yaml <<EOF
|
|
# enable bootstrapping nodes to create CSR
|
|
kind: ClusterRoleBinding
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
metadata:
|
|
name: create-csrs-for-bootstrapping
|
|
subjects:
|
|
- kind: Group
|
|
name: system:bootstrappers
|
|
apiGroup: rbac.authorization.k8s.io
|
|
roleRef:
|
|
kind: ClusterRole
|
|
name: system:node-bootstrapper
|
|
apiGroup: rbac.authorization.k8s.io
|
|
EOF
|
|
|
|
|
|
kubectl create -f csrs-for-bootstrapping.yaml
|
|
|
|
```
|
|
|
|
## Step 3 Authorize workers(kubelets) to approve CSR
|
|
```
|
|
kubectl create clusterrolebinding auto-approve-csrs-for-group --clusterrole=system:certificates.k8s.io:certificatesigningrequests:nodeclient --group=system:bootstrappers
|
|
|
|
--------------- OR ---------------
|
|
|
|
cat > auto-approve-csrs-for-group.yaml <<EOF
|
|
# Approve all CSRs for the group "system:bootstrappers"
|
|
kind: ClusterRoleBinding
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
metadata:
|
|
name: auto-approve-csrs-for-group
|
|
subjects:
|
|
- kind: Group
|
|
name: system:bootstrappers
|
|
apiGroup: rbac.authorization.k8s.io
|
|
roleRef:
|
|
kind: ClusterRole
|
|
name: system:certificates.k8s.io:certificatesigningrequests:nodeclient
|
|
apiGroup: rbac.authorization.k8s.io
|
|
EOF
|
|
|
|
|
|
kubectl create -f auto-approve-csrs-for-group.yaml
|
|
```
|
|
|
|
## Step 3 Authorize workers(kubelets) to Auto Renew Certificates on expiration
|
|
|
|
We now create the Cluster Role Binding required for the nodes to automatically renew the certificates on expiry. Note that we are NOT using the **system:bootstrappers** group here any more. Since by the renewal period, we believe the node would be bootstrapped and part of the cluster already. All nodes are part of the **system:nodes** group.
|
|
|
|
```
|
|
kubectl create clusterrolebinding auto-approve-renewals-for-nodes --clusterrole=system:certificates.k8s.io:certificatesigningrequests:selfnodeclient --group=system:nodes
|
|
|
|
--------------- OR ---------------
|
|
|
|
cat > auto-approve-renewals-for-nodes.yaml <<EOF
|
|
# Approve renewal CSRs for the group "system:nodes"
|
|
kind: ClusterRoleBinding
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
metadata:
|
|
name: auto-approve-renewals-for-nodes
|
|
subjects:
|
|
- kind: Group
|
|
name: system:nodes
|
|
apiGroup: rbac.authorization.k8s.io
|
|
roleRef:
|
|
kind: ClusterRole
|
|
name: system:certificates.k8s.io:certificatesigningrequests:selfnodeclient
|
|
apiGroup: rbac.authorization.k8s.io
|
|
EOF
|
|
|
|
|
|
kubectl create -f auto-approve-renewals-for-nodes.yaml
|
|
```
|
|
|
|
## Step 4 Configure Kubelet to TLS Bootstrap
|
|
|
|
It is now time to configure the second worker to TLS bootstrap using the token we generated
|
|
|
|
For worker-1 we started by creating a kubeconfig file with the TLS certificates that we manually generated.
|
|
Here, we don't have the certificates yet. So we cannot create a kubeconfig file. Instead we create a bootstrap-kubeconfig file with information about the token we created.
|
|
|
|
This is to be done on the `worker-2` node.
|
|
|
|
```
|
|
sudo kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-cluster bootstrap --server='https://192.168.5.30:6443' --certificate-authority=/var/lib/kubernetes/ca.crt
|
|
sudo kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-credentials kubelet-bootstrap --token=07401b.f395accd246ae52d
|
|
sudo kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig set-context bootstrap --user=kubelet-bootstrap --cluster=bootstrap
|
|
sudo kubectl config --kubeconfig=/var/lib/kubelet/bootstrap-kubeconfig use-context bootstrap
|
|
```
|
|
|
|
Or
|
|
|
|
```
|
|
cat <<EOF | sudo tee /var/lib/kubelet/bootstrap-kubeconfig
|
|
apiVersion: v1
|
|
clusters:
|
|
- cluster:
|
|
certificate-authority: /var/lib/kubernetes/ca.crt
|
|
server: https://192.168.5.30:6443
|
|
name: bootstrap
|
|
contexts:
|
|
- context:
|
|
cluster: bootstrap
|
|
user: kubelet-bootstrap
|
|
name: bootstrap
|
|
current-context: bootstrap
|
|
kind: Config
|
|
preferences: {}
|
|
users:
|
|
- name: kubelet-bootstrap
|
|
user:
|
|
token: 07401b.f395accd246ae52d
|
|
EOF
|
|
```
|
|
|
|
## Step 5 Create Kubelet Config File
|
|
|
|
Create the `kubelet-config.yaml` configuration file:
|
|
|
|
```
|
|
cat <<EOF | sudo tee /var/lib/kubelet/kubelet-config.yaml
|
|
kind: KubeletConfiguration
|
|
apiVersion: kubelet.config.k8s.io/v1beta1
|
|
authentication:
|
|
anonymous:
|
|
enabled: false
|
|
webhook:
|
|
enabled: true
|
|
x509:
|
|
clientCAFile: "/var/lib/kubernetes/ca.crt"
|
|
authorization:
|
|
mode: Webhook
|
|
clusterDomain: "cluster.local"
|
|
clusterDNS:
|
|
- "10.96.0.10"
|
|
resolvConf: "/run/systemd/resolve/resolv.conf"
|
|
runtimeRequestTimeout: "15m"
|
|
EOF
|
|
```
|
|
|
|
> Note: We are not specifying the certificate details - tlsCertFile and tlsPrivateKeyFile - in this file
|
|
|
|
## Step 6 Configure Kubelet Service
|
|
|
|
Create the `kubelet.service` systemd unit file:
|
|
|
|
```
|
|
cat <<EOF | sudo tee /etc/systemd/system/kubelet.service
|
|
[Unit]
|
|
Description=Kubernetes Kubelet
|
|
Documentation=https://github.com/kubernetes/kubernetes
|
|
After=docker.service
|
|
Requires=docker.service
|
|
|
|
[Service]
|
|
ExecStart=/usr/local/bin/kubelet \\
|
|
--bootstrap-kubeconfig="/var/lib/kubelet/bootstrap-kubeconfig" \\
|
|
--config=/var/lib/kubelet/kubelet-config.yaml \\
|
|
--image-pull-progress-deadline=2m \\
|
|
--kubeconfig=/var/lib/kubelet/kubeconfig \\
|
|
--cert-dir=/var/lib/kubelet/pki/ \\
|
|
--rotate-certificates=true \\
|
|
--rotate-server-certificates=true \\
|
|
--network-plugin=cni \\
|
|
--register-node=true \\
|
|
--v=2
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
```
|
|
|
|
Things to note here:
|
|
- **bootstrap-kubeconfig**: Location of the bootstrap-kubeconfig file.
|
|
- **cert-dir**: The directory where the generated certificates are stored.
|
|
- **rotate-certificates**: Rotates client certificates when they expire.
|
|
- **rotate-server-certificates**: Requests for server certificates on bootstrap and rotates them when they expire.
|
|
|
|
## Step 7 Configure the Kubernetes Proxy
|
|
|
|
```
|
|
sudo mv kube-proxy.kubeconfig /var/lib/kube-proxy/kubeconfig
|
|
```
|
|
|
|
Create the `kube-proxy-config.yaml` configuration file:
|
|
|
|
```
|
|
cat <<EOF | sudo tee /var/lib/kube-proxy/kube-proxy-config.yaml
|
|
kind: KubeProxyConfiguration
|
|
apiVersion: kubeproxy.config.k8s.io/v1alpha1
|
|
clientConnection:
|
|
kubeconfig: "/var/lib/kube-proxy/kubeconfig"
|
|
mode: "iptables"
|
|
clusterCIDR: "192.168.5.0/24"
|
|
EOF
|
|
```
|
|
|
|
Create the `kube-proxy.service` systemd unit file:
|
|
|
|
```
|
|
cat <<EOF | sudo tee /etc/systemd/system/kube-proxy.service
|
|
[Unit]
|
|
Description=Kubernetes Kube Proxy
|
|
Documentation=https://github.com/kubernetes/kubernetes
|
|
|
|
[Service]
|
|
ExecStart=/usr/local/bin/kube-proxy \\
|
|
--config=/var/lib/kube-proxy/kube-proxy-config.yaml
|
|
Restart=on-failure
|
|
RestartSec=5
|
|
|
|
[Install]
|
|
WantedBy=multi-user.target
|
|
EOF
|
|
```
|
|
|
|
## Step 8 Start the Worker Services
|
|
|
|
```
|
|
{
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable kubelet kube-proxy
|
|
sudo systemctl start kubelet kube-proxy
|
|
}
|
|
```
|
|
> Remember to run the above commands on worker node: `worker-2`
|
|
|
|
|
|
## Step 9 Approve Server CSR
|
|
|
|
`kubectl get csr`
|
|
|
|
```
|
|
NAME AGE REQUESTOR CONDITION
|
|
csr-95bv6 20s system:node:worker-2 Pending
|
|
```
|
|
|
|
|
|
Approve
|
|
|
|
`kubectl certificate approve csr-95bv6`
|
|
|
|
|
|
## Verification
|
|
|
|
List the registered Kubernetes nodes from the master node:
|
|
|
|
```
|
|
master-1$ kubectl get nodes --kubeconfig admin.kubeconfig
|
|
```
|
|
|
|
> output
|
|
|
|
```
|
|
NAME STATUS ROLES AGE VERSION
|
|
worker-1 NotReady <none> 93s v1.13.0
|
|
worker-2 NotReady <none> 93s v1.13.0
|
|
```
|
|
Note: It is OK for the worker node to be in a NotReady state. That is because we haven't configured Networking yet.
|
|
|
|
Next: [Configuring Kubectl](11-configuring-kubectl.md)
|