We’re going to need some certs to get rid of the annoying messages for http sites. For this we will install cert-manager

Adding cert-manager to Cluster

This has to just be an easy copy paste here. Once thing I changed and will change in the others is:

Delete:

  valuesFrom:
  - kind: ConfigMap
    name: cert-manager-helm-chart-value-overrides
    valuesKey: values.yaml # This is the default, but best to be explicit for clarity

Add:

  values: # pasted contents of upstream values.yaml below, indented 4 spaces
    # ... all of the values ...

I also updated the chart to 1.15.x as 1.15.2 looks to be the latest.

Configuring cert-manager for DNS01 from Cloudflare Cert Issuer

First part was easy but now it’s heating up a bit. This guide now goes into how to setup cert-manager to use Cloudflare but it doesn’t go into making the sealed secret until a later guide so I’ll bring that forward.

Also, when following this I noticed a dependency flag was added for sealed-secrets:

  dependsOn:
  - name: sealed-secrets

However, everything using this prior did not do this so I’ll give it a shot everywhere that needs a sealed secret. Details for the Cloudflare token were here.

I guess we didn’t need the secret yet but I ended up running this and throwing the output file (sealedsecret-cloudflare-api-token-secret.yaml) in the cert-manager folder since it felt weird to be missing the secret that was configured.

  kubectl create secret generic cloudflare-api-token-secret \
  --namespace cert-manager \
  --dry-run=client \
  --from-literal=api-token=REDACTED -o json \
  | kubeseal --controller-name=sealed-secrets --controller-namespace=sealed-secrets --cert pub-cert.pem \
  > sealedsecret-cloudflare-api-token-secret.yaml

Finally, we test to see if it’s good:

kubectl describe clusterissuer letsencrypt-prod
...
Status:
  Acme:
    Last Private Key Hash:  f4g8fTo5jjGBpxg4vwYcSPkGW2UQVFuKF0kEoLBTSP4=
    Last Registered Email:  [email protected]
    Uri:                    https://acme-v02.api.letsencrypt.org/acme/acct/1879879286
  Conditions:
    Last Transition Time:  2024-08-08T03:32:15Z
    Message:               The ACME account was registered with the ACME server
    Observed Generation:   1
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>

Looks ready to rock and roll!

Creating a Wildcard Certificate

Staging

After creating the secret, which we already did, we get right into making a “staging” cert which doesn’t have the same rate limiting as the prod endpoint.

The name of the cert in the example is wrong

To get the name I ran:

kubectl get certificate -A

Then I panicked for 2-4min waiting for the cert. Finally all systems were nominal:

kubectl describe certificate -n letsencrypt-wildcard-cert letsencrypt-wildcard-cert-example.com-staging
...
  Conditions:
    Last Transition Time:  2024-08-08T03:49:25Z
    Message:               Certificate is up to date and has not expired
    Observed Generation:   1
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2024-11-06T02:50:51Z
  Not Before:              2024-08-08T02:50:52Z
  Renewal Time:            2024-10-07T02:50:51Z
  Revision:                1

As I waited I checked the logs for cert-manager namespaced pods. They were terrifying! Bunch of “re-queueing”, “failed to update”, “no longer exists” logs but we nailed it:

{"log":"I0808 03:49:25.544288       1 acme.go:233] \"certificate issued\" logger=\"cert-manager.controller.sign\" resource_name=\"letsencrypt-wildcard-cert-example.com-staging-1\" resource_namespace=\"letsencrypt-wildcard-cert\" resource_kind=\"CertificateRequest\" resource_version=\"v1\" related_resource_name=\"letsencrypt-wildcard-cert-example.com-staging-1-2727934737\" related_resource_namespace=\"letsencrypt-wildcard-cert\" related_resource_kind=\"Order\" related_resource_version=\"v1\"\n","stream":"stderr","time":"2024-08-08T03:49:25.544425649Z"}

Prod

Next was to test the prod issuer, letsencrypt-prod, which I will not rush this time.

kubectl describe certificate -n letsencrypt-wildcard-cert letsencrypt-wildcard-cert-example.com
...
Status:
  Conditions:
    Last Transition Time:  2024-08-08T03:59:29Z
    Message:               Certificate is up to date and has not expired
    Observed Generation:   1
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2024-11-06T03:00:57Z
  Not Before:              2024-08-08T03:00:58Z
  Renewal Time:            2024-10-07T03:00:57Z
  Revision:                1

Replicating the Secrets (Which Point to Certs)

Secret Replicator

Now this is some jank shit but we need to replicate the certs (which are secrets) across namespaces so everyone can use them. For this we have the secret replicator tool. The repo is even archived!

Once again I am going to skip the separate ConfigMap file since this dude decided that sucked and go right to the helmrelease with the configs.

It didn’t work:

secret-replicator   52s   False   Helm install failed for release secret-replicator/secret-replicator with chart [email protected]: template: secret-replicator/templates/serviceaccount.yaml:1:18: executing "secret-replicator/templates/serviceaccount.yaml" at <.Values.rbac.enabled>: nil pointer evaluating interface {}.enabled

Going to the official chart here I can see that funky penguin was trying to fix something and I don’t think latest is a good thing to pull.

I went with chart 0.5.0 and tag 0.1.1, the versions before funky man’s changes and things came up:

thaynes@kubevip:~/workspace/cert-manager$ kubectl get pods -n secret-replicator
NAME                                 READY   STATUS    RESTARTS   AGE
secret-replicator-7b9f696d99-m6sm4   1/1     Running   0          18s

Now we should see that our secrets cloned themselves:

kubectl get secrets -A | grep letsencrypt-wildcard-cer
letsencrypt-wildcard-cert   letsencrypt-wildcard-cert-example.com                      kubernetes.io/tls                     2      24m
letsencrypt-wildcard-cert   letsencrypt-wildcard-cert-example.com-staging              kubernetes.io/tls                     2      34m

Aaand they didn’t…

Cert manager has a guide on a more up to date way to replicate secrets. Plus the last commit in this repo says not to use it so I’m going to ROLL BACK!

Roll Back Secret Replicator

Funky man never says how to do this so I’m going to start be deleting the shit that was added for secret-replicator.

$ git status
On branch main
Your branch is up to date with 'origin/main'.

Changes not staged for commit:
  (use "git add/rm <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        deleted:    bootstrap/helmrepositories/helmrepository-kiwigrid.yaml
        deleted:    bootstrap/kustomizations/kustomization-secret-replicator.yaml
        deleted:    bootstrap/namespaces/namespace-secret-replicator.yaml
        deleted:    secret-replicator/helmrelease-secret-replicator.yaml

And like a boss everything is gone! That was easy.

What to Use Now?

I am now presented with two choices. Either kubernetes-reflector or kubernetes-replicator.

I am going to go with reflector because it seems easy enough given the [cert-manager docs](kubectl get secrets -A grep letsencrypt-wildcard-cer)

For my certs I just added reflection to podinfo to start since we’ll be testing that end to end.

  secretTemplate:
    annotations:
      reflector.v1.k8s.emberstack.com/reflection-allowed: "true"
      reflector.v1.k8s.emberstack.com/reflection-allowed-namespaces: "podinfo"  # Control destination namespaces
      reflector.v1.k8s.emberstack.com/reflection-auto-enabled: "true" # Auto create reflection for matching namespaces
      reflector.v1.k8s.emberstack.com/reflection-auto-namespaces: "podinfo" # Control auto-reflection namespaces

The rest were pretty generic files I copied from podinfo.

However, nothing worked. Something got borked:

flux-system                     main@sha1:dbbb3b3f      False           False   Kustomization/flux-system/reflector dry-run failed: failed to create typed patch object (flux-system/reflector; kustomize.toolkit.f
luxcd.io/v1, Kind=Kustomization): .spec.validation: field not declared in schema

It does not like validation: server which was in podman, maybe because I used v1 instead of v1beta1 so I will just delete that and maybe look what it does later. I am also going to remove the values specified in the helmrelease and just rock the defaults all the way here.

In my thrashing I also set the helm repo name to reflector when this chart is in the emberstack repo. Hopefully this was the last mistake.

And it worked!

thaynes@kubevip:~/workspace/cert-manager$ kubectl get secrets -A | grep letsencrypt-wildcard-cer
letsencrypt-wildcard-cert   letsencrypt-wildcard-cert-example.com                      kubernetes.io/tls                     2      76m
letsencrypt-wildcard-cert   letsencrypt-wildcard-cert-example.com-staging              kubernetes.io/tls                     2      86m
podinfo                     letsencrypt-wildcard-cert-example.com                      kubernetes.io/tls                     2      5m56s
podinfo                     letsencrypt-wildcard-cert-example.com-staging              kubernetes.io/tls                     2      5m56s

But now I have a weird version of sealed-secrets failing to start that isn’t the tag I specified!!

thaynes@kubevip:~/workspace/cert-manager$ kubectl describe deployment sealed-secrets -n sealed-secrets
Name:                   sealed-secrets
Namespace:              sealed-secrets
CreationTimestamp:      Sat, 03 Aug 2024 21:22:37 -0400
Labels:                 app.kubernetes.io/instance=sealed-secrets
                        app.kubernetes.io/managed-by=Helm
                        app.kubernetes.io/name=sealed-secrets
                        app.kubernetes.io/version=v0.16.0
                        helm.sh/chart=sealed-secrets-1.16.1
                        helm.toolkit.fluxcd.io/name=sealed-secrets
                        helm.toolkit.fluxcd.io/namespace=sealed-secrets
Annotations:            deployment.kubernetes.io/revision: 2
                        meta.helm.sh/release-name: sealed-secrets
                        meta.helm.sh/release-namespace: sealed-secrets
Selector:               app.kubernetes.io/instance=sealed-secrets,app.kubernetes.io/name=sealed-secrets
Replicas:               1 desired | 1 updated | 2 total | 1 available | 1 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:           app.kubernetes.io/instance=sealed-secrets
                    app.kubernetes.io/name=sealed-secrets
  Service Account:  sealed-secrets
  Containers:
   sealed-secrets:
    Image:      quay.io/bitnami/sealed-secrets-controller:v0.16.0
    Port:       8080/TCP
    Host Port:  0/TCP
    Command:
      controller
    Args:
      --key-prefix
      sealed-secrets-key
    Liveness:     http-get http://:8080/healthz delay=0s timeout=1s period=10s #success=1 #failure=3
    Readiness:    http-get http://:8080/healthz delay=0s timeout=1s period=10s #success=1 #failure=3
    Environment:  <none>
    Mounts:
      /tmp from tmp (rw)
  Volumes:
   tmp:
    Type:          EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:        
    SizeLimit:     <unset>
  Node-Selectors:  <none>
  Tolerations:     <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    False   ProgressDeadlineExceeded
OldReplicaSets:  sealed-secrets-7fbdddf68 (1/1 replicas created)
NewReplicaSet:   sealed-secrets-f88f5fccb (1/1 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  33m   deployment-controller  Scaled up replica set sealed-secrets-f88f5fccb to 1

Fixed it with fiddling, was crazy.