Skip to main content
  1. Posts/

[K3s-05] How to automate issuing TLS certificate in K3s ingress

By using Let's encrypt and cert-manager we can attach some TLS certificate to our k3s cluster. Install cert-manager using helm. I won’t explain about how to install helm and what is helm in this post. If helm is not installed, please refer to helm docs

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

curl -L -o cert-manager.yaml https://github.com/cert-manager/cert-manager/releases/download/v1.8.0/cert-manager.crds.yaml
kubectl apply -f cert-manager.yaml

helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--version v1.8.0

kubectl get pods -n cert-manager

First, create a ClusterIssuer (that can access through across the namespaces). Also, k3s secret that store cloudflare api token. This cloudflare api token must have access to edit DNS zones (in dns-01 challenges). If you are using Route53 in AWS, you can visit this blog (Medium about Wild card certificates). So, the basic idea is to know what cloud provider you are using for your DNS. For detailed list that cert-manager supports, visit cert-manager configuration on acme dns01

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-stg
spec:
  acme:
    email: [email protected]
    # let's encrypt staging environment
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      # Secret resource that will store the account's private key.
      name: le-issuer-acct-key
    solvers:
    - dns01:
        cloudflare:
          email: [email protected]
          apiTokenSecretRef:
            name: cloudflare-api-token-secret
            key: api-token
      selector:
        dnsZones:
        - 'jaehong21.com'
        - '*.jaehong21.com'
       
---
apiVersion: v1
kind: Secret
metadata:
  name: cloudflare-api-token-secret
  namespace: cert-manager
type: Opaque
stringData:
  api-token: <cloudflare-api-token>

Then, check if issuer’s status Ready is True by kubectl get clusterissuer -A. It may take some time.

! IMPORTANT: https://letsencrypt.org/docs/staging-environment/
You must be aware above example with https://acme-staging-v02.api.letsencrypt.org/directory is an staging environment for let’s encrypt, which doesn’t get bounded to rate limit issuing certificates. You must replace above URL to https://acme-v02.api.letsencrypt.org/directory in production, when you want to get an trusted(valid) certificate for the browser.

Now, create a certificate with existing issuers. Even wildcards certificate is available when it is using dns-01 challenges. When it is using http-01, it is hard to achieve wildcard certificates, you should directly annotate the specific URL.

apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
  name: le-jaehong21-com
  namespace: default
spec:
  secretName: le-jaehong21-com
  issuerRef:
    name: letsencrypt-stg
    kind: ClusterIssuer
  commonName: '*.jaehong21.com'
  dnsNames:
    - "*.jaehong21.com"
# Check whether certificates are created
# Initially, the `Ready` status will be `False`
kubectl get certs -A

# issues are presented command below will show the progress
# when it is done, it won't show any resources
kubectl get challenges -A

# After, double-check whether certificate's `Ready` is also `True`

You can also, look at the progress by checking the (cloudflare) dashboard whether TXT records are added on the hosted zone (which is used with dns-01 challenges).

Finally, implement the tls secret that is created with certificate to the ingress.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: traefik-ingress
  annotations:
    traefik.ingress.kubernetes.io/router.tls: "true"
spec:
  ingressClassName: traefik
  rules:
    - host: k3s.jaehong21.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-deployment
                port:
                  number: 80

  tls:
  - hosts:
      - k3s.jaehong21.com
    secretName: le-jaehong21-com

Cloudflare Origin CA #

For me, I used to manage DNS records on Cloudflare. Which Cloudflare also provides to issue some certificate with much longer expiry date compare to let’s encrypt.

kubectl create secret generic k3s-tls-secret --from-file=tls.crt=./server.crt --from-file=tls.key=./server.key --namespace dev
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: traefik-ingress
  annotations:
    traefik.ingress.kubernetes.io/router.tls: "true"

spec:
  ingressClassName: traefik
  rules:
    - host: k3s.jaehong21.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: nginx-deployment
                port:
                  number: 80
  tls:
    - secretName: k3s-tls-secret

However, Origin CA is a self-signed certificate, which is valid only in Cloudflare Network itself. So, to do so the host must be proxied through Cloudflare Network like example below. In this case, as reqeusts are going through Cloudflare Network, it can be rerouted through LAX locations also.

resource "cloudflare_record" "k3s_test" {
  zone_id = var.jaehong21_com.zone_id
  name    = "k3s"
  value   = "43.202.242.99"
  proxied = true
  type    = "A"
  ttl     = 1
}

You should upgrade terraform cloudflare provider to the latest version (at least above version 4). As version 3 in cloudflare-go has bug on modifying and maintaining proxied value to true on terraform.

Reference #