SSL is Easy: Enable HTTPS for your application on Kubernetes in 5 steps, and for free

Mayank Kapoor
7 min readAug 31, 2018

--

source: https://dunamisblog.com/what-is-a-ssl-certificate/

There are multiple reasons why setting up SSL/TLS/HTTPS (referred to as SSL in this article) is hard for application developers. In order to have SSL setup correctly, application developers need to:

  1. procure a “valid”/publicly trusted SSL certificate with the correct domain name. This used to involve paying a significant amount to an issuer like Comodo.
  2. generate something called a CSR (certificate signing request) and a private key file with all the right information. If this info is incorrect, SSL will not validate properly. The process to generate these items is not very intuitive and needs a “DevOps” engineer to help.
  3. work with DevOps to install the certificate “chain” and required artifacts like CSR and private key at the right location on the web server VM. The process/procedure to do this varies with different web servers (NGINX, Apache etc.) and OS. Already we’re talking significant complexity with different teams involved, needing good communication between teams.
  4. (maybe) read a 550-page book on deploying SSL/TLS as online documentation isn’t quite good enough yet².

And this is just the tip of the iceberg. People realized this is a problem and the tech industry has started building platforms such as www.letsencrypt.org and AWS certificate manager¹ to make this process cheaper/free, faster and less prone to errors. Now developers should be able to easily deploy SSL enabled websites. This article walks through one way of doing this on Kubernetes and using AWS’ Elastic Load Balancer (ELB). To follow through, you’ll need an AWS account, access to a kubernetes cluster running on AWS, and a domain you control on which to host your application.

I’m going to describe the following steps today to enable “A” grade SSL on your application:

  1. Setup NGINX Ingress Controller.
  2. Deploy a simple “cafe” application on Kubernetes. This demo application can be replaced by your own application.
  3. Get AWS certificate manager to issue a free wildcard (*.domain.com) SSL certificate.
  4. Create an AWS ELB with an application health check, assign the SSL certificate to the ELB and terminate SSL connections at the ELB.
  5. Validate your SSL configuration using SSLLabs.com’s free test.

Step 1: Setup NGINX Ingress Controller

Assuming helm is already installed and kubectl is configured correctly on your laptop, let’s setup nginx-ingress using helm package manager.

# kubectl get namespaces # Check if kubectl is working fine
NAME STATUS AGE
default Active 1d
kube-public Active 1d
kube-system Active 1d
# vi rbac-config.yaml # YAML for helm service account and role
apiVersion: v1
kind: ServiceAccount
metadata:
name: tiller
namespace: kube-system
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: tiller
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
subjects:
- kind: ServiceAccount
name: tiller
namespace: kube-system
# kubectl create -f rbac-config.yaml # Create helm service account for RBAC enabled kubernetes clusters
serviceaccount/tiller created
clusterrolebinding.rbac.authorization.k8s.io/tiller created
# helm init --service-account tiller # Helm install on kubernetes
Creating ...
Adding stable repo with URL: https://kubernetes-charts.storage.googleapis.com
Adding local repo with URL: http://127.0.0.1:8879/charts
$HELM_HOME has been configured at /home/ubuntu/snap/helm/common.
Tiller (the Helm server-side component) has been installed into your Kubernetes Cluster.Happy Helming!# helm install --name my-ingress-controller stable/nginx-ingress # Install the nginx-ingress controller
NOTES:
The nginx-ingress controller has been installed.
It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status by running 'kubectl --namespace default get services -o wide -w my-ingress-controller-nginx-ingress-controller'
...
# kubectl --namespace default get services -o wide my-ingress-controller-nginx-ingress-controller # This should show you the nginx-controller's external IP and NodePorts. Some k8s installations, like mine, don't support service type LoadBalancer and External IPs. In this case, we can use the NodePort mapped to the controllers HTTP port 80 highlighted below to access the nginx-ingress-controller.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
my-ingress-controller-nginx-ingress-controller LoadBalancer 10.108.54.6 <pending> 80:32268/TCP,443:31331/TCP 2m app=nginx-ingress,component=controller,release=my-ingress-controller

Confirm that the nginx-ingress controller is working by typing your kubernetes <HostIP>:<NodePortIP> into a browser or using curl:

$ curl 13.232.XYZ.ABC:32268
default backend - 404

Step 2: Deploy a simple “cafe” sample application which has to be SSL secured

Now that nginx-controller is working, let’s deploy the sample cafe application that is provided by the nginx team. You can replace this with our own application as well.

# git clone https://github.com/nginxinc/kubernetes-ingress.git # Clone nginxinc's ingress repo with the example application
Cloning into 'kubernetes-ingress'...
Resolving deltas: 100% (4896/4896), done.
Checking connectivity... done.
# ls kubernetes-ingress/examples/complete-example/ # This folder contains the cafe application deployment files
cafe-ingress.yaml cafe-secret.yaml cafe.yaml dashboard.png README.md
# cd kubernetes-ingress/examples/complete-example/ # cd into this folder to start creating the sample application# kubectl create -f cafe.yaml
deployment.extensions/coffee created
service/coffee-svc created
deployment.extensions/tea created
service/tea-svc created
# kubectl create -f cafe-secret.yaml
secret/cafe-secret created
# vi cafe-ingress.yaml # Replace the hostname with a domain you control, as we'll need to create A and CNAME records for this domain
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
name: cafe-ingress
spec:
tls:
- hosts:
- cafe.mayankkapoor.com
secretName: cafe-secret
rules:
- host: cafe.mayankkapoor.com
http:
paths:
- path: /tea
backend:
serviceName: tea-svc
servicePort: 80
- path: /coffee
backend:
serviceName: coffee-svc
servicePort: 80
# kubectl create -f cafe-ingress.yaml # Create the ingress routing rules
ingress.extensions/cafe-ingress created
# kubectl describe ing cafe-ingress
Name: cafe-ingress
Namespace: default
Address:
Default backend: default-http-backend:80 (<none>)
TLS:
cafe-secret terminates cafe.mayankkapoor.com
Rules:
Host Path Backends
---- ---- --------
cafe.mayankkapoor.com
/tea tea-svc:80 (<none>)
/coffee coffee-svc:80 (<none>)
Annotations:
Events: <none>

To test the application, curl the coffee and the tea services. /coffee should route to the coffee page, and /tea should route to the tea page. We’ll use curl's -H option to send the Host header that ingress needs to route our request properly:

$ curl -H "Host: cafe.mayankkapoor.com" http://13.232.XYZ.ABC:32268/coffee
Server address: 192.168.0.28:80
Server name: coffee-7dbb5795f6-7v7rb
Date: 31/Aug/2018:00:56:14 +0000
URI: /coffee
Request ID: 7f25e75da2e4387ea79dc2cd25e3fb9f
$ curl -H "Host: cafe.mayankkapoor.com" http://13.232.XYZ.ABC:32268/tea
Server address: 192.168.0.25:80
Server name: tea-7d57856c44-2g4ld
Date: 31/Aug/2018:00:56:21 +0000
URI: /tea
Request ID: 36d2c1970fc4deb91386aefaf0e1aa6c

Step 3: Create a wildcard SSL certificate in AWS certificate manager

  1. Log into AWS certificate manager and click “Request a certificate”.
  2. Request a wildcard certificate *.mayankkapoor.com which can be used on multiple domains, e.g., www.mayankkapoor.com, cafe.mayankkapoor.com etc.
  3. Choose DNS Validation on the “Select validation method” page. DNS validation is the fastway way to validate that you own the domain for which you’re requesting the SSL certificates.
  4. Click “confirm and request”. On the Validation page, click on the arrow next to the domain to see the CNAME entry we need to create. Copy the CNAME name and value.
  5. Log into your domain registrar and create this CNAME entry with a TTL of 1 minute.

You should see status as “Issued” on the main Certificates page of AWS certificate manager after 5-10 minutes if the CNAME entry was created properly.

Step 4: Create the ELB and terminate SSL at the ELB

We want to terminate SSL at the ELB itself, as internal traffic from the ELB to the application doesn’t always need to be SSL encrypted. This also alleviates the web servers of the extra CPU cycles needed to decrypt SSL traffic.

  1. Log into EC2 and click Load Balancers on the left.
  2. Click Create Load Balancer and choose an “Application Load Balancer”.
  3. Enter a name for the ELB, choose scheme as “internet-facing” and add HTTPS protocol under listeners on port 443. Select appropriate availability zones where your kubernetes cluster/application resides.
  4. In “Configure Security Settings”, choose your freshly issues wildcard certificate, and choose one of the latest security policies. The later the policy, the higher your SSL rating will be. However, clients which talk to your application need to support the latest protocols as well.
  5. In “Configure Security Groups”, create a new security group and allow HTTP/80 and HTTPS/443 from any source IP.
  6. In “Configure Routing”, enter a name for the new target group, choose protocol HTTP, enter the port on which your nginx-controller listens (in my case, my NodePort 32268), and choose target type“ip”.
  7. nginx-ingress has a very useful default health check configured at /healthz which you can use to check the health of your nginx-ingress-controller. This should suffice for the ELB health check requirement. Any further application health check logic should be defined inside Kubernetes.
  8. In “Register Targets”, add the private IP of your application. In my case, I gave the private IP of my kubernetes node.
  9. Click Review, then Create. Wait for the ELB state to show “active”. Note down the ELB DNS name, e.g., mayank-demo-1234.ap-south-1.elb.amazonaws.com. You’ll need this to create the CNAME record to redirect traffic from cafe.mayankkapoor.com to this ELB.
  10. Check if your target IPs are healthy under EC2 “Target groups”>Select the target group>Targets. Status of your registered targets should be “healthy”. If not, you’ll need to debug this issue before proceeding further.
  11. Log into your domain registrar and create another CNAME entry with host cafe.mayankkapoor.com with target as the ELB DNS name, with a TTL of 5 min.

Step 5: Validate your SSL configuration.

Home stretch.

  1. Test if your application is reachable on https://cafe.mayankkapoor.com/coffee. It should be, and you should be able to see the SSL certificate being served by clicking on the lock icon to the left of the url.
  2. Go to https://www.ssllabs.com/ssltest/ and enter your domain url cafe.mayankkapoor.com to start the SSL validation. The two server IPs shown are the ELB IPs.
Application is reachable and SSL is enabled
Grade A score for our setup

That’s it. Enjoy the green HTTPS badge on your application.

Footnotes

  • ¹AWS certificate manager will issue free wildcard or domain specific certificates as long as you use AWS resources, so technically you still need to pay for EC2 resources like the ELB and your kubernetes cluster.
  • ²Interview with SSLLabs.com founder where he says “lot of online documentation is wrong
  • A non-AWS centric version of this article is in the works.

--

--

Mayank Kapoor

IIT Engineer, MIT MBA. Ex-Amazon, Apple. Currently at Jio.