Service exposed from a Kubernetes cluster should by encrypted with TLS. Learn how to get fully automatic, self-renewed Let’s Encrypt certificates.
Kubernetes is an industry standard based on security best practices. By default, all deployments are cluster-internal only, you need to explicitly expose them with a Kubernetes resource of type
Service. But when you open your application to public traffic, you should provide strong TLS encryption.
In this article, I show you the essential, easy to apply steps to expose services and automatically get self-renewed Let’s Encrypt certificates.
This article originally appeared at my blog.
Preparation: Install Nginx Ingress
K3S, the Kubernetes distribution that I’m using, uses the Traefik Ingress per default. We need to install the Nginx-Ingress manually. To do this, we will use the great helper tool arkade.
curl -sLS https://dl.get-arkade.dev | sudo sh
Then, we can install Nginx with a simple one liner.
> arkade install nginx-ingressRelease "nginx-ingress" has been upgraded. Happy Helming!
LAST DEPLOYED: Fri May 8 14:11:09 2020
TEST SUITE: None
The nginx-ingress controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
Deploy an Application
For demonstration purposes, let’s start a webserver that returns a simple message. We will use the following Kubernetes
- name: echo
args: ['--listen', ':5678', '--text', 'echo']
- containerPort: 5678
Let’s deploy this and check that the pods are created.
> kb apply -f deployment.yaml> kb get allNAME READY STATUS RESTARTS AGE
pod/echo-7b86d65bc8-6crzv 1/1 Running 0 9sNAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/echo 1/1 2 2 9s
In order to access the webserver, we will define a Service resource of type
ClusterIP. This means that all
echo pods will be accessible from within the cluster.
- port: 80
To make this service available from the outside, we need to route from the internet through the Nginx ingress to the services. For this, we need to define the following
- host: echo.admantium.com
spec part, you see that this rule applies when the request hostname is
echo.admantium.com. All requests will be forwarded to the service
echo on port
5678. For this to work, you need of course configure the DNS entry for this domain to point to your Kubernetes cluster.
Apply this rule, and then check its status with the
> kubectl apply -f echo-service.yml> kubectl describe ingress echoName: echo
Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
letsencrypt-staging terminates echo.admantium.com
Host Path Backends
---- ---- --------
Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging
Type Reason Age From Message
---- ------ ---- ---- -------
Normal UPDATE 2m25s (x4 over 159m) nginx-ingress-controller Ingress default/echo
Now you can already access the echo server. But it’s not TLS encrypted.
In Kubernetes, certificate management is a central responsibility that can be realized with the
cert-manager. Internally, this tool consists of Kubernetes resources like Pods, Services and Deployments. In your application, you configure to use the cert-manager as a provide for TLS certificates. Then, it will automatically issue certificates that are stored as secrets inside your cluster. It will also check and renew certificates automatically before they expire.
First of all, we will install the
arkade install cert-manager
Client: x86_64, Darwin...NAME: cert-manager
LAST DEPLOYED: Mon Apr 27 19:58:05 2020
TEST SUITE: None
cert-manager has been deployed successfully!
In order to issue TLS certificates, you need to decide which certificate issuer you want to use. In our case, we will use Let’s Encrypt. This issuer needs to be configured as a Kubernetes resource of type
ClusterIssuer. For Let’s Encrypt, there are two issuers: staging and production. It is essential that you use the staging issuer until the configuration completely works.
Apply this file, and then check the cert-managers log file to see that the cluster issuer is created correctly. This can take one minute or two.
kubectl logs -n cert-manager deploy/cert-manager -f
ClusterIssuer is successfully created, we can execute the last step.
Configuring Ingress Resource to use TLS
Ingress resource, we need to add two new configuration options. Inside
cert-manager.io/cluster-issuer. And inside
spec, add a
tls block, with the domain name and a
secretName which is the same as the
- host: echo.admantium.com
When we apply this ingress, we can follow the cert-managers logfiles to see the progress.
I0508 12:19:24.176712 1 pod.go:58] cert-manager/controller/challenges/http01/selfCheck/http01/ensurePod "level"=0 "msg"="found one existing HTTP01 solver pod" "dnsName"="echo.admantium.com" "related_resource_kind"="Pod" "related_resource_name"="cm-acme-http-solver-pmn4v" "related_resource_namespace"="default" "resource_kind"="Challenge" "resource_name"="letsencrypt-staging-1985468592-3302894823-3409218764" "resource_namespace"="default" "type"="http-01"
I0508 12:29:41.771005 1 acme.go:166] cert-manager/controller/certificaterequests-issuer-acme/sign "level"=0 "msg"="certificate issued" "related_resource_kind"="Order" "related_resource_name"="letsencrypt-staging-1985468592-3302894823" "related_resource_namespace"="default" "resource_kind"="CertificateRequest" "resource_name"="letsencrypt-staging-1985468592" "resource_namespace"="default"
The certificate is issued. Now you can access the service in a browser — and check its certificate.
With Kubernetes, you can automate the creation of TLS certificates. Once properly setup, the cert-manager takes care of creating certificates, checking their expiration date and re-creating new certificates. To apply certificates, you add an annotation and a TLS block to your deployment specification. That is all you need.