Certificates, kubeconfig and the CSR API
The cluster's certificates and how to renew them, what a kubeconfig holds, and how to give a new user a certificate through the CSR API.
Key ideas in this lesson · tap one for a simple explanation
What you will be able to do
- Find any control plane certificate, read its subject, issuer, SANs and expiry date with openssl, and renew it with kubeadm.
- Read and build a kubeconfig with kubectl config, with the certificates embedded in the file.
- Create a user with a private key and a CertificateSigningRequest, approve it and give the user a working kubeconfig.
A certificate is an ID card for a program or a person. Every part of a cluster shows one to prove who it is. Example: when the connects to , it shows its client certificate, and etcd checks that a CA it trusts signed it. A CA (certificate authority) issues these ID cards: whoever holds its key can sign new ones.
People reach the cluster through a : a file that says which cluster to talk to and which ID to show. This lesson covers both, plus a favorite exam task: create a new user and give them a kubeconfig that works. Certificates also expire and must be renewed. So the lesson touches two exam topics: Create and manage Kubernetes clusters using kubeadm and Manage the lifecycle of Kubernetes clusters.
The certificates kubeadm makes
kubeadm init creates a set of certificates and keys in /etc/kubernetes/pki. This set is called the cluster's PKI (public key infrastructure). These are the ones you will meet:
Next to pki/, kubeadm writes one kubeconfig per component in /etc/kubernetes: controller-manager.conf, scheduler.conf and kubelet.conf. There is also admin.conf for you, the administrator, and super-admin.conf for emergencies. What each identity may do is the topic of RBAC.
- Most certificates are valid for one year. The CAs are valid for ten years.
- The 's client certificate is the exception: the kubelet renews it on its own, under
/var/lib/kubelet/pki/.
Check and renew certificates
Two tools read certificates. kubeadm certs check-expiration shows when every kubeadm certificate expires. openssl x509 reads one certificate file in detail.
# exam: ssh <control-plane>; sudo -i lab: docker exec -it cka-control-plane bash
kubeadm certs check-expiration
# Subject, issuer, expiry and SANs of any certificate
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -subject -issuer -enddate
# subject=CN=kube-apiserver
# issuer=CN=kubernetes
# notAfter=Oct 17 09:51:49 2027 GMT
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -ext subjectAltName
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -text # everything
# Renew one certificate, or all of them (on a real cluster; see below before you do)
kubeadm certs renew apiserver
kubeadm certs renew allRenewing rewrites the certificate files, including the ones inside the kubeconfig files. But running control plane still hold the old certificate in memory. So after renewing:
- 1.Restart each control plane Pod. cannot restart a . Instead, move its file out of
/etc/kubernetes/manifests, wait about 20 seconds for the kubelet to stop it, then move it back. - 2.If your own kubeconfig is a copy of
admin.conf, copy the renewed file again.
kubeadm upgrade apply also renews every certificate. That is why a cluster upgraded at least once a year never hits expiry.
Inside a kubeconfig
A kubeconfig is a file on your machine. It tells kubectl where the cluster is and who you are. It holds three lists and a pointer:
A kubeconfig file
~/.kube/config
clusters
where: the API server's URL and its CA
users
who: a certificate and key, or a token
contexts
one cluster + one user + a namespace
current-context
the context kubectl uses now
Example: the context jane@kind-cka means talk to cluster kind-cka, as user jane, in namespace dev.
apiVersion: v1
kind: Config
clusters: # where: API server URL and the CA that signed its certificate
- name: kind-cka
cluster:
server: https://127.0.0.1:52634
certificate-authority-data: LS0tLS1CRUdJTi... # base64 PEM, or certificate-authority: /path/ca.crt
users: # who: a client certificate, a token or an exec plugin
- name: jane
user:
client-certificate-data: LS0tLS1CRUdJTi...
client-key-data: LS0tLS1CRUdJTi...
contexts: # a named pairing of cluster and user, with an optional default namespace
- name: jane@kind-cka
context:
cluster: kind-cka
user: jane
namespace: dev
current-context: jane@kind-ckaTo list, switch and change contexts with kubectl config, see kubectl fluency. Below you build a kubeconfig from scratch, for a new user.
A new user through the CSR API
Kubernetes has no User object. Instead, any client certificate signed by the cluster CA lets you log in. Its Common Name (CN) becomes the username, and each Organization (O) becomes a group. Example: a certificate for /CN=jane/O=developers logs in as user jane in group developers.
The CertificateSigningRequest (CSR) API gets such a certificate signed, without anyone touching ca.key:
- 1.You submit a CSR object for the signer
kubernetes.io/kube-apiserver-client. - 2.An administrator approves it. This signer never approves anything automatically.
- 3.The controller manager signs it with the cluster CA. The certificate appears in the CSR's status.
Key and request
jane.key and jane.csr, on your machine
openssl
CSR object
waits as Pending
kubectl apply
Approve
the cluster CA signs it
kubectl certificate approve
jane.crt
goes into jane's kubeconfig
expirationSeconds asks for how long the certificate stays valid, at least 600 seconds. The signer may make it shorter.
mkdir -p /tmp/cka && cd /tmp/cka
openssl genrsa -out jane.key 2048
openssl req -new -key jane.key -subj "/CN=jane/O=developers" -out jane.csr
cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
name: jane
spec:
request: $(base64 < jane.csr | tr -d '\n')
signerName: kubernetes.io/kube-apiserver-client
expirationSeconds: 86400
usages:
- client auth
EOF
kubectl get csr jane # CONDITION: Pending
kubectl certificate approve jane
kubectl get csr jane # CONDITION: Approved,Issued
kubectl get csr jane -o jsonpath='{.status.certificate}' | base64 -d > jane.crt
openssl x509 -in jane.crt -noout -subject -issuer -enddateNow jane has a certificate. Put it in a kubeconfig file of her own, together with the cluster's address and CA. Run this in the same folder, /tmp/cka:
# A kubeconfig for jane in its own file. On a control plane node the CA is /etc/kubernetes/pki/ca.crt.
SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
kubectl config view --minify --raw -o jsonpath='{.clusters[0].cluster.certificate-authority-data}' | base64 -d > ca.crt
kubectl config set-cluster kind-cka --server="$SERVER" --certificate-authority=ca.crt --embed-certs=true --kubeconfig=jane.kubeconfig
kubectl config set-credentials jane --client-certificate=jane.crt --client-key=jane.key --embed-certs=true --kubeconfig=jane.kubeconfig
kubectl config set-context jane@kind-cka --cluster=kind-cka --user=jane --kubeconfig=jane.kubeconfig
kubectl config use-context jane@kind-cka --kubeconfig=jane.kubeconfig
kubectl --kubeconfig=jane.kubeconfig auth whoami
kubectl --kubeconfig=jane.kubeconfig get pods # Forbidden: authenticated, not yet authorizedIn the official docs· open on exam day too
- Certificates and Certificate Signing Requestssearch: certificate signing request normal user
- Certificate Management with kubeadmsearch: kubeadm certs renew
- Organizing Cluster Access Using kubeconfig Filessearch: kubeconfig
- PKI certificates and requirementssearch: pki certificates
Inspect control plane certificates
cka-control-plane. (1) Write the expiry date of the API server's serving certificate to /tmp/cka/ar-apiserver-expiry.txt, exactly as openssl prints it after notAfter= (for example Oct 17 09:51:49 2027 GMT). (2) Find the client certificate the API server presents to etcd and write the Common Name of the CA that issued it to /tmp/cka/ar-etcd-client-issuer.txt. (3) Save the complete output of kubeadm certs check-expiration to /tmp/cka/ar-cert-expiration.txt.Cluster:kubectl config use-context kind-cka
- 1
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-1/setup | bash - 2
Solve it in your terminal
Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under
/tmp/cka/. - 3
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-1/verify | bashGot FAIL? Read which check failed, fix it, run it again. - 4
Clean up (optional)
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-1/cleanup | bash
Create a user with the CSR API and a kubeconfig
ar-dev in group ar-team. Generate a 2048-bit RSA private key at /tmp/cka/ar-dev.key and a certificate signing request for it. Submit the request as a CertificateSigningRequest named ar-dev for signer kubernetes.io/kube-apiserver-client, valid for 24 hours, approve it, and save the issued certificate as /tmp/cka/ar-dev.crt. Then create a self-contained kubeconfig at /tmp/cka/ar-dev.kubeconfig (CA, certificate and key embedded, not referenced by path) whose current context connects to this cluster as ar-dev with default namespace ar-certs. A RoleBinding that lets ar-dev read Pods in ar-certs already exists. Do not change your own kubeconfig.Cluster:kubectl config use-context kind-cka
- 1
Set up the scenario
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-2/setup | bash - 2
Solve it in your terminal
Use kubectl and the Kubernetes docs, like on exam day. Save any answer files under
/tmp/cka/. - 3
Check your work
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-2/verify | bashGot FAIL? Read which check failed, fix it, run it again. - 4
Clean up (optional)
curl -fsSL https://codewithmuh.com/labs/cka/t/ar-certs-2/cleanup | bash
Finished this lesson?
0 of 2 tasks passed. Your readiness counts tasks, so run them before you move on.