From 779e448686646313c49c82156307c7a818c13301 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 2 Aug 2026 17:07:18 +1000 Subject: [PATCH 1/2] Add Let's Encrypt DNS-01 clouddns ClusterIssuers unkin.net public DNS is delegated to Google Cloud DNS, so publicly-trusted wildcard certs need an ACME issuer using cert-manager's clouddns DNS-01 solver. The existing vault-issuer (internal PKI) is unchanged. - Add ClusterIssuers letsencrypt (prod) and letsencrypt-staging, both using a dns01 clouddns solver with a GCP service-account key. - Sync that key from Vault KV into Secret cert-manager-clouddns via a VaultStaticSecret + VaultAuth (role cert_manager_clouddns) and a dedicated cert-manager-clouddns service account. - Wire the new files into the base kustomization. - Whitelist cert-manager.io ClusterIssuer in the platform AppProject. The clouddns project and the KV secret value are set out-of-band. Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT --- .../clusterissuer_letsencrypt-staging.yaml | 19 +++++++++++++++++++ .../clusterissuer_letsencrypt.yaml | 19 +++++++++++++++++++ apps/base/cert-manager/kustomization.yaml | 5 +++++ .../serviceaccount_cert-manager-clouddns.yaml | 11 +++++++++++ apps/base/cert-manager/vaultauth.yaml | 18 ++++++++++++++++++ .../vaultstaticsecret_clouddns.yaml | 15 +++++++++++++++ argocd/projects/platform.yaml | 2 ++ 7 files changed, 89 insertions(+) create mode 100644 apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml create mode 100644 apps/base/cert-manager/clusterissuer_letsencrypt.yaml create mode 100644 apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml create mode 100644 apps/base/cert-manager/vaultauth.yaml create mode 100644 apps/base/cert-manager/vaultstaticsecret_clouddns.yaml diff --git a/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml b/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml new file mode 100644 index 0000000..ee63acc --- /dev/null +++ b/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-staging +spec: + acme: + server: https://acme-staging-v02.api.letsencrypt.org/directory + email: admin@unkin.net + privateKeySecretRef: + name: letsencrypt-staging-account-key + solvers: + - dns01: + # project must match the GCP project hosting the unkin.net Cloud DNS zone + cloudDNS: + project: REPLACE-WITH-GCP-PROJECT + serviceAccountSecretRef: + name: cert-manager-clouddns + key: key.json diff --git a/apps/base/cert-manager/clusterissuer_letsencrypt.yaml b/apps/base/cert-manager/clusterissuer_letsencrypt.yaml new file mode 100644 index 0000000..fc9dd11 --- /dev/null +++ b/apps/base/cert-manager/clusterissuer_letsencrypt.yaml @@ -0,0 +1,19 @@ +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: admin@unkin.net + privateKeySecretRef: + name: letsencrypt-account-key + solvers: + - dns01: + # project must match the GCP project hosting the unkin.net Cloud DNS zone + cloudDNS: + project: REPLACE-WITH-GCP-PROJECT + serviceAccountSecretRef: + name: cert-manager-clouddns + key: key.json diff --git a/apps/base/cert-manager/kustomization.yaml b/apps/base/cert-manager/kustomization.yaml index f1fcada..d395bd2 100644 --- a/apps/base/cert-manager/kustomization.yaml +++ b/apps/base/cert-manager/kustomization.yaml @@ -5,6 +5,11 @@ kind: Kustomization resources: - namespace.yaml - serviceaccount.yaml + - serviceaccount_cert-manager-clouddns.yaml - clusterrole.yaml - clusterrolebinding.yaml - vmservicescrape.yaml + - vaultauth.yaml + - vaultstaticsecret_clouddns.yaml + - clusterissuer_letsencrypt.yaml + - clusterissuer_letsencrypt-staging.yaml diff --git a/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml b/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml new file mode 100644 index 0000000..5514800 --- /dev/null +++ b/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: cert-manager-clouddns + namespace: cert-manager + labels: + app.kubernetes.io/name: cert-manager-config + app.kubernetes.io/instance: cert-manager-config + app.kubernetes.io/component: clouddns-solver +automountServiceAccountToken: true diff --git a/apps/base/cert-manager/vaultauth.yaml b/apps/base/cert-manager/vaultauth.yaml new file mode 100644 index 0000000..a9a2f35 --- /dev/null +++ b/apps/base/cert-manager/vaultauth.yaml @@ -0,0 +1,18 @@ +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultAuth +metadata: + name: default + namespace: cert-manager +spec: + method: kubernetes + mount: k8s/au/syd1 + vaultConnectionRef: vso-system/default + allowedNamespaces: + - cert-manager + kubernetes: + role: cert_manager_clouddns + serviceAccount: cert-manager-clouddns + audiences: + - vault + tokenExpirationSeconds: 600 diff --git a/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml b/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml new file mode 100644 index 0000000..e7493a5 --- /dev/null +++ b/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml @@ -0,0 +1,15 @@ +--- +apiVersion: secrets.hashicorp.com/v1beta1 +kind: VaultStaticSecret +metadata: + name: clouddns + namespace: cert-manager +spec: + vaultAuthRef: default + mount: kv + type: kv-v2 + path: service/kubernetes/au/syd1/cert-manager/clouddns + refreshAfter: 5m + destination: + name: cert-manager-clouddns + create: true diff --git a/argocd/projects/platform.yaml b/argocd/projects/platform.yaml index 1eabece..da8742d 100644 --- a/argocd/projects/platform.yaml +++ b/argocd/projects/platform.yaml @@ -84,6 +84,8 @@ spec: kind: Certificate - group: 'cert-manager.io' kind: Issuer + - group: 'cert-manager.io' + kind: ClusterIssuer - group: 'gateway.networking.k8s.io' kind: GatewayClass - group: 'networking.k8s.io' -- 2.47.3 From 4434265cf65e3c4dc928e2b9117d2328d1002f10 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Sun, 2 Aug 2026 17:28:56 +1000 Subject: [PATCH 2/2] Switch Let's Encrypt ClusterIssuers to RFC2136/TSIG solver The clouddns approach is dropped in favour of self-delegating the ACME challenge to our own BIND: a one-time CNAME sends _acme-challenge.unkin.net into acme.unkin.net (served by bind-external), and cert-manager solves DNS-01 via RFC2136+TSIG against it. No GCP service account or Vault KV secret needed. - Replace the dns01 clouddns solver in both ClusterIssuers with rfc2136 (nameserver 198.18.199.53:53, key certmanager, HMACSHA256, secret certmanager-tsig reflected into the cert-manager namespace). - Remove the now-unneeded VaultAuth, VaultStaticSecret and clouddns ServiceAccount. Claude-Session: https://claude.ai/code/session_01JUoARVdmhxKQHyyyp1pxeT --- .../clusterissuer_letsencrypt-staging.yaml | 13 +++++++------ .../clusterissuer_letsencrypt.yaml | 13 +++++++------ apps/base/cert-manager/kustomization.yaml | 3 --- .../serviceaccount_cert-manager-clouddns.yaml | 11 ----------- apps/base/cert-manager/vaultauth.yaml | 18 ------------------ .../vaultstaticsecret_clouddns.yaml | 15 --------------- 6 files changed, 14 insertions(+), 59 deletions(-) delete mode 100644 apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml delete mode 100644 apps/base/cert-manager/vaultauth.yaml delete mode 100644 apps/base/cert-manager/vaultstaticsecret_clouddns.yaml diff --git a/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml b/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml index ee63acc..03e7600 100644 --- a/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml +++ b/apps/base/cert-manager/clusterissuer_letsencrypt-staging.yaml @@ -11,9 +11,10 @@ spec: name: letsencrypt-staging-account-key solvers: - dns01: - # project must match the GCP project hosting the unkin.net Cloud DNS zone - cloudDNS: - project: REPLACE-WITH-GCP-PROJECT - serviceAccountSecretRef: - name: cert-manager-clouddns - key: key.json + rfc2136: + nameserver: "198.18.199.53:53" + tsigKeyName: certmanager + tsigAlgorithm: HMACSHA256 + tsigSecretSecretRef: + name: certmanager-tsig + key: secret diff --git a/apps/base/cert-manager/clusterissuer_letsencrypt.yaml b/apps/base/cert-manager/clusterissuer_letsencrypt.yaml index fc9dd11..b2950c7 100644 --- a/apps/base/cert-manager/clusterissuer_letsencrypt.yaml +++ b/apps/base/cert-manager/clusterissuer_letsencrypt.yaml @@ -11,9 +11,10 @@ spec: name: letsencrypt-account-key solvers: - dns01: - # project must match the GCP project hosting the unkin.net Cloud DNS zone - cloudDNS: - project: REPLACE-WITH-GCP-PROJECT - serviceAccountSecretRef: - name: cert-manager-clouddns - key: key.json + rfc2136: + nameserver: "198.18.199.53:53" + tsigKeyName: certmanager + tsigAlgorithm: HMACSHA256 + tsigSecretSecretRef: + name: certmanager-tsig + key: secret diff --git a/apps/base/cert-manager/kustomization.yaml b/apps/base/cert-manager/kustomization.yaml index d395bd2..33e1bc4 100644 --- a/apps/base/cert-manager/kustomization.yaml +++ b/apps/base/cert-manager/kustomization.yaml @@ -5,11 +5,8 @@ kind: Kustomization resources: - namespace.yaml - serviceaccount.yaml - - serviceaccount_cert-manager-clouddns.yaml - clusterrole.yaml - clusterrolebinding.yaml - vmservicescrape.yaml - - vaultauth.yaml - - vaultstaticsecret_clouddns.yaml - clusterissuer_letsencrypt.yaml - clusterissuer_letsencrypt-staging.yaml diff --git a/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml b/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml deleted file mode 100644 index 5514800..0000000 --- a/apps/base/cert-manager/serviceaccount_cert-manager-clouddns.yaml +++ /dev/null @@ -1,11 +0,0 @@ ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cert-manager-clouddns - namespace: cert-manager - labels: - app.kubernetes.io/name: cert-manager-config - app.kubernetes.io/instance: cert-manager-config - app.kubernetes.io/component: clouddns-solver -automountServiceAccountToken: true diff --git a/apps/base/cert-manager/vaultauth.yaml b/apps/base/cert-manager/vaultauth.yaml deleted file mode 100644 index a9a2f35..0000000 --- a/apps/base/cert-manager/vaultauth.yaml +++ /dev/null @@ -1,18 +0,0 @@ ---- -apiVersion: secrets.hashicorp.com/v1beta1 -kind: VaultAuth -metadata: - name: default - namespace: cert-manager -spec: - method: kubernetes - mount: k8s/au/syd1 - vaultConnectionRef: vso-system/default - allowedNamespaces: - - cert-manager - kubernetes: - role: cert_manager_clouddns - serviceAccount: cert-manager-clouddns - audiences: - - vault - tokenExpirationSeconds: 600 diff --git a/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml b/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml deleted file mode 100644 index e7493a5..0000000 --- a/apps/base/cert-manager/vaultstaticsecret_clouddns.yaml +++ /dev/null @@ -1,15 +0,0 @@ ---- -apiVersion: secrets.hashicorp.com/v1beta1 -kind: VaultStaticSecret -metadata: - name: clouddns - namespace: cert-manager -spec: - vaultAuthRef: default - mount: kv - type: kv-v2 - path: service/kubernetes/au/syd1/cert-manager/clouddns - refreshAfter: 5m - destination: - name: cert-manager-clouddns - create: true -- 2.47.3