Files
cluster-platform-v3/templates/cluster-issuer.yaml

56 lines
2.4 KiB
YAML

{{- if .Values.tenant.domain }}
# letsencrypt-prod ClusterIssuer — DNS-01 challenge via Cloudflare.
#
# Multi-zone: the solver has NO `selector.dnsZones` restriction. The
# tenant's Cloudflare token typically covers many zones (a tenant with
# 41 owned domains is normal); we let cert-manager pick whichever zone
# matches the requested host. The token's access is the natural
# boundary — if it can't write a zone, the challenge fails loudly.
#
# Earlier the solver was scoped to `.Values.tenant.domain` only, which
# made instances on ANY other tenant-owned domain unable to issue (the
# `app.havari.me` symptom on a tenant whose primary domain is
# `4th.online`). Dropping the selector unifies single-zone and
# multi-zone tenants under one issuer.
#
# The cloudflare-api-token Secret is NOT in this chart. Tower
# kubectl-applies it into cert-manager ns at Connect time using the
# tenant's per-tenant Vault credential (v3/tenants/<id>/cloudflare-token).
# The chart references it by name only.
#
# Sync wave 1 (Slice 2B.1.1, 2026-05-04). cert-manager itself
# installs at the default wave 0; Argo waits for ALL wave-0
# resources (cert-manager Deployments + webhook Service) to be
# Healthy before applying wave 1. Without this we hit a race:
# Argo applied this ClusterIssuer in the same wave as cert-manager
# Deployments → cert-manager-webhook wasn't Ready yet → admission
# webhook rejected the resource → Argo backed off exponentially
# 30-90s before retrying. retries=2 was the smoking gun in the
# demo-server105 timing analysis (3 min ready instead of ~45 s).
#
# Note ordering: ClusterIssuer at wave 1, Certificate at wave 2
# (in tenants-wildcard-cert.yaml) — Certificate references the
# ClusterIssuer by name, so the resource graph also reflects the
# logical dependency.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
annotations:
argocd.argoproj.io/sync-wave: "1"
labels:
app.kubernetes.io/managed-by: cluster-platform-v3
spec:
acme:
email: {{ required "acme.email is required" .Values.acme.email | quote }}
server: {{ .Values.acme.server | quote }}
privateKeySecretRef:
name: letsencrypt-prod-account-key
solvers:
- dns01:
cloudflare:
apiTokenSecretRef:
name: {{ .Values.secrets.cloudflareTokenSecret.name | quote }}
key: {{ .Values.secrets.cloudflareTokenSecret.key | quote }}
{{- end }}