docs: correct the publishing reference and document the existingSecret cert mode#619
Conversation
✅ Deploy Preview for cozystack ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Review limit reached
Next review available in: 44 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request updates the Cozystack documentation for both the 'next' and 'v1.5' versions to detail the new 'existingSecret' certificate mode, which allows using an operator-provided wildcard Secret instead of ACME. It also documents the 'publishing.exposureClass' configuration as the successor to the deprecated 'publishing.exposure' setting. The review feedback suggests improving clarity and consistency by using full spec paths (such as 'spec.ingress: true' and 'spec.gateway: true') instead of shorthand terms in the documentation.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
|
|
||
| - **Inheriting children** (the default, no `spec.gateway`) have no Gateway of their own. Their `*.<child-apex>` listener is rendered on the owner's Gateway and bound to the Secret in the *owner's* namespace, so coverage depends entirely on that Secret's SAN list — a bare `*.<apex>` does not match `*.<child-apex>`, and clients of the child subdomain are served the owner's certificate and see a hostname mismatch. Replicating the Secret into the child's namespace fixes nothing here, because no listener reads it; the SANs must cover each child apex. | ||
| - **Children that own a Gateway** (`spec.gateway: true`) render their own `TenantGateway`, inherit the Secret *name* through the cluster values channel, and resolve it in **their own** namespace. Gateway API does allow a listener to reference a Secret in another namespace, via a `ReferenceGrant`, but the controller does not use that route: it renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference is always local. There the Secret does have to be replicated, or the tenant is left without a certificate. | ||
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. |
There was a problem hiding this comment.
For consistency with the previous bullet point (which uses spec.gateway: true), it is clearer to refer to the tenant-level ingress setting as spec.ingress: true instead of ingress: true.
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. | |
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`spec.ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. |
|
|
||
| - **Inheriting children** (the default, no `spec.gateway`) have no Gateway of their own. Their `*.<child-apex>` listener is rendered on the owner's Gateway and bound to the Secret in the *owner's* namespace, so coverage depends entirely on that Secret's SAN list — a bare `*.<apex>` does not match `*.<child-apex>`, and clients of the child subdomain are served the owner's certificate and see a hostname mismatch. Replicating the Secret into the child's namespace fixes nothing here, because no listener reads it; the SANs must cover each child apex. | ||
| - **Children that own a Gateway** (`spec.gateway: true`) render their own `TenantGateway`, inherit the Secret *name* through the cluster values channel, and resolve it in **their own** namespace. Gateway API does allow a listener to reference a Secret in another namespace, via a `ReferenceGrant`, but the controller does not use that route: it renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference is always local. There the Secret does have to be replicated, or the tenant is left without a certificate. | ||
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. |
There was a problem hiding this comment.
For consistency with the previous bullet point (which uses spec.gateway: true), it is clearer to refer to the tenant-level ingress setting as spec.ingress: true instead of ingress: true.
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. | |
| - **Children on the ingress-nginx path** (`gateway.enabled=false`, the default) are hit hardest. The Secret name reaches every tenant, and the app and system ingress templates drop their per-host ACME annotation as soon as it is non-empty — but the ingress chart passes `default-ssl-certificate` only to the *publishing* controller. A child running its own ingress controller (`spec.ingress: true`) therefore ends up with neither: its apps have no certificate of their own, and its controller has no default one, so ingress-nginx serves its built-in self-signed certificate for every host in that tenant. Replicating the Secret does not help — the flag is gated on the namespace, not on the Secret. This is tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set `wildcardSecretName` on a cluster whose child tenants run their own ingress controllers. |
| | `publishing.certificates.issuerName` | `"letsencrypt-prod"` | `ClusterIssuer` name for TLS certificates used in system Helm releases. | | ||
| | `publishing.certificates.wildcard` | `false` | Opt-in shared wildcard certificate on the default ingress-nginx path (`gateway.enabled=false`). When `true` with `solver=dns01` and no `publishing.certificates.wildcardSecretName`, the platform issues one `*.<root-host>` + `<root-host>` `Certificate` via the DNS-01 `ClusterIssuer` and serves it as the ingress controller's default SSL certificate, so system services stop minting a per-host ACME certificate each — avoiding Let's Encrypt rate limits at scale, at parity with the Gateway API path. Ignored on `http01` (cannot issue wildcards) and when `gateway.enabled=true` (the `TenantGateway` controller issues the wildcard there). Coverage caveat: a single-label wildcard does not cover a custom service host outside `*.<root-host>` (e.g. a keycloak `ingress.host` on another domain) or a child tenant's nested host (`<service>.<tenant>.<root-host>`); those services fall back to the default certificate, which would not cover them. Enabling it propagates the issued wildcard Secret name through the cluster values channel that child tenants inherit, so this is not root-tenant scoped — only enable it when every exposed host is covered, or supply a covering `publishing.certificates.wildcardSecretName` instead. Off by default so a `dns01` cluster is never switched silently on upgrade. | | ||
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates; only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Takes precedence over `publishing.certificates.wildcard` — when set, no ACME issuance happens. Leave empty to keep ACME issuance. | | ||
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates; only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Takes precedence over `publishing.certificates.wildcard` — when set, no ACME `Certificate` is issued for these hosts and no per-tenant `Issuer` is created (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/next/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | |
There was a problem hiding this comment.
To avoid confusion with platform-level settings (such as gateway.enabled), it is clearer to refer to tenant-level settings using their full spec paths (e.g., spec.gateway: true and spec.ingress: true).
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates; only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Takes precedence over `publishing.certificates.wildcard` — when set, no ACME `Certificate` is issued for these hosts and no per-tenant `Issuer` is created (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/next/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | | |
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates; only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Takes precedence over `publishing.certificates.wildcard` — when set, no ACME `Certificate` is issued for these hosts and no per-tenant `Issuer` is created (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `spec.gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `spec.gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`spec.ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/next/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | |
| | `publishing.exposure` | `"externalIPs"` | Exposure mode for the ingress-nginx Service. Possible values: `externalIPs`, `loadBalancer`. The default writes `Service.spec.externalIPs` from `publishing.externalIPs`; `loadBalancer` switches to `Service.type: LoadBalancer` and a `CiliumLoadBalancerIPPool` over the same IPs (with `externalTrafficPolicy: Local` to preserve client source IP). `Service.spec.externalIPs` is deprecated upstream in v1.36 (KEP-5707); plan to switch to `loadBalancer` before upgrading past Kubernetes v1.40 when the `AllowServiceExternalIPs` feature gate flips off. The `loadBalancer` mode requires Cilium L2/BGP announcements to reach the IP from outside the cluster (off by default in cozystack), and at least one address in `publishing.externalIPs` (otherwise render fails). | | ||
| | `publishing.certificates.solver` | `"http01"` | ACME challenge solver type for default letsencrypt issuer. Possible values: `http01`, `dns01`. | | ||
| | `publishing.certificates.issuerName` | `"letsencrypt-prod"` | `ClusterIssuer` name for TLS certificates used in system Helm releases. | | ||
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates, and no per-tenant `Issuer` or `Certificate` is created for them (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way); only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/v1.5/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | |
There was a problem hiding this comment.
To avoid confusion with platform-level settings (such as gateway.enabled), it is clearer to refer to tenant-level settings using their full spec paths (e.g., spec.gateway: true and spec.ingress: true).
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates, and no per-tenant `Issuer` or `Certificate` is created for them (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way); only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/v1.5/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | | |
| | `publishing.certificates.wildcardSecretName` | `""` | Operator-provided wildcard TLS Secret. When set, platform services and the root tenant's ingress/Gateway serve under this pre-existing Secret instead of minting per-host ACME certificates, and no per-tenant `Issuer` or `Certificate` is created for them (`solver` / `issuerName` still govern the cluster-wide `ClusterIssuer`s, which are rendered either way); only the NAME travels through the platform values channel, never the certificate or private key. The Secret must already exist in the publishing namespace (`tenant-root` by default — the namespace running the root ingress controller selected by `publishing.ingressName`), be of type `kubernetes.io/tls`, and cover the served hosts (typically `*.<root-host>` + `<root-host>`). Supported for the root tenant only — but not enforced: the Secret NAME is distributed through the cluster values channel that every tenant inherits, so enabling it also affects children, which splits into three cases. An inheriting child (no `spec.gateway: true`) has no Gateway of its own — its `*.<child-apex>` listener is rendered on the owner's Gateway against this same Secret, so the SAN list must cover every child apex, or those clients are served the owner's certificate and see a hostname mismatch (replicating the Secret into the child's namespace does nothing there). A child with `spec.gateway: true` owns a Gateway and resolves the Secret in **its own** namespace — the controller renders `certificateRefs` without a namespace and issues no `ReferenceGrant`, so the reference never crosses namespaces — and the Secret must be replicated there or that tenant gets no certificate at all. On the default ingress-nginx path (`gateway.enabled=false`) a child running its own ingress controller (`spec.ingress: true`) gets neither a per-host ACME certificate nor the operator Secret: its apps drop the ACME annotation cluster-wide, while `default-ssl-certificate` is passed only to the publishing controller, so ingress-nginx serves its built-in self-signed certificate for that tenant's hosts — and replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug, tracked as [cozystack/cozystack#3296](https://github.com/cozystack/cozystack/issues/3296); until it is fixed, do not set this on a cluster whose child tenants run their own ingress controllers. See [Gateway API → Certificates]({{% ref "/docs/v1.5/networking/gateway-api#certificates" %}}). Leave empty to keep ACME issuance. | |
2abf956 to
fa89b7f
Compare
The platform-package tables documented publishing.exposure. The key was added, then reverted before v1.5 shipped, and never returned. Drop the stale row from the v1.5 and next references, note the removal for anyone upgrading from v1.4 with the key still set, and drop the cross-reference on the Gateway API pages, which additionally asserted externalTrafficPolicy: Local for the legacy ingress path. That holds only when publishing.externalIPs is empty; with external IPs set — the usual bare-metal install — the host ingress Service is a ClusterIP with externalTrafficPolicy: Cluster and NATs client source IPs exactly like the Gateway path does. Document publishing.exposureClass, the successor key on the next branch, which the reference omitted entirely: the backends it maps to, the guard that fails the render when a pool or cloud backend is combined with a non-empty publishing.externalIPs, and the real deprecation timeline — AllowServiceExternalIPs defaults to false from Kubernetes v1.40, so the migration has to happen before that upgrade, not after it. Document the cert mode that publishing.certificates.wildcardSecretName selects. The Gateway API pages described a binary HTTP-01 / DNS-01 choice throughout — intro, controller responsibilities, listener layout, migration runbook, rollback and threat model all assumed a per-tenant Issuer and Certificate always exist. A third mode ships: with an operator-provided wildcard Secret the controller mints neither and garbage collects what it previously owned, though the ACME account private key outlives the switch. The cluster-wide ClusterIssuers are unaffected — they render from the solver either way. Spell out what the option does to a tenant tree, in all three cases rather than only the two on the Gateway path. The Secret name — never the key material — rides the cluster values channel, so an inheriting child gets a *.<child-apex> listener on the owner's Gateway and needs SAN coverage, while a child owning a Gateway resolves the Secret in its own namespace and needs it replicated there. On the default ingress path a child running its own ingress controller gets neither certificate and is served the built-in self-signed one from ingress-nginx; that case is an open bug rather than a constraint an operator can configure around, and the docs now say so and point at the tracker. Root-tenant scope is the supported configuration and is not enforced by any guard. Assisted-By: Claude <noreply@anthropic.com> Signed-off-by: Aleksei Sviridkin <f@lex.la>
fa89b7f to
461e842
Compare
Split out of #615 so the corrections below get reviewed as what they are — behavioural claims about the TenantGateway controller and the publishing path — rather than riding along in a typo fix. Both the v1.5 and the
nextsnapshots are updated.publishing.exposureis goneThe reference documented
publishing.exposure. That key was added and then reverted (revert(ingress)!: remove broken publishing.exposure=loadBalancer mode) before v1.5 shipped: it exists inrelease-1.4and in no branch since. An operator on v1.5 following the table set a key that is silently ignored. The row is removed from both snapshots, along with a stale cross-reference to it on the Gateway API pages, and a note tells v1.4 upgraders to drop it. The v1.4 docs are deliberately left alone — there the key is real.That cross-reference also asserted
externalTrafficPolicy: Localfor the legacy ingress path. It is onlyLocalwhenpublishing.externalIPsis empty; with external IPs set — the usual bare-metal install — the host ingress Service is aClusterIPwithexternalTrafficPolicy: Clusterand NATs client source IPs exactly like the Gateway path. Corrected.publishing.exposureClassis documentedRemoving the dead key left
nextwith no documented way to publish the host ingress at all. Its successor,publishing.exposureClass, is now in the reference: the four backends and what each renders, the guard that fails the render when a pool or cloud backend meets a non-emptypublishing.externalIPs, and the KEP-5707 deprecation phasing (the later release numbers are approximate upstream, and the text says so rather than inventing precision).The third cert mode
The Gateway API pages described a binary HTTP-01 / DNS-01 choice throughout — intro, controller responsibilities, listener layout, migration runbook, rollback, threat model and known limitations all assumed a per-tenant
IssuerandCertificatealways exist. A third mode ships and is selected bypublishing.certificates.wildcardSecretName: the controller points listeners at an operator-supplied wildcard Secret, mints neitherIssuernorCertificate, and garbage-collects what it previously owned. It now has its own section, and the surrounding prose no longer contradicts it. Two consequences that were undocumented:Issuerreferencing it is collected. Onnexta post-delete hook removes it when the gateway release goes away — not when the mode changes; in v1.5 nothing removes it at all.What the option does to a tenant tree
The Secret name — never the key material — rides the cluster values channel that every tenant inherits, so this is not root-scoped in practice even though root is the only supported configuration. Three cases, with different remedies:
*.<child-apex>listener on the owner Gateway against the same Secret, so the SAN list must cover each child apex.certificateRefswithout a namespace and issues noReferenceGrant, so the reference never crosses namespaces — and the Secret must be replicated there.default-ssl-certificatereaches only the publishing controller, so ingress-nginx serves its built-in self-signed certificate. Replicating the Secret does not help, because the flag is gated on the namespace. That is an open bug — A non-empty wildcard Secret name disables ACME for every tenant but only certifies one ingress controller — child tenants silently serve a fake certificate cozystack#3296 — and the docs say so rather than presenting it as a constraint to configure around.Verification
Built with the Hugo version CI uses, in both environments — production (1491 pages) and development (1694 pages, the only one that renders
next/, which production excludes). Every claim above was checked againstrelease-1.5andmainrather than inferred.