Free SSL Certificates With Let’s Encrypt for Grafana & Prometheus-Operator Helm Charts

Kevin Coakley
The Startup
Published in
5 min readJul 20, 2020
Let’s Encrypt, Grafana and Kubernetes Logos

Grafana is the most popular web-based tool for creating dashboard and alerts. Tools like Kubernetes and Helm have made it possible to deploy Grafana with only a few commands if have the existing infrastructure for launching Docker containers. However, the documentation and tutorials for setting up Grafana almost always skip over how to secure the web-based front end using SSL encryption.

Before Let’s Encrypt, it was tedious and expensive to add SSL encryption to a website. Let’s Encrypt’s APIs have made it simple (automated) and free to deploy SSL encryption, so there is no reason not to SSL encrypt your web-based applications, even if you are just in the testing phase.

In this post, I will show you how to setup Free SSL certificates with automated renewals using cert-manager and Let’s Encrypt for Grafana deployed on Kubernetes using Helm (version 3).

This example will use the NGINX Ingress Controller for the Kubernetes Ingress resource. I will not be going into very much detail in this post on how to configure the NGINX Ingress Controller, you can find those details here. This deploying SSL certificates with cert-manager doesn’t require any specific configuration for the NGINX Ingress Controller, so we can just use the default Helm Chart values.

First, create namespace for the deploying nginx-ingress Helm Chart:

kubectl create namespace nginx-ingress

Then install the nginx-ingress Helm Chart:

helm install nginx-ingress stable/nginx-ingress --namespace nginx-ingress

If everything was done correctly, when you get the services for the nginx-ingress namespace, you should see the EXTERNAL-IP address listed for the nginx-ingess-contoller service.

$ kubectl -n nginx-ingress get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-ingress-controller LoadBalancer 10.233.51.219 XXX.XXX.XXX.XXX 80:30816/TCP,443:31905/TCP 49s
nginx-ingress-default-backend ClusterIP 10.233.27.227 <none> 80/TCP 49s

I highly recommend creating a hostname for Grafana before installing any additional Helm Charts. See your domain registrar’s documentation for how to do this. In this post I will use pretend that I own the example.com domain name and that I created the grafana.example.com hostname and mapped it to the EXTERNAL-IP address from the above step. A hostname for the SSL certificate will be necessary when configuring the prometheus-operator Helm Chart.

After Nginx Ingress is running, we need to install cert-manager in order to issue Let’s Encrypt SSL certificates. Click here for full cert-manager install instructions. The following instructions will cover installing cert-manager version 0.15.2 with the Let’s Encrypt ClusterIssuer.

First, create a Kubernetes namespace for deploying the cert-manager Helm Chart:

kubectl create namespace cert-manager

Add the cert-manager Helm repository:

helm repo add jetstack https://charts.jetstack.io
helm repo update

Install the cert-manager Helm Chart with the default values:

helm install cert-manager jetstack/cert-manager --namespace cert-manager --version v0.15.2  --set installCRDs=true

Verify that cert-manager has started and the status of the pods is Running:

$ kubectl -n cert-manager get po
NAME READY STATUS RESTARTS AGE
cert-manager-749df5b4f8-r5tqm 1/1 Running 0 49s
cert-manager-cainjector-67b7c65dff-t6hmj 1/1 Running 0 49s
cert-manager-webhook-7d5d8f856b-rccxp 1/1 Running 0 49s

Once cert-manager is running, we need to create the ClusterIssuer resource for Let’s Encrypt. Create a file named cluster-issuer-prod.yaml and add the following, remember to replace your-email@addresss.com with your E-mail address:

---
apiVersion: cert-manager.io/v1alpha2
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
# You must replace this email address with your own.
# Let's Encrypt will use this to contact you about expiring
# certificates, and issues related to your account.
email: your-email@address.com
server: https://acme-v02.api.letsencrypt.org/directory
privateKeySecretRef:
# Secret resource used to store the account's private key.
name: letsencrypt-prod
# Add a single challenge solver, HTTP01 using nginx
solvers:
- http01:
ingress:
class: nginx

If you are testing you may want to use the staging server https://acme-staging-v02.api.letsencrypt.org/directory instead of the production server so you don’t run into any of the certificate request limitations when you are ready for production. Let’s Encrypt’s staging server doesn’t have the same API limitations as the production server, but it doesn’t issue valid SSL certificates.

Finally, apply the Let’s Encrypt ClusterIssuer:

kubectl apply -f cluster-issuer-prod.yaml

Now cert-manager is ready to start issuing Let’s Encrypt SSL certificates for any NGINX Ingress Controller backed ingress resources on your Kubernetes Cluster.

Now we will install the Grafana or Prometheus-Operator Helm Chart. This example will install the Prometheus-Operator Helm Chart, but the values.yml file for both the Grafana portions are the same.

Again the first step is to create a Kubernetes namespace for deploying the prometheus-operator Helm Chart:

kubectl create namespace prom

Now we need to configure the ingress values for Grafana in the Helm Chart’s values.yml. The complete configuration options are available on the prometheus-operator Helm Chart GitHub repository.

For the annotations, we want to specify that nginx is used for the ingress and that letsencrypt-prod is used for the cluster-issuer. Then we want to specify the host to use for the ingress and the tls host (SSL certificate common name), in most cases they will be the same. Below is an example of the grafana portion of my values.yml for the prometheus-operator Helm Chart:

## Using default values from https://github.com/helm/charts/blob/master/stable/grafana/values.yaml
##
grafana:
enabled: true

ingress:
enabled: true
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: "letsencrypt-prod"
hosts:
- grafana.example.com
tls:
- hosts:
- grafana.example.com
secretName: grafana-tls

We can install the prometheus-operator Helm Chart once the values.yml file has been configured:

helm install prom stable/prometheus-operator -f values.yaml --namespace prom

Verify the prom-grafana pod is running (it make take a few minutes to get running):

$ kubectl -n prom get po
NAME READY STATUS RESTARTS AGE
prom-grafana-798b7b89bf-rnbpt 2/2 Running 0 10s
prom-kube-state-metrics-568dc84666-z5vm6 1/1 Running 0 10s
prom-prometheus-node-exporter-88h4k 1/1 Running 0 10s
prom-prometheus-operator-operator-67d764bff6-j99jm 2/2 Running 0 56s
prometheus-prom-prometheus-operator-prometheus-0 3/3 Running 0 56s

Finally, you can view the status of the grafana-tls certificate. The ca.crt will be 0 bytes, but the tls.crt and tls.key should be greater than 0 bytes. If there is an error, the error message should show up here:

$ kubectl -n prom describe secret grafana-tls
Name: grafana-tls
Namespace: prom
Labels: <none>
Annotations: cert-manager.io/alt-names: grafana.example.com
cert-manager.io/certificate-name: grafana-tls
cert-manager.io/common-name: grafana.example.com
cert-manager.io/ip-sans:
cert-manager.io/issuer-kind: ClusterIssuer
cert-manager.io/issuer-name: letsencrypt-prod
cert-manager.io/uri-sans:
Type: kubernetes.io/tlsData
====
ca.crt: 0 bytes
tls.crt: 3574 bytes
tls.key: 1671 bytes

Now you can go to https://grafana.example.com and it should be secured by a free valid SSL certficiate. That is it. As long as the cert-manager is running you don’t have to do anything, ever! cert-manager should automatically renew Let’s Encrypt SSL certificates without any user interaction every month.

--

--

Kevin Coakley
The Startup

Senior Systems and Cloud Integration Engineer — San Diego Supercomputer Center — http://www.kevincoakley.comhttps://github.com/kevincoakley