What happened?
TLS Secrets referenced only by Gateway API ListenerSets are not synchronized into the configured cilium-secrets namespace when the Secret already exists before the ListenerSet is created.
The ListenerSets report Accepted=True, Programmed=True, and ResolvedRefs=True. Cilium also generates a CiliumEnvoyConfig containing the expected TLS filter chains and SDS secret names. However, the referenced cilium-sync-secret-* resources do not exist, so Envoy resets TLS connections during ClientHello.
Restarting the Cilium operator causes its initial Secret watch to reconcile the existing Secrets. All synchronized Secrets are then created immediately and HTTPS starts working without restarting Envoy.
Expected behavior
Creating or updating a ListenerSet should enqueue all TLS Secrets in spec.listeners[].tls.certificateRefs for synchronization, regardless of whether the Secret or ListenerSet was created first. A listener should also not report Programmed=True if its required SDS Secret is unavailable.
Reproduction
- Install the ListenerSet CRD before starting the Cilium operator.
- Enable Gateway API secret synchronization with
gatewayAPI.secretsNamespace.sync=true.
- Create a Gateway that accepts ListenerSets.
- Create a valid
kubernetes.io/tls Secret in an application namespace.
- Wait until the Secret exists.
- Create a ListenerSet in that namespace whose HTTPS listener references the existing Secret.
- Attach an HTTPRoute to the ListenerSet.
- Observe that the ListenerSet becomes Accepted, ResolvedRefs, and Programmed.
- Observe that the generated CiliumEnvoyConfig references a hashed Secret in
cilium-secrets, but that Secret was not created.
- A TLS request resets during ClientHello.
- Restart the Cilium operator. The synchronized Secret is created and the same request succeeds.
This was reproduced with five ListenerSets/Secrets across multiple namespaces. The source Secrets were created several minutes before the ListenerSets. After one operator restart, all five synchronized Secrets appeared at the same timestamp and all HTTPS listeners became reachable.
Suspected cause
SecretSyncHandler.IsReferencedByGateway uses helpers.GetGatewaysForSecret, which accounts for ListenerSets through ListenerSetSecretIndex. Thus a source Secret event can discover its ListenerSet reference.
However, SecretSyncHandler.EnqueueTLSSecrets only accepts *gatewayv1.Gateway and walks gw.Spec.Listeners. The secret-sync registration watches Gateway reference objects, but does not appear to add ListenerSet as an additional watch that enqueues ListenerSet certificate references. Therefore creating the ListenerSet after the source Secret produces no secret-sync reconciliation event.
Relevant source:
The same relevant code appears unchanged on main at the time of filing.
Workaround
Restart the Cilium operator, or update the source TLS Secret after the ListenerSet exists, to trigger Secret reconciliation.
Environment
- Cilium operator:
quay.io/cilium/operator-generic:v1.20.0
- Cilium commit reported by the running components:
450c5314
- Kubernetes server:
v1.36.2
- ListenerSet storage API version:
gateway.networking.k8s.io/v1
enable-gateway-api=true
enable-gateway-api-secrets-sync=true
gateway-api-secrets-namespace=cilium-secrets
What happened?
TLS Secrets referenced only by Gateway API ListenerSets are not synchronized into the configured
cilium-secretsnamespace when the Secret already exists before the ListenerSet is created.The ListenerSets report
Accepted=True,Programmed=True, andResolvedRefs=True. Cilium also generates a CiliumEnvoyConfig containing the expected TLS filter chains and SDS secret names. However, the referencedcilium-sync-secret-*resources do not exist, so Envoy resets TLS connections during ClientHello.Restarting the Cilium operator causes its initial Secret watch to reconcile the existing Secrets. All synchronized Secrets are then created immediately and HTTPS starts working without restarting Envoy.
Expected behavior
Creating or updating a ListenerSet should enqueue all TLS Secrets in
spec.listeners[].tls.certificateRefsfor synchronization, regardless of whether the Secret or ListenerSet was created first. A listener should also not reportProgrammed=Trueif its required SDS Secret is unavailable.Reproduction
gatewayAPI.secretsNamespace.sync=true.kubernetes.io/tlsSecret in an application namespace.cilium-secrets, but that Secret was not created.This was reproduced with five ListenerSets/Secrets across multiple namespaces. The source Secrets were created several minutes before the ListenerSets. After one operator restart, all five synchronized Secrets appeared at the same timestamp and all HTTPS listeners became reachable.
Suspected cause
SecretSyncHandler.IsReferencedByGatewayuseshelpers.GetGatewaysForSecret, which accounts for ListenerSets throughListenerSetSecretIndex. Thus a source Secret event can discover its ListenerSet reference.However,
SecretSyncHandler.EnqueueTLSSecretsonly accepts*gatewayv1.Gatewayand walksgw.Spec.Listeners. The secret-sync registration watches Gateway reference objects, but does not appear to add ListenerSet as an additional watch that enqueues ListenerSet certificate references. Therefore creating the ListenerSet after the source Secret produces no secret-sync reconciliation event.Relevant source:
The same relevant code appears unchanged on
mainat the time of filing.Workaround
Restart the Cilium operator, or update the source TLS Secret after the ListenerSet exists, to trigger Secret reconciliation.
Environment
quay.io/cilium/operator-generic:v1.20.0450c5314v1.36.2gateway.networking.k8s.io/v1enable-gateway-api=trueenable-gateway-api-secrets-sync=truegateway-api-secrets-namespace=cilium-secrets