Traefik With Lets Encrypt Wildcard SSL Certificate Using Kubernetes

Let’s encrypt has introduced wildcard certificates and traefik has released a v2 which is completely different from v1. This calls for a tutorial on how to use the two together in kubernetes.

Why wildcard certificates?

You don’t need separate https certicates for your subdomain, especially if you are used to deploying your applications as different subdomains. This will significantly reduce calls to Let’s Encrypt servers which is now important since they have introduced serious rate limits. And of course, Let’s encrypt is totally free so you have no reason to not use HTTPS anymore, not to mention its more secure than using HTTP.

Why traefik

Traefik is a beautiful piece of software which is container aware and can get/issue/renew Let’s Encrypt certificates automatically, once configured.

How?

At the time of writing this, Let’s Encrypt only supports wildcard certificates using the DNS-01 verification method so thats what this article uses as well.

This article also uses GoDaddy for domains. The API keys for GoDaddy can be generated from GoDaddy Developer Portal. Traefik supports other DNS providers, any of which can be used instead.

  1. Create a subchart for traefik:

    helm create traefik-server
    
  2. Add traefik chart as a dependency by adding the following section in Charts.yml in the traefik-server chart:

    dependencies:
      - name: traefik
        version: 10.20.1
        repository: https://helm.traefik.io/traefik
    
  3. Add the traefik chart repo and update the dependency added.

    helm repo add traefik https://helm.traefik.io/traefik
    helm repo update
    helm dependency update
    
  4. Create the following values.yml file in the traefik-server chart:

    Note: Use the let’s encrypt staging endpoint while testing or the domain will get rate limited. The traefik section indicates that the values set are for the dependent traefik chart

    traefik:
      providers:
        kubernetesCRD:
            enabled: true
    
      logs:
        # Traefik logs concern everything that happens to Traefik itself (startup, configuration, events, shutdown, and so on).
        general:
            # By default, the logs use a text format (common), but you can
            # also ask for the json format in the format option
            # format: json
            # By default, the level is set to ERROR. Alternative logging levels are DEBUG, PANIC, FATAL, ERROR, WARN, and INFO.
            level: INFO
        access:
            # To enable access logs
            enabled: true
    
        # resources:
        #   limits:
        #     cpu: 1
        #     memory: 100Mi
    
      rbac:
        enabled: true
    
      additionalArguments:
        # - "--providers.kubernetesingress=true"
        # - "--providers.kubernetesingress.ingressclass=traefik"
    
        # Please note that this is the staging Let's Encrypt server.
        # Once you get things working, you should remove that whole line altogether.
        - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
    
      certResolvers:
        letsencrypt:
          # for challenge options cf. https://doc.traefik.io/traefik/https/acme/
          email: <your email>
          dnsChallenge:
          provider: godaddy
          delayBeforeCheck: 30
          resolvers:
              - 1.1.1.1
              - 8.8.8.8
          # tlsChallenge: true
          # httpChallenge:
          #   entryPoint: "web"
          # match the path to persistence
          storage: /data/acme.json
    
      env:
        - name: GODADDY_API_KEY
          value: <your API key>
        - name: GODADDY_API_SECRET
          value: <your API secret>
    
      ports:
        web:
          redirectTo: websecure
        websecure:
          tls:
            enabled: true
            # this is the name of a TLSOption definition
            options: ""
            certResolver: "letsencrypt"
            domains:
              - main: "<your domain>"
                sans:
                  - "*.<your domain>"
    
      # If the certificates are to be persisted
      # persistence:
      #   enabled: true
      #   name: data
      #   accessMode: ReadWriteOnce
      #   size: 128Mi
      #   path: /data
    
      # securityContext:
      #   readOnlyRootFilesystem: false
      #   runAsGroup: 0
      #   runAsUser: 0
      #   runAsNonRoot: false
    
      # deployment:
      #   initContainers:
      #   - name: volume-permissions
      #     image: busybox:1.31.1
      #     command: ["sh", "-c", "chmod -Rv 600 /data/*"]
      #     volumeMounts:
      #       - name: data
      #         mountPath: /data
    
  5. Deploy the traefik-server chart

    helm upgrade --install traefik-server traefik-server
    
  6. Point the loadbalancer IP to the deomain

    Wait for a few minutes for DNS propagation, challenge andmthe SSL cetrificate to be issued.

  7. For every service add an ingress route as follows:

    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: {{ .Values.appName }}-ingress-route
      labels:
        chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
        release: "{{ .Release.Name }}"
    spec:
      entryPoints:
        - websecure
      routes:
      - match: Host(`{{ .Values.appName }}.{{ .Values.ingressHost }}`)
        kind: Rule
        services:
        - name: {{ .Values.appName }}-service
          port: 80
      tls:
        certResolver: "letsencrypt"
        domains:
          - main: "<your domain>"
            sans:
              - "*."<your domain>"
    

    Note: The tls section here. The cert resolver and the domains has to match with the ones in the traefik-server chart’s values.yml file

  8. Switch to Let’s Encrypt’s production endpoints once everything is working by removing/commenting the following line from values.yml file:

    # Please note that this is the staging Let's Encrypt server.
    # Once you get things working, you should remove that whole line altogether.
    - "--certificatesresolvers.letsencrypt.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
    
  9. That’s it

References