How to configure HTTPS for your applications on the Kubernetes cluster? How to manage TLS certificates using traefik? Traefik proxy is a very popular ingress controller in kubernetes environments. It supports both HTTPS (router) and TLS connections. This article explains how to configure TLS connection and obtain TLS certificates dynamically using pebble (Only for the test environment). In the real world, you will be using Let’s Encrypt (ACME). ACME stands for Automatic Certificate Management environment.

Pebble is used to simulate the let’s encrypt on the LAB environment. The following diagram explains what we are going to achieve at the end of this article.

How to Pebble works?
Pebble is a small ACME test server and not suited for production use. It provides a simplified ACME testing front end. In my LAB environment, the pebble will listen on the HTTPS port and traefik will try to establish an HTTPS connection to obtain the TLS certs from the pebble. To establish the connection between traefik and pebble, the root certificate needs to be shared with traefik pod.

Installing Pebble:
Pebble can be installed quickly using the helm charts. For detailed information about pebble and deployment, kindly check in Pebble GitHub page.
- Add the pebble jupyterhub helm repo.
root@kmaster1:~# helm repo add jupyterhub https://jupyterhub.github.io/helm-chart "jupyterhub" has been added to your repositories root@kmaster1:~#
2. Search the jypyterhub repo to know the pebble version.
root@kmaster1:~# helm search repo jupyterhub NAME CHART VERSION APP VERSION DESCRIPTION jupyterhub/jupyterhub 2.0.0 3.0.0 Multi-user Jupyter installation jupyterhub/pebble 1.0.1 v2.3.1 This Helm chart bootstraps Pebble: an ACME serv... root@kmaster1:~#
3. Download the pebble value files to edit a few parameters.
root@kmaster1:~# helm show values jupyterhub/pebble > /tmp/pebble_vaules.yaml
4. Update the environment values. Enable “PEBBLE_VA_ALWAYS_VALID ” and change the value to “1” .
env:
## ref: https://github.com/letsencrypt/pebble#testing-at-full-speed
- name: PEBBLE_VA_NOSLEEP
value: "1"
## ref: https://github.com/letsencrypt/pebble#invalid-anti-replay-nonce-errors
- name: PEBBLE_WFE_NONCEREJECT
value: "0"
## ref: https://github.com/letsencrypt/pebble#authorization-reuse
- name: PEBBLE_AUTHZREUSE
value: "100"
# ## ref: https://github.com/letsencrypt/pebble#skipping-validation
- name: PEBBLE_VA_ALWAYS_VALID
value: "1"
5. Disable the coredns which is optional.
## coredns is an optional DNS server that can for example point anything.test ## to your-acme-client.your-namespace.svc.cluster.local. coredns: enabled: false
6. Deploy the pebble using the modified values file.
root@kmaster1:~# helm install pebble jupyterhub/pebble --values /tmp/pebble_vaules.yaml -n traefik
NAME: pebble
LAST DEPLOYED: Tue Oct 18 05:27:40 2022
NAMESPACE: traefik
STATUS: deployed
REVISION: 1
NOTES:
The ACME server is available at:
https://pebble.traefik/dir
https://localhost:32443/dir
The ACME server generates leaf certificates to ACME clients,
and signs them with an insecure root cert, available at:
https://pebble.traefik:8444/roots/0
https://localhost:32444/roots/0
Communication with the ACME server itself requires
accepting a root certificate in configmap/pebble:
kubectl get configmap/pebble -o jsonpath="{.data['root-cert\.pem']}"
root@kmaster1:~#
7. Verify the pebble installation.
root@kmaster1:~# kubectl get all -n traefik NAME READY STATUS RESTARTS AGE pod/pebble-5dfccc497b-tmz7f 1/1 Running 0 20m pod/traefik-fc9c99df4-pc6ps 1/1 Running 0 13s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/pebble NodePort 10.102.40.179 <none> 443:32443/TCP,8444:32444/TCP 20m service/traefik LoadBalancer 10.111.162.31 172.16.16.9 80:30170/TCP,443:32044/TCP 13s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/pebble 1/1 1 1 20m deployment.apps/traefik 1/1 1 1 13s NAME DESIRED CURRENT READY AGE replicaset.apps/pebble-5dfccc497b 1 1 1 20m replicaset.apps/traefik-fc9c99df4 1 1 1 13s root@kmaster1:~#
8. Pebble deployment would have created the configmap to store the root certificate. List the configmap in the traefik namespace.
root@kmaster1:~# kubectl get cm -n traefik NAME DATA AGE kube-root-ca.crt 1 37h pebble 3 21m root@kmaster1:~#
We can see that configmap has been created in the name of “pebble” which has the root certificate and root keys.
Configuring traefik to use Pebble ACME:
Traefik value files need a few changes to use pebble as ACME server. If you do not have the traefik value file, you can extract using the following command.
root@kmaster1:~# helm get values traefik -n traefik > /tmp/traefik_vaules.current.yaml
1. I have an existing value file that was used to deploy traefik. Navigate to “additionalArguments” section.
#
# Configure Traefik static configuration
# Additional arguments to be passed at Traefik's binary
# All available options available on https://docs.traefik.io/reference/static-configuration/cli/
## Use curly braces to pass values: `helm install --set="additionalArguments={--providers.kubernetesingress.ingressclass=traefik-internal,--log.level=DEBUG}"`
additionalArguments: []
# - "--providers.kubernetesingress.ingressclass=traefik-internal"
# - "--log.level=DEBUG"
2. Remove the empty list from “additionalArguments” and update the following.
additionalArguments: - --certificatesresolvers.pebble.acme.tlschallenge=true - --certificatesresolvers.pebble.acme.email=test@hello.com - --certificatesresolvers.pebble.acme.storage=/data/acme.json - --certificatesresolvers.pebble.acme.caserver=https://pebble.traefik/dir
3. Mount the pebble configmap as volume on the traefik pod.
Existing entries:
volumes: [] # - name: public-cert # mountPath: "/certs" # type: secret
Updated entries
volumes:
- name: pebble
mountPath: "/cacerts"
type: configMap
# - name: public-cert
# mountPath: "/certs"
# type: secret
4. update the environment variable to make the traefik pod to aware that the pebble root certificate is placed under mount path “/cacerts”. Search for “env” and update the environment variable.
Existing entries:
# Environment variables to be passed to Traefik's binary env: [] # - name: SOME_VAR # value: some-var-value # - name: SOME_VAR_FROM_CONFIG_MAP # valueFrom:
Updated entries:
# Environment variables to be passed to Traefik's binary
env:
- name: LEGO_CA_CERTIFICATES
vaule: "/cacerts/root-cert.pem"
5. We have successfully updated all the required values. Let’s upgrade the traefik deployment using helm.
root@kmaster1:~# helm ls -n traefik NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION pebble traefik 1 2022-10-18 05:27:40.845619796 +0000 UTC deployed pebble-1.0.1 v2.3.1 traefik traefik 1 2022-10-18 05:47:58.708384764 +0000 UTC deployed traefik-15.2.2 2.9.1 root@kmaster1:~#
root@kmaster1:~# helm upgrade traefik traefik/traefik --values traefik-values.yaml -n traefik Release "traefik" has been upgraded. Happy Helming! NAME: traefik LAST DEPLOYED: Tue Oct 18 06:14:54 2022 NAMESPACE: traefik STATUS: deployed REVISION: 2 TEST SUITE: None root@kmaster1:~#
Test our work:
To test the certificate resolver, will deploy nginx and expose the service.
1. Deploy the nginx service on a new namespace called “testpebble”
root@kmaster1:~# kubectl apply -f https://k8s.io/examples/application/deployment.yaml -n testpebble --createnamespace deployment.apps/nginx-deployment created root@kmaster1:~# kubectl get all -n testpebble NAME READY STATUS RESTARTS AGE pod/nginx-deployment-66b6c48dd5-fmpmb 1/1 Running 0 11s pod/nginx-deployment-66b6c48dd5-ltwpm 1/1 Running 0 11s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-deployment 2/2 2 2 11s NAME DESIRED CURRENT READY AGE replicaset.apps/nginx-deployment-66b6c48dd5 2 2 2 11s root@kmaster1:~#
2. Expose the nginx-deployment to ClusterIP.
root@kmaster1:~# kubectl expose deployment nginx-deployment -n testpebble service/nginx-deployment exposed root@kmaster1:~# kubectl get all -n testpebble NAME READY STATUS RESTARTS AGE pod/nginx-deployment-66b6c48dd5-fmpmb 1/1 Running 0 92s pod/nginx-deployment-66b6c48dd5-ltwpm 1/1 Running 0 92s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/nginx-deployment ClusterIP 10.110.176.233 <none> 80/TCP 4s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/nginx-deployment 2/2 2 2 92s NAME DESIRED CURRENT READY AGE replicaset.apps/nginx-deployment-66b6c48dd5 2 2 2 92s root@kmaster1:~#
3. Create an ingressroute for nginx-deployment.
root@kmaster1:~# cat ingress.yaml
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: nginx-deployment
namespace: testpebble
spec:
entryPoints:
- websecure
routes:
- match: Host(`nginxtls.ua.com`)
kind: Rule
services:
- name: nginx-deployment
port: 80
tls:
certResolver: pebble
root@kmaster1:~#
root@kmaster1:~# kubectl create -f ingress.yaml -n testpebble ingressroute.traefik.containo.us/nginx-deployment created root@kmaster1:~#
4. Ensure that you have the following host entry from where you are trying to access this nginx deployment.
172.16.16.9 nginxtls.ua.com
172.16.16.9 is the traefik IP address.
root@kmaster1:~# kubectl get svc -n traefik |grep LoadBalancer traefik LoadBalancer 10.111.162.31 172.16.16.9 80:30170/TCP,443:32044/TCP 57m root@kmaster1:~#
5. Try to access “https://nginxtls.ua.com” from your host.

Click on advanced and proceed. You are getting this warning because pebble provides fake certificates for the LAB environment.
6. Here is the nginx welcome page.

7. Click on “Not Secure” and click on the “Certificate not valid” tab.

Check the pebble logs to know how the valid certificate has been assigned to the microservice.
root@kmaster1:~# kubectl logs pebble-5dfccc497b-tmz7f -n traefik
Pebble 2022/10/18 06:26:12 There are now 1 accounts in memory
Pebble 2022/10/18 06:26:12 POST /order-plz -> calling handler()
Pebble 2022/10/18 06:31:19 POST /order-plz -> calling handler()
Pebble 2022/10/18 06:55:46 POST /order-plz -> calling handler()
Pebble 2022/10/18 06:55:46 There are now 1 authorizations in the db
Pebble 2022/10/18 06:55:46 Added order "OFjipa4HBL9PqiCC89nOQZqkdGbcZMThwStzf_98Jws" to the db
Pebble 2022/10/18 06:55:46 There are now 1 orders in the db
Pebble 2022/10/18 06:55:46 POST /authZ/ -> calling handler()
Pebble 2022/10/18 06:55:46 POST /chalZ/ -> calling handler()
Pebble 2022/10/18 06:55:46 Pulled a task from the Tasks queue: &va.vaTask{Identifier:acme.Identifier{Type:"dns", Value:"nginxtls.ua.com"}, Challenge:(*core.Challenge)(0xc000079720), Account:(*core.Account)(0xc000226900)}
Pebble 2022/10/18 06:55:46 Starting 3 validations.
Pebble 2022/10/18 06:55:46 PEBBLE_VA_ALWAYS_VALID is enabled. Skipping real validation of challenge gEz7JpwqaomkhZuV9fnxvHbN6wXLlAVCCh6y-0IK0PA
Pebble 2022/10/18 06:55:46 POST /authZ/ -> calling handler()
Pebble 2022/10/18 06:55:52 POST /finalize-order/ -> calling handler()
Pebble 2022/10/18 06:55:52 Order OFjipa4HBL9PqiCC89nOQZqkdGbcZMThwStzf_98Jws is fully authorized. Processing finalization
Pebble 2022/10/18 06:55:52 Issued certificate serial 1ee532509beda353 for order OFjipa4HBL9PqiCC89nOQZqkdGbcZMThwStzf_98Jws
Pebble 2022/10/18 06:55:52 POST /my-order/ -> calling handler()
Pebble 2022/10/18 06:55:52 POST /certZ/ -> calling handler()
root@kmaster1:~#
On the traefik dashboard, you can see the TLS cert resolver entry like below.

We have successfully configured the TLS connection and obtained TLS certificates dynamically using pebble on the k8s traefik environment.
Leave a Reply