July 31, 20230 mins read
As cloud technology continues to evolve, the demand for Kubernetes is skyrocketing. As a result, security has become a top priority for developers looking to protect their application data. That's where Transport Layer Security (TLS) comes into play. TLS is essential for ensuring a secure connection between your applications and the internet.
TLS leverages asymmetric and symmetric cryptographies to keep your data secure in transit and at rest. The TLS certificate used in Kubernetes is typically a public key certificate issued by a trusted certificate authority (CA). This certificate contains information about the server's identity, the public key used for encryption, and the digital signature of the CA that issued the certificate.
This article will guide you through the implementation of an TLS connection for your applications on a Kubernetes cluster, providing you with a comprehensive understanding of TLS and its importance in securing your applications and data.
How TLS is implemented for Kubernetes workloads
Kubernetes does not provide a default implementation of TLS encryption for the communication between the client and the network component. This means that requests are served through HTTP, not HTTPS. However, you can easily configure your Kubernetes app to use HTTPS for secure communication using TLS.
Historically, TLS (and its predecessor, SSL) was implemented between systems and users that required secure communications. As distributed systems have proliferated along with a massive reduction in the cost and preformance impact of implementing TLS — not to mention ever-growing privacy concerns — most experts now recommend all traffic be encryted. Some common use cases for network encryption include the following:
Payment systems: If your application deals with payment data, such as bank accounts and credit cards, you have to implement TLS. This is a regulatory requirement and is necessary to ensure user data is safe in transit. Encrypting payment data also instills confidence in your customers and ensures they feel secure when using your services.
End-to-end data encryption with a service mesh: Using an end-to-end data encryption mechanism with a service mesh like Istio, TLS can secure communication between different microservices within a Kubernetes cluster. This is a popular approach for modern, distributed microservice architectures.
IoT and edge computing: Kubernetes applications interacting with IoT devices or edge computing systems should implement TLS to secure data transmission between devices and the cloud, preventing unauthorized access to data generated by IoT devices.
Content delivery networks (CDNs): If your applications use CDNs for caching and delivering content, implementing TLS can help you ensure secure transmission of content and prevent data tampering or unauthorized access.
Implementing a TLS configuration on Kubernetes
In the following sections, you'll implement TLS for a simple Kubernetes application using a Node.js project that mocks a payment processing application. The application code and necessary Kubernetes configuration are available in this GitHub repository.
To follow along with this tutorial, you need the following:
A container runtime engine: For this article, we will assume you have Docker installed and configured on your workstation.
A Kubernetes distribution: You need to install a Kubernetes distribution to create the Kubernetes cluster and other necessary resources, such as deployments and services. This tutorial uses kind (v0.18.0), but you can use any other Kubernetes distribution, including minikube or K3s.
kubectl: This command line interface is used to communicate with your Kubernetes cluster and issue commands. This tutorial uses version 1.26.
mkcert: This is used to obtain a trusted TLS certificate with a custom domain name for your development machine. You can install
mkcerton your development machine following the official instructions.
The following sections will walk you through implementing TLS for a demo payment application deployed to a local Kubernetes cluster. You initially deploy the app to your kind cluster and verify that it uses HTTP instead of HTTPS. Then, you’ll generate a self-signed TLS certificate and configure your payment app to use that. Finally, you’ll create a trusted TLS certificate using
mkcert and verify that your Kubernetes app can be accessed over HTTPS.
Deploy the payment app to Kubernetes
To start the process of implementing TLS in your Kubernetes-based sample application, you need to clone the application to your local machine by running the following command:
git clone https://github.com/snyk-snippets/tls-in-k8s.git
Then change into the project directory using this command:
Once you're in the project directory, you can deploy the application to your Kubernetes cluster.
Use the following command to create your kind cluster:
kind create cluster --config=config.yaml
config.yaml file binds a couple of ports on the kind nodes to your localhost, this is nessesary on Docker Desktop instalations as the kind containers are actually running inside a virtual machine (VM). If you are running on a Linux workstation with docker natively installed (no Docker Desktop VM), you can omit the
--config option here.
Next, we will build the container image and load it into our kind cluster.
1docker build -t demo-payment-app . 2kind load docker-image demo-payment-app
Note: optionally, you could push that image to a registry instead of using
kind load, if you do so, you will also need to edit the deployment.yaml file (used below) with your registry information and update the
imagePullPolicy to “Always” so it will pull that image down.
Once the cluster is created, deploy the application to your cluster with these commands:
1kubectl apply -f deployment.yaml 2kubectl apply -f service.yaml
Wait a few minutes for Kubernetes to create the pods and other necessary resources. You can check the status of your resources using these commands:
1kubectl get deployments 2kubectl get pods
Your demo project exposes the payment application using a
nodePort service on port
30080. When you define a Kubernetes service without specifying the type field, Kubernetes assigns the default service type —
However, the IP address exposed by
ClusterIP is only accessible from within the cluster's network. This means you can't view the application running in your cluster using an external client like your web browser. In production, you might want to use a
LoadBalancer service so your cloud provider can provide an external load balancer with a publicly accessible IP address.
Use these commands to see the services for this demo application:
kubectl get services
Depending on your runtime environment, you can access the demo application in one of two ways:
VM based (i.e. Docker Desktop):
To access the demo application from your browser, you will use the kind port binding set up in the
config.yaml file and simply use
Non-VM based (i.e. Linux w/out Docker Desktop):
To access the demo application from your browser, you need to find the IP address exposed by the
nodePort with the following command:
kubectl get nodes -o wide
This should display the
INTERNAL-IP of your Kubernetes control plane (ie
172.18.0.2). Now, you can access the payment application using the following:
Make sure to replace the IP address with the actual
INTERNAL-IP of your Kubernetes control plane or
localhost per above directions.
Opening your browser to the above IP and port brings up the Payment Page where you can enter the card information. Because this is a mock application, there's no actual processing behind this page and the data isn't saved anywhere:
Depending on your browser, you should see some kind of indication that the page is Not secure near your URL bar, indicating that this application is being served through HTTP, not HTTPS. You can verify this by clicking the prompt:
Implement a self-signed TLS certificate for your app
Now, implement TLS for the sample Kubernetes application. Create a self-signed certificate and private key:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=my-demo-app.local"
Then create a Kubernetes Secret containing the certificate and private key:
kubectl create secret tls my-demo-app-tls --key tls.key --cert tls.crt
Now, you need to install the Nginx Ingress Controller so that it can redirect incoming requests to your payment app to use HTTPS. Since you've exposed the app using
nodePort, you need to install the Ingress using a custom value file that specifies the service type to
Create a file called
ingress-values.yaml with the following content:
1controller: 2 service: 3 type: NodePort 4 nodePorts: 5 http: 32080 6 https: 32443
Then install the Nginx Ingress Controller using Helm and the custom values file:
1helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx 2helm repo update 3helm install ingress-nginx ingress-nginx/ingress-nginx -f ingress-values.yaml
ingress.yaml file with the following content:
1apiVersion: networking.k8s.io/v1 2kind: Ingress 3metadata: 4 name: demo-payment-app-ingress 5 annotations: 6 kubernetes.io/ingress.class: "nginx" 7spec: 8 tls: 9 - hosts: 10 - my-demo-app.local 11 secretName: my-demo-app-tls 12 rules: 13 - host: my-demo-app.local 14 http: 15 paths: 16 - path: / 17 pathType: Prefix 18 backend: 19 service: 20 name: demo-payment-app 21 port: 22 number: 80
And apply this manifest file by using the following command:
kubectl apply -f ingress.yaml
/etc/hosts file with the following entry:
Make sure you replace
<control_plane_IP> with 127.0.0.1 if you are running Docker Desktop, or the IP address you retrieved from running
kubectl get nodes -o wide if not.
Now you can access the payment app using HTTPS by going to
https://my-demo-app.local:32443 from your browser. When you visit the app for the first time, your browser should display a warning page, allowing you to accept the risk and proceed. After you do this, you can access the site with HTTPS:
Your browser displays this error because the TLS certificate is self-signed. This is evident from the fact that the HTTPS part of the URL is crossed out.
Implement a trusted TLS certificate for your app
To get around this, you can use
mkcert to create a trusted certificate for your development environment. To do so, create a local CA and generate a trusted certificate for your local domain:
1mkcert -install 2mkcert my-demo-app.local
Delete the previous Secret and create a new one for
1kubectl delete secret my-demo-app-tls 2kubectl create secret tls my-demo-app-tls --key 3my-demo-app.local-key.pem --cert my-demo-app.local.pem
Finally, redeploy the Ingress resource by running the following:
1kubectl delete ingress demo-payment-app-ingress 2kubectl apply -f ingress.yaml
Verify the TLS for your Kubernetes application
At this point, you should be able to visit your payment app with a trusted TLS certificate. Restart the browser, then go to https://my-demo-app.local:32443 so that the new certificate can work. You should see that there's no warning this time, and the HTTPS part of the URL isn't crossed out, just like normal TLS-secured websites:
Click on the lock icon in the URL bar to verify that the TLS certificate is trusted by your browser and that the connection is secure:
You can find the updated code examples on the
solution branch of our GitHub project repository.
When working with sensitive data, TLS provides you with the security you need. However, implementing TLS can be challenging if you don't know what to do.
In this tutorial, you learned how to implement TLS for an application deployed to a local Kubernetes cluster. You can also use these principles to implement TLS for applications hosted on-premise or in a cloud provider.
When implementing TLS for a production system, you should consider using a public certificate from a trusted CA, such as Let's Encrypt. While this requires that you have access to your site domain, you can implement additional configurations, such as using a load balancer service to expose your application or managing multiple certificates through a certificate manager like cert-manager. The advantages of exposing your application through a load balancer include improved availability, scalability, and resilience. And using a certificate manager makes the provisioning and management of your cluster certificates effortless in the future.