diff --git a/apps/overlays/upc-dev/forte-drop/wildcard-drop-tls-certificate.yaml b/apps/overlays/upc-dev/forte-drop/wildcard-drop-tls-certificate.yaml index fde30ec..d83faf5 100644 --- a/apps/overlays/upc-dev/forte-drop/wildcard-drop-tls-certificate.yaml +++ b/apps/overlays/upc-dev/forte-drop/wildcard-drop-tls-certificate.yaml @@ -1,18 +1,20 @@ --- # Wildcard TLS cert for the per-slug drop subdomains: .drop.forteapps.net. # forte_drop serves forte-login drops on their own subdomain (gated by the auth -# sidecar), so each drop needs a valid cert for *.drop.forteapps.net. +# sidecar), so each drop needs a valid cert for *.drop.forteapps.net — a name the +# existing *.forteapps.net wildcard CANNOT cover (TLS wildcards match one label only). # -# Issued DIRECTLY into the forte-drop namespace (not cert-manager) so the app's -# Traefik IngressRoute — which must reference a TLS secret in its OWN namespace — -# can use it without cross-namespace cloning. The secret-cloner Kyverno policy -# can't help here: it only clones on NEW namespace creation (generateExisting:false) -# and forte-drop already exists. +# Scope: this cert covers ONLY *.drop.forteapps.net. The apex drop.forteapps.net is +# NOT included here — it is served by the forteapp chart's own Certificate (secret +# forte-drop-tls, dnsNames: [drop.forteapps.net]) and/or the existing *.forteapps.net +# wildcard, so adding it here would be redundant. # -# This is the SINGLE issuer of secret `wildcard-drop-forteapps-net-tls`. The -# forte-helm chart must reference this secret VERBATIM and must NOT also create a -# Certificate into the same secret (else cert-manager thrashes). The dns01 solver -# in letsencrypt-prod is authorized for these names via its selector.dnsNames. +# Issued DIRECTLY into the forte-drop namespace (not via the chart) so the app's +# Traefik IngressRoute — which must reference a TLS secret in its OWN namespace — can +# use it without cross-namespace cloning. This is the single issuer of secret +# wildcard-drop-forteapps-net-tls; the forte-drop-subdomains IngressRoute references +# that secret. The letsencrypt-prod dns01 solver is authorized for this name via its +# selector.dnsZones (forteapps.net). apiVersion: cert-manager.io/v1 kind: Certificate metadata: @@ -25,7 +27,6 @@ spec: kind: ClusterIssuer dnsNames: - '*.drop.forteapps.net' # per-slug forte drop subdomains - - 'drop.forteapps.net' # apex (admin + /shared public drops) duration: 2160h0m0s # 90 days renewBefore: 720h0m0s # renew 30 days before expiry privateKey: diff --git a/cluster-resources/letsencrypt-issuer.yaml b/cluster-resources/letsencrypt-issuer.yaml index 2f1689b..480b193 100644 --- a/cluster-resources/letsencrypt-issuer.yaml +++ b/cluster-resources/letsencrypt-issuer.yaml @@ -24,12 +24,15 @@ spec: name: azuredns-config key: client-secret selector: - dnsNames: - # *.forteapps.net only matches single-label children, NOT *.drop.forteapps.net, - # so the per-drop subdomain wildcard needs its own selector entry. - - '*.drop.forteapps.net' - - 'drop.forteapps.net' - - '*.forteapps.net' + # NOTE: cert-manager solver selectors are NOT TLS-style wildcards. selector.dnsNames + # matches by exact FQDN, so '*.forteapps.net' here would match only a cert literally + # named '*.forteapps.net' — it would NOT cover 'drop.forteapps.net'. selector.dnsZones + # instead suffix-matches the zone apex AND every subdomain at any depth, so this single + # entry routes all forteapps.net ACME challenges (forteapps.net, *.forteapps.net, + # drop.forteapps.net, *.drop.forteapps.net, mcp.drop.forteapps.net, ...) through this + # Azure dns01 solver. Wildcard names require dns01; non-wildcard names that ever fail + # to match fall through to the http01 solver below. + dnsZones: - 'forteapps.net' # HTTP-01 fallback for non-wildcard certificates - http01: @@ -62,12 +65,15 @@ spec: name: azuredns-config key: client-secret selector: - dnsNames: - # *.forteapps.net only matches single-label children, NOT *.drop.forteapps.net, - # so the per-drop subdomain wildcard needs its own selector entry. - - '*.drop.forteapps.net' - - 'drop.forteapps.net' - - '*.forteapps.net' + # NOTE: cert-manager solver selectors are NOT TLS-style wildcards. selector.dnsNames + # matches by exact FQDN, so '*.forteapps.net' here would match only a cert literally + # named '*.forteapps.net' — it would NOT cover 'drop.forteapps.net'. selector.dnsZones + # instead suffix-matches the zone apex AND every subdomain at any depth, so this single + # entry routes all forteapps.net ACME challenges (forteapps.net, *.forteapps.net, + # drop.forteapps.net, *.drop.forteapps.net, mcp.drop.forteapps.net, ...) through this + # Azure dns01 solver. Wildcard names require dns01; non-wildcard names that ever fail + # to match fall through to the http01 solver below. + dnsZones: - 'forteapps.net' # HTTP-01 fallback for non-wildcard certificates - http01: