Secure your Kubernetes application with HTTPS
Tags: kubernetes, ssl, https, nginx
Published: 2019-12-28 00:13:02

Hope you were having fun with [my Kubernetes tutorial](https://www.martinpham.com/2019/12/07/having-fun-with-kubernetes-1/)! 

Today, I've got [a new comment from Thorsten Hirsch](https://dev.to/thorstenhirsch/comment/jcn2), asking about TLS certificate. Well,  @thorstenhirsch, I'm interested as with this thing as well :), and it was the first thing I had to check while I was learning Kubernetes. So I'm gonna share with you guys how did I solve this problem.

Requirement
==
We'd like to add a TLS certificate to our application, which we built [here](https://www.martinpham.com/2019/12/14/having-fun-with-kubernetes-final/)

![](https://thepracticaldev.s3.amazonaws.com/i/elbmq2f23mih7q3dbdpa.png)

Problem
==
*What? problem? there is no problem! we were using `certbot` thousands times before. Just install it, run a magical script to get a free [Let's Encrypt](https://letsencrypt.org/), then config it with nginx.*
If you are thinking like this, wait a second and think about this:
- Your application lives in read-only containers, managed by Pods. And Pods are added/removed dynamically, by your configurations. And you will need to give nginx certificates when you start it.
- You can generate certificates, then pack it within the image, and deloy it. It's a **terrible solution**. Because you'd need to deploy every time the certificates are gonna expired.
- You can store it in a shared volume, and config nginx to take certificates from it. It's another **terrible solution**. Because you'd still need to manage the certificates yourself (even when you want to run it via a cron?)
- ...

Solution
==
Let's try to think about another solution (*Spoiler*: It's **awesome**):
- We will keep our infrastructure as before, with minimum modifications. No changes to application, no changes to nginx, no changes to deployment.
- TLS certificate will be monitored and renewed automatically.

First of all, I'd like to introduce you a new guy - [Ingress](https://kubernetes.io/docs/concepts/services-networking/ingress/). 

![Ingress](https://thepracticaldev.s3.amazonaws.com/i/9ra8tz1hfs7ohccallwv.png)

Basically, Ingress is like a Router, which takes the incoming traffic then passes it to the corresponding Service, with the help from [Ingress Controllers](https://kubernetes.io/docs/concepts/services-networking/ingress-controllers/).

Simple example: You have 3 services: 
- `apple-service` (which runs [apple.com](apple.com) website, selling fruits)
- `pineapple-service` (another website - pineapple.com, selling phone, tablet, computer,...)
- `pineapple-cloud-service` (offering some cloud services for Pineapple's clients)

What you want is:
- apple.com will be pointed to `apple-service`
- pineapple.com will be pointed to `pineapple-service`
- pineapple.com/cloud will be pointed to `pineapple-cloud-service`

Ingress comes to help you here: Just need to create an Ingress object, with routing rules you want, then point those domains into the Ingress IP. And everything will be handled correctly!

Preparing stuffs
===
Before everything, we need to prepare stuffs for our Ingress configuration.

***Install ingress-nginx***
[ingress-nginx](https://github.com/kubernetes/ingress-nginx) is a Ingress Controller, which helps Ingress to route the traffic easily.

```
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/mandatory.yaml
```

```
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/static/provider/cloud-generic.yaml
```

For this Lab, we'd like to expose the Ingress with the Kube master's IP (`192.168.1.33` as we configured before), so let's edit this controller a bit:

```
kubectl edit svc/ingress-nginx -n ingress-nginx
```

```
spec:
  externalIPs:
  - 192.168.1.33
```
***Install cert-manager***
[cert-manager](https://cert-manager.io/docs/) is a Kubernetes controller, which helps you to manage certificates without pain. We're gonna use it to request & renew Let's encrypt TLS certificate for our application.

```
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml
```

Check all the pods are running correctly
```
kubectl get pods --namespace cert-manager
```

Setup Ingress & TLS
===

Back to our infrastructure: Before we were using a LoadBalancer as the start point, now we can just change it to stay behind in the cluster, and add an Ingress as the start point.

**Before**
```
(Users) ---> Service ---> Pods
```

**Now**
```
(Users) ---> Ingress ---> Service ---> Pods
```

We're gonna edit a bit the service file `service-loadbalancer.yml` (We've created it [before](https://www.martinpham.com/2019/12/14/having-fun-with-kubernetes-final/))
```
apiVersion: v1
kind: Service
metadata:
  name: service-loadbalancer
spec:
  selector:
    name: templated-pod

  type: ClusterIP
  ports:  
  - name: http
    nodePort: null
    port: 80
    targetPort: 80
    protocol: TCP
    
  # type: LoadBalancer
  # ports:
  #   - port: 80
  #     targetPort: 80
  # externalIPs:
  #   - 192.168.1.33
```

Apply it
```
kubectl apply -f service-loadbalancer.yml
```
***Create a ClusterIssuer***
Create new file `cert_issuer.yml`
```
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod-site
  namespace: cert-manager
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: YOUR-EMAIL@HERE.TLD
    privateKeySecretRef:
      name: letsencrypt-prod-site
    solvers:
    - http01:
        ingress:
          class: nginx

```

Apply it
```
kubectl apply -f cert_issuer.yml
```

***Create an Ingress***
Here comes our Rockstar tonight: `ingress.yml`

```
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: ingress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    cert-manager.io/cluster-issuer: "letsencrypt-prod-site"

spec:
  tls:
  - hosts:
    - YOUR-DOMAIN-HERE.TLD
    secretName: site-tls

  rules:
  - host: YOUR-DOMAIN-HERE.TLD
    http:
      paths:
      - path: /
        backend:
          serviceName: service-loadbalancer
          servicePort: 80
```

Nothing special:
- Line 12: Define your hostname which will run under this Ingress
- Line 16-22: Define a rule, to tell this Ingress: When user browses `YOUR-DOMAIN-HERE.TLD`, under path `/`, you'd like to pass him to the service `service-loadbalancer` on port `80` (defined in `service-loadbalancer.yml`)

*Remember with this Lab, you must point YOUR-DOMAIN-HERE.TLD to your IP address, then forward all traffic on port `80` and `443` to the kube-master IP `192.168.1.33`*

Show time:
```
kubectl apply -f ingress.yml
```

*Note: requesting/renewing a new certificate from Let's encrypt could take some minutes. You can monitor here: *
```
kubectl describe certificate site-tls
```


Now you can try to browser https://YOUR-DOMAIN-HERE.TLD/ on your favorite web browser, and open a *prosecco* :)

--- 

I've updated the repository [https://gitlab.com/martinpham/kubernetes-fun](https://gitlab.com/martinpham/kubernetes-fun), so you can take all the files we were talking about.