cert-manager is a certificate controller for Kubernetes. It makes requesting certificates trivial and can be used simply through annotations.

Architecture

cert-manager uses CRDs to handle the lifecycle of certificates. It listens for the creation of new Certificate objects, or specifically annotated Ingresses, which, once discovered, automatically get a CertificateRequest assigned. cert-manager handles the processing of the request, proving the ownership of the domain either through an HTTP or DNS ACME Challenge & then creates a kubernetes.io/tls Secret containing the resulting serving cert.

flowchart LR
    acme[ACME Challenge] -.-> achttp(HTTP)
    acme -.-> acdns(DNS)

    achttp --> nginx
    achttp --> cm
    acdns --> cm

    subgraph cluster[Kubernetes Cluster]
    direction LR
    subgraph nginx[NGINX Ingress]
    end

    issuer([Cluster Issuer])

    subgraph tn[Tenant Namespace]
    svc{{Service}}
    ing(Ingress) --> svc
    ing -->|tls| certsec
    ing -.->|annotation| cert([Certificate])
    cert --> certsec([Certificate Secret])
    cert -.-> certreq([Certificate Request])
    end

    certreq -.-> issuer

    nginx --> ing

    cm --> issuer

    subgraph cm[cert-manager]
    
    end

    end

When using a custom Domain & Issuer:

flowchart LR
    acme[ACME Challenge] -.-> achttp(HTTP)
    acme -.-> acdns(DNS)

    achttp --> nginx
    achttp --> cm
    acdns --> cm

    subgraph cluster[Kubernetes Cluster]
    direction LR
    subgraph nginx[NGINX Ingress]
    end

    subgraph tn[Tenant Namespace]
    svc{{Service}}
    ing(Ingress) --> svc
    ing -->|tls| certsec
    ing -.->|annotation| cert([Certificate])
    cert --> certsec([Certificate Secret])
    cert -.-> certreq([Certificate Request])

    issuer([Issuer])
    end

    certreq -.-> issuer

    nginx --> ing

    cm --> issuer

    subgraph cm[cert-manager]
    
    end

    end

Usage

To use another domain besides *.app.konst.fish, create a CNAME record that points to the clusters canonical domain app.konst.fish. Ingress Nginx will then correctly route traffic originating from the custom domain.

First create an Issuer CRD. These can be created for any challenge, the following example uses http. See a complete list of available solvers here.

apiVersion: cert-manager.io/v1
kind: Issuer
metadata:
  name: letsencrypt-<tenant>-http
  namespace: tenant
spec:
  acme:
    email: <tenant email>
    privateKeySecretRef:
      name: <sample-secret-name-this-can-be-anything>
    server: https://acme-v02.api.letsencrypt.org/directory
    solvers:
    - http01:
        ingress:
          class: nginx
          serviceType: ClusterIP

Once this issuer has been created, adjust the Ingress like the following.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    cert-manager.io/issuer: "letsencrypt-<tenant>-http"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - example.com
    secretName: example-tls # note, this can be any name, it's the secret cert-manager will use to populate the cert into
  rules:
  - host: "example.com"