Creating TLS Certificates for K8s components with OpenSSL

Creating TLS Certificates for K8s components with OpenSSL

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

comm-map-1.png

The procedure for creating a Certificate

  1. First: Generate a Private Key

    <PrivateKey>: The name of the key

    openssl genrsa -out <PrivateKey>.key 2048
    
  2. 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 sign

    openssl req \
        -new \
        -key <PrivateKey>.key \
        -subj "/CN=<CommonName>/O=<Organization>" \
        -out <CertificateSigningRequest>.csr
    
  3. Third: Sign the CSR Certificates

    • Self-signing the CSR

      <CertificateAuthorityPrivateKey>: The private key of the CA <SignedCertificate>: The signed certificate

      openssl x509 \
          -req \
          -in <CertificateSigningRequest>.csr \
          -signkey <CertificateAuthorityPrivateKey>.key \
          -out <SignedCertificate>.crt
      

      Where <CertificateAuthorityPrivateKey>.key and <PrivateKey>.key are the same

    • Signing 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 CSR

      openssl x509 \
          -req \
          -in <CertificateSigningRequest>.csr \
          -CA <CACertificate>.crt \
          -CAkey <CAPrivateKey>.key \
          -out <SignedCertificate>.crt
      
  4. 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

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