It's that time of the day... You sit down at your computer to access your application you're running on Kubernetes. You navigate to your site, and what's this!? Someone has defaced it! Oh no, looks like you didn't have your ingress secured and someone has sniffed all your traffic and gotten your admin password. This of course, is what could happen, but we're smart SysAdmins - we're going to secure our ingress controllers using Let's Encrypt.

But what is Let's Encrypt?

If you've read my Article on TLS (which you should), you know that securing a website is important because it allows each party to trust that they are the only ones participating in their conversation. A few years ago, the ability to provide encryption on a website was hampered by the fact all Certificate Authorities (CAs) issued SSL Certificates for cash. This meant that for most hobbyists or smaller websites, securing their stuff was expensive. Enter Let's Encrypt, a service run by the non-profit Internet Security Research Group. Let's Encrypt effectively has democratised access to secure communications by maintaining a CA that issues Certificates for free. That's right, free as in free beer. This means that you can setup TLS on your website for free, and anyone who access your site does so with the knowledge and comfort that your communication is secure. A vitally important, yet criminally underrated privilege that a lot of people take for granted.

Building the Door

This process for configuring Let's Encrypt is relatively straight forward thanks to cert-manager, a project by Jetstack. We're going to use cert-manager and Digital Ocean DNS to provision our certificates. Let's get started by jumping into kubectl and installing our components.

# Creates Namespace
kubectl create namespace cert-manager

# Installs Custom Resource Definitions and the cert-manager Services
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v0.12.0/cert-manager.yaml

This setups up the basic pods and configuration required to run cert-manager. The main configuration items we need to take care of, is setting up our Digital Ocean API Key, and creating our Cluster Issuers. Before we creating our Digital Ocean Secret, we need to go to the Digital Ocean Control Panel and Generate an API Key. Once this is done, let's create the Secret:

# dns_secret.yaml
apiVersion: v1
kind: Secret
metadata:
  name: digitalocean-dns
  namespace: cert-manger-issuer
data:
  access-token: <DIGITAL_OCEAN_API_KEY>
dns_secret.yaml
kubectl apply -f dns_secret.yaml

Now we've got all our configuration in place, we just need to tell cert-manager how to issue certificates for our Cluster. We do this using ClusterIssuers which can issue Certificates across all Ingress in our Cluster. The cluster issuer consists of two pieces of configuration: your email address (for notifications) and solver configuration (so that Let's Encrypt can prove you own your domain). With these in place, we can get to the part where we actually secure our Ingress!

# production_do_dns_issuer.yaml
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
  name: do-issuer-production
  namespace: cert-manger-issuer
spec:
  acme:
    # The ACME server URL
    server: https://acme-v02.api.letsencrypt.org/directory
    # Email address used for ACME registration
    email: <YOUR_EMAIL_ADDRESS>
    # Name of a secret used to store the ACME account private key
    privateKeySecretRef:
      name: letsencrypt-production
    # Enable the DNS-01 challenge provider
    solvers:
    - dns01:
        digitalocean:
          tokenSecretRef:
            name: digitalocean-dns
            key: access-token
production_do_dns_issuer.yaml
kubectl apply -f production_do_dns_issuer.yaml

Installing the Lock

We're finally on the home stretch! The final step is to apply our TLS configuration to our Ingress. This involves:

  1. Adding a cert-manager.io/cluster-issuer annotation which tells cert-manager that we want a Certificate issued by the listed ClusterIssuer.
  2. Add tls block under the spec that lists the host names we want the Certificate to provide TLS for, as well as the name of the Secret where the Certificate should be stored.
# production_ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
	[...]
	annotations:
    	# Add our annotation
		cert-manager.io/cluster-issuer: letsencrypt-production
spec:
  # Add our host and Secret configuration
  tls:
  - hosts:
    - www.example.com
    - example.com
    secretName: wp-example-com-tls-production
[...]
kubectl apply -f production_ingress.yaml

Once this changes have been applied to the ingress, the following will occur:

  1. a TXT record will be placed into your DNS Zone by the dns01 solver.
  2. a secret will be created in the same namespace as the ingress with the name listed at spec > tls > secretName.

Within about five minutes, your Certificate will be issued and if you refresh your site it will now be secured.

See, Totally Easy!

As you can see, it is very easy to setup TLS on our Ingress on Kubernetes. No one could possibly have any excuse to let their own and their customers private communications be snooped on by third party actors. I love TLS and this is probably my favourite article of the series. Security is really important, and it is easy to forget that given how ubiquitous and easy it is to use in our modern day. However, we can't let our guard down because threats and bad actors take a high baseline of security into account. Don't be easy pickings.

If you liked this article, consider giving Let's Encrypt a donation, or you could just follow me on Twitter @stophammotime. Thanks for reading, and see you for the next installment where we setup backups on our Cluster.