mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-06 12:03:39 +09:00
## What this PR does On the default ingress-nginx path (`gateway.enabled=false`) every published hostname still minted its own per-host ACME certificate via ingress-shim, even with a DNS-01 solver configured. That hits the Let's Encrypt rate limit (50 certs per registered domain per week) once a deployment exceeds ~50 endpoints. The Gateway API path already issues a single per-apex wildcard in DNS-01 mode; this brings the ingress-nginx path to parity. It implements the remaining ingress-nginx gap that #2400 was narrowed to in its triage comment (DNS-01 is already multi-provider, and the Gateway path already issues wildcards). The new `publishing.certificates.wildcard` toggle is opt-in and OFF by default. When set to `true` with `solver=dns01` and no operator-provided wildcard Secret, the platform issues one shared wildcard `Certificate` for `<root-host>` + `*.<root-host>` and serves the resulting Secret as the publishing controller's `--default-ssl-certificate`. The system service Ingresses (dashboard, grafana, keycloak, harbor, …) then stop requesting a per-host cert. How it works: - `core/platform` `apps.yaml` computes the effective wildcard Secret name and a new `_cluster.wildcard-issue` signal. An operator-provided `wildcardSecretName` (the BYO path) always wins and never triggers issuance; HTTP-01 cannot issue wildcards; and when Gateway API is enabled the TenantGateway controller issues the wildcard instead — so all three are excluded from auto-issuance. - `extra/ingress` renders the wildcard `Certificate` only on the publishing controller (`Release.Namespace == expose-ingress`), because the Secret must be same-namespace for ingress-nginx to read it. The Certificate references the DNS-01 `ClusterIssuer` that `cert-manager-issuers` already renders in `dns01` mode, so no per-tenant Issuer is minted. This reuses the existing wildcard-secret consumption path, so no per-service Ingress template changes were needed — only the issuance side is new. Why opt-in (default off): the chosen wildcard Secret name rides the `_cluster` channel, which every child tenant inherits verbatim, so this is not root-tenant scoped. A single-label wildcard `*.<root-host>` covers `<service>.<root-host>` but not a custom service host outside it (a keycloak `ingress.host`, harbor `host`, or grafana `host` pointed at another domain) nor a child tenant's nested host (`<service>.<tenant>.<root-host>`). Enabling issuance makes every such service drop its per-host ACME cert and fall back to the default certificate, which does not cover it. Leaving it off by default means a dns01 cluster is never silently switched on upgrade; an operator who enables it accepts the same coverage responsibility as the operator-provided wildcard path, which propagates identically. The tradeoff is pinned by contract tests (keycloak custom host, bucket nested child-tenant host). Per-tenant wildcards remain on the Gateway path and are tracked separately. Docs: cozystack/website#588 Closes #2400. Part of #2811. ### Screenshots N/A — no UI changes. ### Release note ```release-note feat(platform): add opt-in shared wildcard certificate issuance on the default ingress-nginx path via publishing.certificates.wildcard (default false). When enabled with a DNS-01 solver, the platform issues one *.<root-host> wildcard for system services instead of a per-host ACME certificate, avoiding Let's Encrypt rate limits at scale. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added automatic wildcard certificate issuance support for DNS-01 ACME solver. * New `certificates.wildcard` configuration option to enable shared wildcard TLS certificates. * Ingress rules now properly utilize wildcard certificates when enabled. * **Tests** * Added comprehensive test suites validating wildcard certificate generation and ingress integration across multiple scenarios. <!-- end of auto-generated comment: release notes by coderabbit.ai -->