Files
cozystack/packages/system/cilium/values.yaml
T
Aleksei Sviridkin be2387323b feat(gateway)!: reserve a passthrough listener's name by the spec alone (#4638)
## What this PR does

This keeps the TLS-passthrough services on port 443 of the tenant
Gateway and makes the passthrough rules hold on Cilium 1.20.2. It is
stacked on #3343 and has to land with it (stack #4717).

On 1.20, upstream Cilium marks an HTTPS-terminate listener and a
TLS-passthrough listener on one port `Conflicted` when their hostnames
overlap, and drops both from Envoy. That took `*.<apex>` and the `api`,
`vm-exportproxy` and `cdi-uploadproxy` passthrough listeners down
together on every tenant Gateway in `dns01` and `existingSecret` mode
(#3912). #3343 now ships a patched operator that conflicts the pair only
when the hostnames are equal, so the layout stays as it is on main: one
Gateway, one address, everything on 443. What remains is the
equal-hostname case and the cleanup that 1.20 allows.

Under `http01`, a name a passthrough listener answers is now reserved by
the spec alone. Before, the controller dropped the terminate listener
for such a name only once a TLSRoute was attached to the passthrough
listener and its backend Service resolved, which copied how Cilium
builds filter chains. Without a working TLSRoute, an HTTPRoute on
`api.<apex>` kept its terminate listener next to `tls-api`, and on 1.20
that equal-hostname pair takes both listeners down. Now such an
HTTPRoute gets no listener and no certificate, TLSRoute or not. If all
its hostnames are reserved it reads `Accepted=False` with
`NoMatchingListenerHostname`. If it also claims a free hostname it stays
`Accepted` and is served there. The backend lookup, its Service and
ReferenceGrant watches and their RBAC rules are gone.

Other changes:

- `tlsPassthroughListeners` is accepted under `dns01` and
`existingSecret`. The old refusal relied on 1.19.5 serving all ports
from one Envoy listener; 1.20 splits ports whose SNI overlaps.
- A TenantGateway reads `Ready=False` with reason
`GatewayClassUnsupported` when its GatewayClass lists features in
`status.supportedFeatures` but not `TLSRoute`, while it renders
passthrough listeners. A class that lists nothing is not judged. Cilium
1.20.2 lists `TLSRoute`.
- Comments, the README and one error message no longer describe Cilium
1.19.5 as the shipped release.
- The gateway chart writes `tlsPassthroughServices` on the publishing
tenant only, behind the same `gateway.enabled` switch and
`publishing.exposedServices` filter as the platform TLSRoutes. Before,
every tenant Gateway got the three passthrough listeners with nothing
routed to them, and the reservation above would have taken those names
from a child tenant's own HTTPRoutes.

The uniform `allowedRoutes.kinds` on port 443 from #3087 stays. The
Cilium bug behind it is fixed since 1.19.6, but several tests pin it, so
removing it is a separate cleanup.

### Upgrade notes

Only the publishing tenant keeps the `api`, `vm-exportproxy` and
`cdi-uploadproxy` passthrough listeners. That is `tenant-root` unless
`publishing.ingressName` says otherwise, and only for names in
`publishing.exposedServices` while `gateway.enabled` is on. Other
tenants lose these listeners on upgrade. Nothing shipped was attached to
them, so a child tenant's own HTTPRoute on `api.<child-apex>` keeps its
listener and certificate.

Two narrow cases do change. On the publishing tenant under `http01`, an
HTTPRoute on an exposed passthrough name loses its listener and
certificate when the platform TLSRoute for that name forwards nowhere
(the owning package is not installed, or its backends do not resolve). A
TLSRoute attached by hand to a child tenant's `tls-*` listener loses its
parent. To keep that listener, merge-patch `tlsPassthroughServices` into
the tenant's `TenantGateway` after the upgrade. From then on the chart
does not render the key there, and later upgrades leave the patch alone.
`dns01` and `existingSecret` do not change: the wildcard listener still
covers such a name, and the passthrough listener answers it by its exact
name.

Tested locally: Go tests for the controller, `go vet`, helm-unittest for
`extra/gateway` and `cozystack-controller`, `make generate` and `make
manifests` with no diff. The gateway e2e scenario checks that a child
tenant renders no passthrough listener. It then declares `tls-api` and a
passthrough listener on a native port on that tenant, checks that the
tenant stays Ready with no route behind either, and checks the
reservation against `tls-api`. Unit tests catch a regression in either
change. In e2e, dropping the reservation turns the gateway suite red,
and dropping the chart scoping does not, because CI never sets
`gateway.enabled` (#4846). The controller was not run on a live cluster;
the operator patch it depends on was, see #3343.

### Downstream repositories

- [ ] No downstream repository is affected by this change
- [x] [cozystack/website](https://github.com/cozystack/website) -
follow-up: https://github.com/cozystack/website/issues/725
- [ ]
[cozystack/terraform-provider-cozystack](https://github.com/cozystack/terraform-provider-cozystack)
- follow-up:
- [ ]
[cozystack/ansible-cozystack](https://github.com/cozystack/ansible-cozystack)
- follow-up:
- [ ] [cozystack/ccp](https://github.com/cozystack/ccp) - follow-up:
- [ ] [cozystack/talm](https://github.com/cozystack/talm) - follow-up:
- [ ] [cozystack/cozyhr](https://github.com/cozystack/cozyhr) -
follow-up:
- [ ] [cozystack/cozy-proxy](https://github.com/cozystack/cozy-proxy) -
follow-up:
- [ ]
[cozystack/cozystack-telemetry-server](https://github.com/cozystack/cozystack-telemetry-server)
- follow-up:
- [ ]
[cozystack/external-apps-example](https://github.com/cozystack/external-apps-example)
- follow-up:
- [ ] [cozystack/examples](https://github.com/cozystack/examples) -
follow-up:
- [ ] [cozystack/community](https://github.com/cozystack/community) -
follow-up:

### Release note

```release-note
feat(gateway)!: the tenant Gateway renders the api, vm-exportproxy and cdi-uploadproxy passthrough listeners only on the publishing tenant, for the names publishing.exposedServices lists while gateway.enabled is on. Under certMode http01 a name a TLS-passthrough listener answers no longer gets an HTTPS listener on 443, with or without a TLSRoute behind it. On the publishing tenant, an HTTPRoute on such a name whose TLSRoute forwards nowhere loses its listener and certificate. A TLSRoute attached by hand to a tls-* listener of another tenant loses its parent. An HTTPRoute with some reserved and some free hostnames used to be rejected whole; it is now Accepted and its free hostnames get a listener and a certificate. tlsPassthroughListeners is accepted under dns01 and existingSecret. A TenantGateway whose GatewayClass does not list TLSRoute in its supported features reads Ready=False while it renders passthrough listeners.
```

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* TLS passthrough listeners are supported with HTTP-01, DNS-01, and
existing-secret certificate modes. Edge-terminated Gateways remain
unsupported.
* Declaring a passthrough hostname reserves it from HTTPS termination
and certificate creation, even when no route is attached.
* Passthrough listeners are rendered only for exposed services on the
enabled publishing Gateway.
* **Bug Fixes**
* Gateways that declare passthrough listeners but explicitly lack
TLSRoute support are now reported as not ready.
* Routes targeting a hostname served by a passthrough listener on a
different Gateway are correctly withdrawn.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-10-10 16:59:17 +03:00

150 lines
7.5 KiB
YAML

cilium:
# Required for the supported service-routing topology. Cozystack does not
# provide a supported alternative owner for ClusterIP translation when this
# is false; on kube-ovn, kube-proxy is absent and the OVN service load balancer
# is explicitly disabled. A false value can therefore break Service
# connectivity. tests/kube_proxy_replacement_test.yaml asserts the rendered
# value across the defaults and every variant valuesFiles combination.
#
# The source-side admission-webhook denies in packages/system/ingress-nginx
# and packages/system/kubeovn-webhook also rely on Cilium translating the
# Service before egress policy. Both policies retain a target-side ingressDeny,
# so even a second unsupported change that introduced another translator would
# not make the webhooks fail open.
#
# No test here can observe a live cluster's values, so an operator override in
# the field is caught only by templates/kube-proxy-replacement-beacon.yaml,
# which renders a status ConfigMap whenever this is not true. The beacon keeps
# a live unsupported override visible; it is not an alternative supported
# mode, and the required remediation is to restore true.
kubeProxyReplacement: true
# Upstream cilium with hostFirewall enabled and the IPv6 datapath disabled
# (ipv6.enabled=false, the default here) drops ALL node IPv6 before any
# policy evaluation ("Unsupported L3 protocol", DROP_UNKNOWN_L3 in
# bpf/bpf_host.c), breaking ICMPv6 Neighbor Discovery and BGP unnumbered on
# IPv6/L3 fabrics. The cozystack cilium image carries a BPF patch
# (images/cilium/patches/) that passes IPv6 to the kernel stack instead.
# Consequence: Cilium host policies apply to IPv4 only; node IPv6 is not
# filtered by Cilium.
hostFirewall:
enabled: true
hubble:
enabled: false
externalIPs:
enabled: true
nodePort:
enabled: true
loadBalancer:
algorithm: maglev
serviceTopology: true
ipam:
mode: "kubernetes"
image:
repository: ghcr.io/cozystack/cozystack/cilium
tag: v1.6.0
digest: "sha256:136a7dfff4d6adcd448424c2354144160028a8e96eb6558b917fd3ee6d7b39fe"
envoy:
enabled: true
# Keep the per-type xDS the 1.19 agent and envoy speak. The 1.20 chart
# defaults to ADS, which a 1.19 agent never serves and a 1.20 agent in
# ADS mode serves exclusively, so while the agent and envoy DaemonSets
# roll, a node with mixed versions gives envoy no config. Moving to ADS
# needs a step of its own once every node runs 1.20.
xdsMode: split
# Upstream default is {} — no requests/limits. The embedded envoy DaemonSet
# idles at ~100 MB RAM/node and grows under L7 traffic; without requests
# the DaemonSet schedules onto any node with no reserved headroom, and
# without a memory limit a runaway listener could OOM the node.
# The baseline matches the commented-out example in the upstream Helm values.
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
memory: 1Gi
# The cilium-agent DaemonSet had the same gap the envoy DaemonSet above
# already closed. Beyond the node-headroom argument, a memory limit is what
# sets memory.max on the pod cgroup, and the Talos userspace OOM handler
# (v1.12+) only ever selects cgroups that lack it — so an unlimited agent is
# an eviction candidate on every node regardless of what caused the pressure.
#
# The limit has to hold the agent's BPF maps as well as its heap. Read at
# cilium 1.19.5: the CT, NAT, neighbor and socket rev-NAT maps are sized as a
# share of total node RAM (bpf-map-dynamic-size-ratio, 0.0025 by default),
# each capped at 16Mi entries (LimitTableMax). They are LRU hash maps, which
# the kernel always preallocates in full, and since Linux 5.11 that memory is
# charged to the memcg of the process that creates them, i.e. this container.
# A map reused from bpffs stays charged to whoever created it, so a limit too
# small for the maps first fails on the start that recreates them: after a
# reboot, or when an upgrade changes a map's size or layout. Policy maps do
# not scale with RAM: they are LPM tries sized by bpf.policyMapMax that
# allocate per entry.
#
# One DaemonSet serves every node, so the limit cannot follow node RAM and
# covers the map ceiling instead: just under 20Gi of kernel memory with every
# map at 16Mi entries for both IPv4 and IPv6 (IPv6 is off by default, but
# turning it on must not bring the OOM back), plus 4Gi for the agent itself,
# the figure from the upstream values' commented example. A limit is a
# ceiling, not a reservation, so it costs no schedulable memory. What it does
# cost is the backstop against a leak in the agent itself: on every node the
# agent is stopped at 24Gi minus its own map memory, which on most nodes is
# no practical backstop. memory.max still keeps the pod out of the Talos
# handler's victim set, which is what the limit is for. Pinning the map sizes
# instead would cap connection capacity on large nodes and, on upgrade,
# recreate the maps and drop their conntrack and NAT state.
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
memory: 24Gi
# Shared by the agent's init containers (config, mount-cgroup, mount-bpf-fs,
# apply-sysctl-overwrites, clean-cilium-state). All are short-lived setup
# steps, but each one still has to carry a limit for the pod cgroup to get
# memory.max at all.
initResources:
requests:
cpu: 10m
memory: 32Mi
limits:
memory: 256Mi
gatewayAPI:
enabled: true
# Upstream default is false — Envoy then advertises no ALPN protocols on
# TLS-terminating Gateway listeners, and browsers (which negotiate HTTP/2
# exclusively through TLS ALPN "h2") silently fall back to HTTP/1.1 on
# every request. ingress-nginx, which the Gateway API path replaces,
# advertised h2 out of the box, so false here is a client-visible
# regression, not a neutral default. With true, ALPN offers h2 then
# http/1.1 — clients without HTTP/2 keep negotiating http/1.1 unchanged.
# This only affects the client↔gateway hop: gateway↔backend stays
# HTTP/1.1 unless a Service opts in per GEP-1911 via appProtocol
# kubernetes.io/h2c (enableAlpn also switches that support on; no
# cozystack-shipped Service declares such a value today).
# Platform-managed clusters can flip this through the platform chart's
# gateway.http2 value, which overrides this default either way.
enableAlpn: true
rollOutCiliumPods: true
operator:
# A rebuild of the upstream operator carrying the Go patches in
# images/cilium-operator/patches/: it accepts an HTTPS listener and a TLS
# passthrough listener on one port when the passthrough hostname is
# strictly narrower, and serves such a port from one Envoy listener.
# `make image` stamps the ref; until it does, the committed value is the
# unpatched upstream operator the rebuild starts from, so a tree rendered
# without a build still pulls an image that exists.
image:
override: "quay.io/cilium/operator-generic:v1.20.2@sha256:64d8798350e8569b8e7622563fed6e44dce2625f311e4651b774816516c744fc"
rollOutPods: true
replicas: 1
resources:
limits:
cpu: 1000m
memory: 1Gi
requests:
cpu: 100m
memory: 128Mi
# kubectl and jq for the hook Jobs in templates/ipam-migration.yaml.
ipamMigration:
image: docker.io/alpine/k8s:1.36.2@sha256:44ef4942e171939b9c665a4a84beb80e2dcdb9a24330d4651cfdfd2e9deecc47