feat: add allowICMP annotation for port-filter mode#12
Conversation
Introduce networking.cozystack.io/allowICMP. When set to "true" on a service in port-filter mode (wholeIP: "false"), ICMP traffic toward the backend pod IP is accepted instead of being dropped by the per-port filter. Without this opt-in, port-filter mode silently drops all ICMP, which breaks ping, PMTU discovery (ICMP "fragmentation needed") and ICMP unreachable signalling. Implementation: - New nft set icmp_allowed_pods (key: ipv4 pod IP). - New rule in port_filter, between the ct established,related accept and the per-port drop: ip daddr @icmp_allowed_pods meta l4proto icmp accept. - New ProxyProcessor methods EnsureICMPAllow / DeleteICMPAllow / CleanupICMPAllow, mirroring the port-filter API. - Controller refactored to consolidate port-filter + ICMP-allow handling into reconcilePortFilter / clearPortFilter helpers. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Andrei Kvapil <kvapss@gmail.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughThis change introduces an optional ICMP allowlist feature for port-filter networking mode. The implementation adds service annotation support ( Changes
Sequence DiagramsequenceDiagram
participant Svc as Service with<br/>allowICMP annotation
participant Ctrl as Services<br/>Controller
participant PP as ProxyProcessor
participant NFT as nftables<br/>port_filter chain
Svc->>Ctrl: Add/Update Service
Ctrl->>Ctrl: Extract allowICMP<br/>annotation value
alt allowICMP: "true" & port-filter mode
Ctrl->>PP: EnsureICMPAllow(svcIP, podIP)
PP->>NFT: Add podIP to<br/>icmp_allowed_pods set
NFT->>NFT: ICMP packets to podIP<br/>match accept rule
else allowICMP absent/false & port-filter mode
Ctrl->>PP: DeleteICMPAllow(svcIP, podIP)
PP->>NFT: Remove podIP from<br/>icmp_allowed_pods set
NFT->>NFT: ICMP packets to podIP<br/>hit drop rule
end
Svc->>Ctrl: Delete Service
Ctrl->>PP: CleanupICMPAllow(keep map)
PP->>NFT: Reconcile set:<br/>remove unlisted entries
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
✨ Finishing Touches📝 Generate docstrings
🧪 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 introduces the networking.cozystack.io/allowICMP annotation, enabling ICMP traffic for services in port-filter mode to support ping and Path MTU Discovery. The ServicesController has been refactored to use reconcilePortFilter and clearPortFilter helpers for managing port-filtering and ICMP rules. Additionally, the NFTProxyProcessor now includes an icmp_allowed_pods nftables set and a rule to accept ICMP traffic for authorized pods. I have no feedback to provide.
## What this PR does The `vm-instance` chart now drives the cozy-proxy `wholeIP` and `allowICMP` annotations explicitly so that `externalMethod: PortList` actually filters ingress traffic to declared ports while keeping ping/PMTU functional. - Render `networking.cozystack.io/wholeIP: "false"` when `externalMethod: PortList` (was always `"true"`, which silently disabled the PortList semantics in cozy-proxy). - Add `externalAllowICMP` value (default `true`) propagated as `networking.cozystack.io/allowICMP` when `externalMethod: PortList`. Without this, cozy-proxy drops ICMP in port-filter mode (ping/PMTU broken). Operators can set `externalAllowICMP: false` to opt out. The changelog entry is intentionally **not** part of this PR — it will be added in a dedicated `docs: add changelog for vX.Y.Z` commit at release time, per project convention. ## Why `externalMethod: PortList` is documented as filtering ingress traffic to declared ports but has been non-functional on Cozystack v1.3.0 — verified empirically on a 3-node Talos lab. Root cause was twofold: chart always set `wholeIP: "true"`, and cozy-proxy v0.2.0 had no port-aware logic. The cozy-proxy side was fixed in cozystack/cozy-proxy#11 (merged) and cozystack/cozy-proxy#12 (allowICMP follow-up); this PR completes the user-visible fix on the chart side. ## Companion PRs - cozystack/cozy-proxy#11 (merged) — per-service ingress port filtering - cozystack/cozy-proxy#12 (merged) — `allowICMP` annotation for port-filter mode ## Test plan - [x] Built cozy-proxy with the companion fix locally, deployed on a 3-node Talos lab (Cozystack v1.3.0) - [x] `wholeIP: "false"` Service with `spec.ports: [22]`: only port 22 reachable from outside; ports 80/443/8080/9999 filtered - [x] WholeIP-annotated Service unchanged: all listening ports reachable - [x] Egress IP preservation works in both modes (TCP curl + UDP DNS) - [x] `nft list table ip cozy_proxy` confirms expected ruleset - [x] `helm template` renders the expected annotation matrix: `PortList` default → `wholeIP=false, allowICMP=true`; `PortList` opt-out → `allowICMP=false`; `WholeIP` → only `wholeIP=true` - [x] `make unit-tests` passes locally - [ ] CI unit tests - [ ] CI E2E ## Backport Suggesting `backport-v1.3` once merged. ## Release note ```release-note [vm-instance] Make `externalMethod: PortList` actually filter ingress traffic to ports listed in `externalPorts`. New `externalAllowICMP` knob (default true) propagates the cozy-proxy `allowICMP` annotation to keep ping/PMTU functional in port-filter mode. Combined with cozy-proxy v0.3.0+, only listed ports plus ICMP are reachable from the VM's LoadBalancer IP. ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added `externalAllowICMP` configuration option to control ICMP traffic acceptance for VM external access in PortList mode (enabled by default). * **Documentation** * Updated parameter documentation to include the new ICMP traffic control setting. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
Summary
Follow-up to #11. In port-filter mode the previous PR drops all ICMP toward the backend pod IP, which breaks
ping, PMTU discovery (ICMP "fragmentation needed"), and ICMP unreachable signalling. This adds an opt-in annotation to accept ICMP for services that need it.networking.cozystack.io/allowICMP: "true"(default false). Only meaningful in port-filter mode (wholeIP: "false").icmp_allowed_podsand a rule inport_filterplaced between thect established,related acceptand the per-port drop:ip daddr @icmp_allowed_pods meta l4proto icmp accept.ProxyProcessormethodsEnsureICMPAllow/DeleteICMPAllow/CleanupICMPAllow, mirroring the port-filter API shape.reconcilePortFilter/clearPortFilterhelpers — fewer scattered if/else blocks for both port-filter and ICMP state.allowICMPpredicate.Why per-pod set, not per-rule
Same reason as
filtered_pods/allowed_ports: the chain runs at priorityfilter(0), afteringress_dnathas rewritten daddr from LB IP to pod IP. Keying on pod IP keeps the chain rule count constant and the per-service state in nft sets, which is consistent with the rest of the design.Test plan
wholeIP: "false"+allowICMP: "true": ping VM works, port filter still in effect for TCP/UDPwholeIP: "false"withoutallowICMP: ICMP dropped (default)wholeIP: "true": passthrough, ICMP worksallowICMPtrue→false: pod removed fromicmp_allowed_podsicmp_allowed_podsentrySummary by CodeRabbit
Release Notes
New Features
networking.cozystack.io/allowICMPservice annotation to selectively allow ICMP traffic to managed pods in port-filter mode.Documentation