This article elaborates more about them in a real deployment.

Scenario

Assuming that we have the following requirement where all component has been running successfully (pods, services).

Standard Kubernetes Ingress Components

So we have 2 services:

  • Learning Management System (lms.hekademia.id)
  • Content Management System (cms.hekademia.id)

Nginx controller is set up to handle ingress connection from the outside world toward both services. It is required to have a secured connection between the user and the services so we need to set up TLS.

To set up TLS, we’ll need certificates signed by certificate authorities (CA). We can have them generated by third-party service providers, but in this scenario, we’ll utilize Letsencrypt.

There are at least 2 good reasons to use letsencrypt:

  • Convenient for Staging / Development. I am pretty sure most software developers find it common to see a complaint from applications such as internet browsers whenever they tried to load a web page from servers that use self-signed certs or other non-standard certs within SSL/TLS connection. Letsencrypt solve the issue easily since letsencrypt provide an easy and convenient way to get TLS certs signed by CA that is trusted by major internet browser worldwide
  • Letsencrypt provide automation to re-generate expired certs so domain owner doesn’t have to worry that their secure connection certs get expired

Cert Manager

Cert-manager is a native Kubernetes certificate management controller. It can help with issuing certificates from a variety of sources, such as Let’s Encrypt, HashiCorp Vault, Venafi, a simple signing key pair, or self-signed.

It will ensure certificates are valid and up to date, and attempt to renew certificates at a configured time before expiry.

Cert Manager

We use Kubernetes to enjoy its simplicity and power of managing orchestration to automate many things, including certificate management. That’s where the cert-manager plays the role.

We assume you already have a cert-manager installed in the k8s cluster. If not, then simply follow these guides.

Kubernetes
cert-manager runs within your Kubernetes cluster as a series of deployment resources. It utilizes…

Cert manager will help our cluster perform the following task automatically:

  • Compose CSR (Certificate Signing Request)
  • Send CSR to Certificate Authority (CA) — in this case, Letsencrypt
  • Retrieved the signed certificate

The signed official certificate will be used in TLS.

Certificate Issuer

To achieve the three main task above, the cert-manager need to perform pre-requisites action.

First, the cert-manager must assure the Letsencrypt server that it has control of the domain where we want to generate the certificate. It is mandatory, otherwise, everyone can claim that they have control over google.com or apple.com domain :)

Letsencrypt server will give a challenge to our cert-manager later. There are several ways of challenges such as using HTTP or DNS records. In this article, we’re going to use DNS records.

We can simply assume that Letsencrypt will ask,

Okey, cert-manager, prove to me that you can create the following entries to your acclaimed DNS record: dlxieixiekc983kdx.hekademia.id

So, in this case, we need to prepare some configuration where the cert-manager can communicate with our DNS server.

In this scenario, I use Cloudflare as my domain's DNS controller. Cloudflare provides a mechanism to add or remove DNS records automatically using token API.

Creating API tokens · Cloudflare API docs
To get started creating an API Token, log into the Cloudflare Dashboard External link icon Open external link and go to…

After creating the token, we can deploy the following manifest (change the token API with your own) to create a secret in our k8s cluster.

cloudflare-token.yml

Then, deploy the following manifest to set up Issuer (you can use ClusterIssuer or namespace Issuer, in this scenario I use Issuer).

issuer-letsencrypt.yml

The above simple manifest basically instructs the cert-manager to create an issuer object that will be registering itself to Letsencrypt using the email jhinkle@hekademia.id. The protocol used to establish communication is ACME.

The challenge will be answered by the cert-manager using the configuration in the “solvers” part, where we put configuration to Cloudflare using the previously saved secret (API token).

Whenever cert-manager creates Issuer, all it does is create the configuration. Certificate generation will be performed once the respective manifest is deployed.

Ingress + TLS Certificate

Since the Issuer object has been created, we can proceed with certificate generation. I will simply use the following ingress manifest where certificate generations are already included in annotations.

ingress-tls-web.yml

Let's update our architecture flow to visually demonstrate what actually happened after the deployment of the above manifest.

Kubernets Ingress + TLS Cert Generation

As we’ve explained before the cert-manager has created the Issuer object. Whenever ingress is deployed, it mentions specifically using the annotation feature to instruct the Issuer object (letsencrypt-issuer) to generate a certificate for domain lms.hekademia.id and cms.hekademia.id.

The Issuer will use its private key to create CSR, and send it to Letsencrypt. Letsencrypt will give a challenge to Issuer to add the particular domain to prove domain ownership authority, since Issuer is already configured to communicate with Cloudflare using its token API, Issuer can solve the challenge and prove to Letsencrypt.

Letsencrypt will then sign the CSR for domain lms.hekademia.id and cms.hekademia.id, and send the signed certificate back to the Issuer. The issuer will save the signed certificate in k8s secret (hekademia-id-tls).

Last, our cluster’s ingress controller will be using that certificate for securing communication towards both domains in hekademia.id (TLS).

You can see the result by retrieving some status using the following commands:

$ kubectl get certificate -n <namespaces>
NAME               READY   SECRET             AGE
hekademia-id-tls   True    hekademia-id-tls   24m

If things not running correctly, then you can analyze the order.

$ kubectl describe order -n <namespace>

You can see the full description of what’s going on (including the above-explained flows) in order.

...
...
Finalize URL:     https://acme-v02.api.letsencrypt.org/acme/finalize/106521584/6772467365State:            validURL:              https://acme-v02.api.letsencrypt.org/acme/order/106521584/6772467365Events:Type    Reason    Age   From          Message----    ------    ----  ----          -------Normal  Created   26m   cert-manager  Created Challenge resource "hekademia-id-tls-dhtl9-208972296-1297791550" for domain "lms.hekademia.id"Normal  Created   26m   cert-manager  Created Challenge resource "hekademia-id-tls-dhtl9-208972296-1130809808" for domain "cms.hekademia.id"Normal  Complete  25m   cert-manager  Order completed successfully

You will see the status as “valid” if things run correctly :)

And whenever we open the application through a secure connection (HTTPS) in the web browser, there should be no indication whatsoever related to warning of fake certificates since the certificate used by TLS is a valid one.

SSL/TLS Certificate as shown by internet browser

Kubernetes is a powerful orchestration framework. As you can see, several lines of configuration in the manifest can produce many tasks underneath. But ones need to be very careful since its simplicity is actually composed of so much complex stuff.

It is why we need to understand the basic technology behind Kubernetes, so whenever things not running as expected, we know where to start looking.

I hope this basic information can be useful for anyone who wants to set up TLS using Nginx ingress in k8s with letsencrypt and Cloudflare.