In this guide we will discuss how to create Key/Certificate pairs to facilitate secure communication between a Kubernetes Cluster components, for this guide we will use OpenSSL and its CLI tool
In summary, each component in our cluster requires a Key/Certificate pair for communicating with other server components
Where the Kube API Server acts as a server for almost all the components of the cluster, including
- Admin Client
- Kube Scheduler
- Kube Controller Manager
- Kube Proxy
- Kubelet
In addition to acting as a server, the Kube API Server also acts as a client for the following components of the cluster
- Kubelet
- ETCD Server
A summary of the communication diagram is like the following
The procedure for creating a Certificate
First: Generate a Private Key
<PrivateKey>
: The name of the keyopenssl genrsa -out <PrivateKey>.key 2048
Second: Create a Certificate Signing Request (CSR)
<CommonName>
: The name of the subject that the certificate would hold<Organization>
: The subject's Organization<CertificateSigningRequest>
: The request for signing the key, this would be sent to the CA to signopenssl req \ -new \ -key <PrivateKey>.key \ -subj "/CN=<CommonName>/O=<Organization>" \ -out <CertificateSigningRequest>.csr
Third: Sign the CSR Certificates
Self-signing the CSR
<CertificateAuthorityPrivateKey>
: The private key of the CA<SignedCertificate>
: The signed certificateopenssl x509 \ -req \ -in <CertificateSigningRequest>.csr \ -signkey <CertificateAuthorityPrivateKey>.key \ -out <SignedCertificate>.crt
Where
<CertificateAuthorityPrivateKey>.key
and<PrivateKey>.key
are the sameSigning the CSR with a Certificate Authority (CA)
<CACertificate>
: The CA signed public certificate<CAPrivateKey>
: The CA private key<SignedCertificate>
: The signed certificate after approving the CSRopenssl x509 \ -req \ -in <CertificateSigningRequest>.csr \ -CA <CACertificate>.crt \ -CAkey <CAPrivateKey>.key \ -out <SignedCertificate>.crt
Bonus: How to check the details of a certificate After creating a Public Signed Certificate we can view its details with the following command
openssl x509 \ -in path/to/certificate.crt \ -text \ -noout
Here we can see the details of the Certificate, like the name of the certificate holder (Common Name, CN), the Issuer CN, and the validity of the certificate
Certificate: Data: Version: 1 (0x0) Serial Number: ... Signature Algorithm: sha256WithRSAEncryption Issuer: CN = Mahmoud Validity Not Before: Oct 26 00:00:00 2022 GMT Not After : Nov 25 00:00:00 2022 GMT Subject: CN = Mahmoud Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: ... Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption ...
The pair of files <PrivateKey>.key
, and <SignedCertificate>.crt
will be referred to as a Key/Certificate pair
Specific implementation for Cluster Components
Creating Key/Certificate pairs is a pretty straightforward process, but we have to consider specific properties of special components in our cluster
First of all: The Certificate Authority
Certificate Authority (CA) is an entity that stores, signs, and issues digital certificates
We need at least one CA in our cluster to verify the identity of each component
Certificate Parameters:
<CommonName>
: Any, cannot be null<Organization>
: Can be null
Notes:
- This is the only certificate that can be self-signed where
<CertificateAuthorityPrivateKey>.key
is the same as<PrivateKey>.key
The Admin Client
The Admin Client is the user that will perform administrative actions on the cluster, the Key/Certificate pair will be used to identify the identity of this user
Certificate Parameters:
<CommonName>
: Any<Organization>
: systems:masters
Other Control Plane Clients
Other control plane clients include:
- Kube Scheduler
- Controller Manager
- Kube Proxy
Certificate Parameters:
<CommonName>
: The name of the component prefixed with system:- Kube Scheduler:
system:kube-scheduler
- Controller Manager:
system:kube-controller-manager
- Kube Proxy:
system:kube-proxy
- Kube Scheduler:
ETCD Server
ETCD is the key/value store that stores all of the cluster's state
It receives requests from the Kube API Server to persist changes to disk
A Key/Cert pair is used to identify the identity of the ETCD server
Certificate Parameters:
CommonName
: Any, e.g. etcd-server
Command line arguments:
Then we can pass the required values for the etcd
executable
--key-file
: ETCD Server Private Key--cert-file
: ETCD Server Signed Certificate--trusted-ca-file
: The CA Signed Certificate
Note: When implementing ETCD as a cluster -not a single server- additional key/cert pairs should be created per each peer of the ETCD cluster
These values can be stated in the following command line arguments when launching ETCD
--peer-client-cert-auth
: Set to true to enable TLS authentication with peers--peer-cert-file
: The signed certificate of the peers, separated by commas--peer-key-file
: ETCD peers' private key, separated by commas--peer-trusted-ca-file
: The CA Signed Certificate
Kube API Server
The Kube API Server is the center of all communications inside a Kubernetes cluster, and therefore we need to secure all communications to and from it
There are 3 Key/Certificate pairs required for the Kube API Server to identify itself for the following communications
- The Server for control plane component communication
- A client to communicate with ETCD Server
- A client to communicate with Kubelets
The Server for control plane components
Domain names and IP address of the API Server should be stated in the certificate signing request, these values can be stated in an external OpenSSL Configuration file
# Signing
openssl req \
-new \
-key apiserver.key \
-subj "/CN=kube-apiserver" \
-out apiserver.csr \
-config openssl.cnf
Where openssl.cnf
is the following
[req]
req_extensions = v3_req
distinguished_name = req_distinguished_name
[v3_req]
basicConstraints = CA:FALSE
keyUsage = nonRepudiation,
subjectAltName = @alt_names
[alt_names]
DNS.1 = kubernetes
DNS.2 = kubernetes.default
DNS.3 = kubernetes.default.svc
DNS.4 = kubernetes.default.svc.cluster.local
IP.1 = 10.96.0.1
IP.2 = 172.17.0.87
Command Line Arguments:
Then we can pass the required values for the kube-apiserver
executable
--client-ca-file
: The CA Signed Certificate--tls-cert-file
: The Signed Certificate of the API Server--tls-private-key-file
: The Private Key of the API Server
A client to communicate with ETCD Server
As stated earlier, the Kube API Server communicates with the ETCD server to store and persist resource data, as such it acts as a client for the ETCD server
Command Line Arguments:
The required values for the kube-apiserver
executable are
--etcd-cafile
: The CA Signed Certificate--etcd-certfile
: The API Server signed certificate for communicating with ETCD (as a client)--etcd-keyfile
: The API Server private key for communicating with ETCD (as a client)
A client to communicate with Kubelets
The Kube API Server communicates with the node Kubelet servers for instructions on creating and deleting pods and other resources
Command Line Arguments:
The required values for the kube-apiserver
executable are
--kubelet-certificate-authority
: The CA Signed Certificate--kubelet-client-certificate
: The API Server signed certificate for communicating with Kubelets (as a client)--kubelet-client-key
: The API Server private key for communicating with Kubelets (as a client)
Kubelets
Kubelet is a service that runs on the nodes of the cluster that controls launching and terminating containerized workloads on that node
There are 2 Key/Certificate pairs required for the Kubelet for the following communications
- The Server for Kube API Server communication
- A client to communicate with Kube API Server
The Server for Kube API Server communication
The Kubelet exposes a server that the Kube API Server can communicate to, so it needs to identify itself for this communication
Certificate Parameters:
<Common Name>
: The name of the node itself, will be used to identify the node, e.g.node01
The Kubelet Manifest:
---
authentication:
x509:
clientCAFile: <CA Signed Certificate>
tlsCertFile: <Kubelet Server Signed Certificate>
tlsPrivateKeyFile: <Kubelet Server Private Key>
A client to communicate with Kube API Server
Also, the Kubelet acts as a client for the Kube API Server, and as we already know by now, it has to identify itself
Certificate Parameters:
<CommonName>
: The node name prefixed with system:node:, e.g.system:node:node01
<Organization>
: system:nodes
Conclusion
Thank you for reading this guide, I hope it helps you understand a bit more on how to establish secure communication between the different components of a Kubernetes Cluster
Kindly let me know your feedback
See you in later guides