Security is still an open question in the Cloud Native world. The inherently dynamic environment with short-lived containers, dynamic orchestrators, and network encapsulation provides significant challenges to traditional network security tools and procedures.
In general, cloud security best practices fall into three categories: Authentication, Authorization, and Encryption. While Authentication and Authorization are still a moving target in cloud-native deployments, there’s no reason we shouldn’t be encrypting all of our network traffic, both internal and external.
In this multi-part series, you will learn how to enable HTTPS (SSL/TLS) for applications deployed in Kubernetes. First we’ll walk through manually deploying and using TLS certificates in containerized applications. Then we’ll level-up our Kubernetes infrastructure by creating an automated certificate generator leveraging Let’s Encrypt, a free and open Certificate Authority. Finally, we will use our automatically created certificates to secure four services: NGINX, an Ingress Controller, a Node.js application, and a Golang application.
Hope you enjoy and don't hesitate to contact me if you or your company needs expert advice.
- Part 1: Creating TLS certificates in Kubernetes
- Part 2: Automatic TLS Certificates with Let's Encrypt (Coming soon!)
- Part 3: Let's Encrypt Ingress (Coming soon!)
- Part 4: Let's Encrypt NGINX (Coming soon!)
- Part 5: Let's Encrypt Go (Coming soon!)
- Part 6: Let's Encrypt Node.js (Coming soon!)
Part 1: Creating TLS certificates in Kubernetes
For the sake of simplicity and repeatability, we'll be using self-signed certificates for Part 1. I loosely followed this guide from the great folks at Digital Ocean on how to create a self-signed certificate.
The TLDR; is:
$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt
This will create two files in our current directory, tls.key
and tls.crt
. With these two files, we can enable TLS encryption for our applications. But first, we need to load these files into our Kubernetes cluster.
Creating a Kubernetes Secret with your Certificate
The next step is to deploy this certificate to our Kubernetes cluster.
We're going to be leveraging Kubernetes Secrets to store the TLS key and certificate.
Create the secret with:
$ kubectl create secret generic tls-certificate --from-file=./tls.key --from-file=./tls.crt
secret "tls-certificate" created
This command creates a new Secret called tls-certificate
that has two data fields, one per file. We can see these data fields using kubectl describe
:
$ kubectl describe secret tls-certificate
Name: tls-certificate
Namespace: tls
Labels:
Annotations:
Type: Opaque
Data
tls.crt: 1598 bytes
tls.key: 1679 bytes
Note: the kubectl
CLI does not print out the raw data in the Secret. If you really need to verify the information, you can use kubectl get secret tls-certificate -o yaml
to get the base64 encoded TLS files.
OK! Our TLS certificate and key are now loaded into our Kubernetes cluster. Now it's time to use them!
Securing our application
For this demo, we're going to be using a simple Golang application that serves up a "Hello World" message, secured with TLS.
Here's our Golang app that reads tls.crt and tls.key from /secure/
:
// main.go
package main
import (
"crypto/tls"
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", func(w http.ResponseWriter, req http.Request) {
w.Write([]byte("Hello world.\n"))
})
cfg := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
PreferServerCipherSuites: true,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
},
}
srv := &http.Server{
Addr: ":443",
Handler: mux,
TLSConfig: cfg,
TLSNextProto: make(map[string]func(http.Server, *tls.Conn, http.Handler), 0),
}
log.Fatal(srv.ListenAndServeTLS("/secure/tls.crt", "/secure/tls.key"))
}
I've already built a Dockerfile from scratch
with this binary in it: rosskukulinski/secure-go-app
. We can deploy this application using a Kubernetes Deployment file:
deployment.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: secure-go-app
spec:
template:
metadata:
labels:
app: secure-go-app
spec:
containers:
- image: rosskukulinski/secure-go-app
imagePullPolicy: Always
name: secure-go-app
ports:
- containerPort: 443
protocol: TCP
volumeMounts:
- mountPath: /secure/
name: tls-secret
restartPolicy: Always
volumes:
- name: tls-secret
secret:
secretName: tls-certificate
Notice we're mounting our TLS Secret as a volume into our container in the /secure
directory.
We can create this deployment:
$ kubectl create -f deployment.yaml
deployment "secure-go-app" created
And assuming we've done everything correctly, one pod should be running:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
secure-go-app-3505491896-7t47r 1/1 Running 0 5s
If a Pod isn't running (or not showing up!), I recommend checking out the 10 most common reasons Kubernetes Deployments fail.
Testing our secure application
Now let's verify the Pod is correctly responding to HTTPS requests by port-forwarding the container port 443 to our localhost port 1443.
$ kubectl port-forward secure-go-app-3505491896-7t47r 1443:443
Forwarding from 127.0.0.1:1443 -> 443
Forwarding from [::1]:1443 -> 443
Now we can make a request with curl:
$ curl -k https://localhost:1443
Hello world.
Woo! We've secured the network traffic to our example application with TLS encryption.
Next Steps
We have successfully secured an example application running in Kubenernetes with a self-signed certificate. If your organization has purchased TLS certificates from a Certificate Authority, you can follow this procedure to deploy them to Kubernetes.
While purchasing certificates from Giant-Certificate-Authority certainly makes sense for e-commerce or customer-facing websites, it can quickly get expensive for supporting services and inter-cluster communication.
One of the recent exciting developments in the web security space is Let's Encrypt, a free, automated, and open Certificate Authority. In Part 2 (coming soon!), you'll learn how to automatically generate TLS certificates in Kubernetes using Let's Encrypt.