network-policies: allow ICMP error-class between pods (CCNP)

Closes the `policy-verdict:none ... DENIED (ICMPv4
DestinationUnreachable(Port))` drops Hubble was showing for
home-assistant <-> coredns and similar. When a UDP query lands on a
closed coredns port (SO_REUSEPORT race, draining pod), the receiver
sends ICMP DU back; without this CCNP the sender's namespace
default-deny CNP drops the ICMP error and the kernel just keeps
retransmitting.

Scoped via matchExpressions Exists on `io.kubernetes.pod.namespace`
so reserved-identity endpoints (host, remote-node, world, health,
kube-apiserver) are excluded — only real pods are widened. Allows
only the error class (DU + TimeExceeded, v4 + v6); ICMP Echo is
NOT allowed by this policy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-20 10:37:54 +02:00
co-authored by Claude Opus 4.7
parent fa0d1e9c84
commit 7f95486abe
2 changed files with 61 additions and 0 deletions
@@ -0,0 +1,60 @@
# When a UDP/TCP packet hits a closed port on a pod (e.g. coredns
# pod that's draining behind a Service, or any SO_REUSEPORT race),
# the receiver replies with an ICMP error message back to the
# sender — ICMPv4 Destination-Unreachable (type 3) /
# Time-Exceeded (type 11), ICMPv6 Destination-Unreachable (type 1) /
# Time-Exceeded (type 3). Without this, the sender never learns the
# destination was unreachable and the kernel keeps retransmitting.
#
# Hubble shows the symptom as e.g.
# `home-assistant/home-assistant <> kube-system/coredns
# policy-verdict:none ... DENIED (ICMPv4 DestinationUnreachable(Port))`
# even though the actual UDP/53 query reaches coredns fine.
#
# This CCNP ingress-allows ICMP error-class FROM any pod TO any pod,
# scoped via matchExpressions Exists on the namespace label so it
# does NOT match reserved-identity endpoints (host, remote-node,
# world, health, kube-apiserver). Egress is symmetric — pods need
# to be able to SEND ICMP errors too (e.g. their own closed ports).
#
# Note: this only widens permissions for the ICMP error class. UDP
# Echo (Type 0/8 / 128/129) is NOT allowed by this policy.
apiVersion: cilium.io/v2
kind: CiliumClusterwideNetworkPolicy
metadata:
name: allow-icmp-errors-between-pods
spec:
endpointSelector:
matchExpressions:
- key: io.kubernetes.pod.namespace
operator: Exists
ingress:
- fromEndpoints:
- matchExpressions:
- key: io.kubernetes.pod.namespace
operator: Exists
icmps:
- fields:
- type: 3 # IPv4 Destination Unreachable
family: IPv4
- type: 11 # IPv4 Time Exceeded
family: IPv4
- type: 1 # IPv6 Destination Unreachable
family: IPv6
- type: 3 # IPv6 Time Exceeded
family: IPv6
egress:
- toEndpoints:
- matchExpressions:
- key: io.kubernetes.pod.namespace
operator: Exists
icmps:
- fields:
- type: 3
family: IPv4
- type: 11
family: IPv4
- type: 1
family: IPv6
- type: 3
family: IPv6
@@ -18,3 +18,4 @@ resources:
- rook-ceph-ns.yaml
- velero-ns.yaml
- allow-cilium-health.yaml
- allow-icmp-errors.yaml