Table of Contents

Transport Layer Security (TLS) encrypts data sent over the Internet to ensure that eavesdroppers and hackers are unable to see what you transmit which is particularly useful for private and sensitive information such as passwords, credit card numbers, and personal correspondence.

CertificateSigningRequest

Kubernetes CertificateSigningRequest objects provide a mechanism to obtain x509 certificates by submitting a certificate signing request, and having it asynchronously approved and issued.

Kubelets use this API to obtain:

  • client certificates to authenticate to kube-apiserver (with the kubernetes.io/kube-apiserver-client-kubelet signerName).
  • serving certificates for TLS endpoints kube-apiserver can connect to securely (with the kubernetes.io/kubelet-serving signerName).

This API can be used to request client certificates to authenticate to kube-apiserver (with the kubernetes.io/kube-apiserver-client signerName), or to obtain certificates from custom non-Kubernetes signers.

Generate a Self-Signed Certificate

Create a private key for the certificate:

openssl genrsa -out myservice.key 2048

Generate a Certificate Signing Request (CSR) using the private key:

openssl req -new -key myservice.key -out myservice.csr -subj "/CN=myservice.default.svc.cluster.local"

Replace myservice.default.svc.cluster.local with the fully qualified domain name (FQDN) of your service.

Create a CSR YAML file, e.g., myservice-csr.yaml:

apiVersion: certificates.k8s.io/v1
kind: CertificateSigningRequest
metadata:
  name: myservice
spec:
  request: <base64-encoded-csr>
  signerName: kubernetes.io/kube-apiserver-client
  usages:
  - digital signature
  - key encipherment
  - client auth

Replace <base64-encoded-csr> with the Base64-encoded CSR:

cat myservice.csr | base64 | tr -d '\n'

Apply the CSR YAML file and approve the CSR:

kubectl apply -f myservice-csr.yaml
kubectl certificate approve myservice

Retrieve the issued certificate:

kubectl get csr myservice -o jsonpath='{.status.certificate}' | base64 --decode > myservice.crt

Create a Kubernetes secret containing the private key and the issued certificate:

kubectl create secret tls myservice-tls --cert=myservice.crt --key=myservice.key

That’s it. Our TLS certificate is ready.

Deploy Service and Use Self-Signed Certificate

Update our service deployment to use the secret for TLS termination. Modify our Kubernetes deployment YAML file to include the secret and the appropriate volume mounts

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myservice
spec:
  replicas: 1
    selector:
    matchLabels:
      app: myservice
  template:
    metadata:
      labels:
        app: myservice
    spec:
      containers:
      - name: myservice-container
        image: myservice-image
        ports:
        - containerPort: 8443
        volumeMounts:
        - name: myservice-tls
          mountPath: "/etc/tls"
          readOnly: true
      volumes:
      - name: myservice-tls
        secret:
          secretName: myservice-tls

Deploy the updated service:

kubectl apply -f myservice-deployment.yaml

Verification

Use the following command for verification:

echo | openssl s_client -showcerts -servername myservice.default.svc.cluster.local -connect myservice.default.svc.cluster.local:443

We should see the following output:

depth=0 CN = myservice.default.svc.cluster.local
verify error:num=20:unable to get local issuer certificate
verify return:1
depth=0 CN = myservice.default.svc.cluster.local
verify error:num=26:unsupported certificate purpose
verify return:1
depth=0 CN = myservice.default.svc.cluster.local
verify error:num=21:unable to verify the first certificate
verify return:1
write W BLOCK
---
Certificate chain
 0 s:/CN=myservice.default.svc.cluster.local
   i:/CN=kubernetes
-----BEGIN CERTIFICATE-----
MIIDJDCCAgygAwIBAgIRAJT0AGOQv8KhD6VUrbPMB/owDQYJKoZIhvcNAQELBQAw
...
98/yeCTMfkYsjgAUlkisaC1KkEg3ROsYbEkWowUqj8E2gdGd7AMtSQ==
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=myservice.default.svc.cluster.local
issuer=/CN=kubernetes
---
No client certificate CA names sent
Server Temp Key: ECDH, X25519, 253 bits
---
SSL handshake has read 1368 bytes and written 411 bytes
---
New, TLSv1/SSLv3, Cipher is AEAD-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.3
    Cipher    : AEAD-AES256-GCM-SHA384
    Session-ID:
    Session-ID-ctx:
    Master-Key:
    Start Time: 1679662991
    Timeout   : 7200 (sec)
    Verify return code: 21 (unable to verify the first certificate)
---
DONE

We can see that the issuer is Kubernetes. It was great. It was just right!

Conclusion

This article introduces how to use CertificateSigningRequest for our application to generate the self-signed certificates.

While the self-signed certificates should not be used in production, they provide an easy way to test the apps you deploy with Kubernetes.

References