Repository navigation
VPC ACL: apply every CIDR of a multi-CIDR rule, in order - #14350
Open
bhouse-nexthop wants to merge 2 commits into
Open
bhouse-nexthop wants to merge 2 commits into
bhouse-nexthop wants to merge 2 commits into
Conversation
A VPC network ACL rule may carry several CIDRs, which reach the VR as a
comma-separated string. Both ways the VR renders them are broken.
iptables (the normal VPC path): the rule went out as a single
"-A ACL_INBOUND_ethX -s a,b ..." line. iptables expands that into one rule
per address, but CsNetfilters.compare() counts the entry as one rule when it
works out where to insert the next ACL rule ahead of the chain's DROP. Every
later rule is therefore inserted one slot too high, between the expanded
rules, and all but the last CIDR of each multi-CIDR rule drift down behind
any explicit deny. An allow silently stops matching for those CIDRs; a
multi-CIDR deny placed ahead of an allow stops denying.
nftables (IPv6, and IPv4 in routed mode): the rule was built as
"ip6 saddr a,b ...", which nft rejects with a syntax error. The failure is
only logged, so the rule is simply missing.
Emit one iptables rule per CIDR so the count stays right, and render a
multi-CIDR nft match as a set, "{a,b}", the way the firewall rules in the same
file already do.
Fixes: apache#12668
Signed-off-by: Brad House <bhouse@nexthop.ai>
Collaborator
Author
|
@vladimirpetrov @sureshanaparti could you take a look at this one? We'd like this fix to make it into the upcoming 4.22.2 release. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## 4.22 #14350 +/- ##
============================================
- Coverage 18.02% 18.02% -0.01%
+ Complexity 16250 16245 -5
============================================
Files 5936 5936
Lines 535823 535823
Branches 65612 65612
============================================
- Hits 96582 96556 -26
- Misses 428242 428270 +28
+ Partials 10999 10997 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
An empty element in the cidr list ("a,,b") would now become its own
iptables rule, fail, and still be counted by CsNetfilters.compare(),
pushing every later ACL rule behind the DROP. The API never produces one,
but skip it rather than rely on that.
Render the nft set as "{ a, b }" rather than "{a,b}": the unspaced form is
only safe because /bin/sh on the VR is dash, and bash would brace-expand it
into a syntax error.
Also cover the egress path and the IPv4 half of a mixed-family rule in the
tests.
Signed-off-by: Brad House <bhouse@nexthop.ai>
3 of 12 tasks
Contributor
|
Hello, this seems to do the work. Is there any chance for this to be applied to systemvm for 4.23 ? As far as i know there is no 4.23 systemvm. Maybe a 4.22 image with only this patch could make it to a 4.23? |
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
A VPC network ACL rule can carry several CIDRs. On the VR those rules are either applied in the wrong order or not applied at all, and nothing is reported back to the UI or API.
Fixes #12668
Why it breaks
IPv4, iptables (normal VPC):
AclDevice.process()turns each ACL rule into one fw entry, e.g.-A ACL_INBOUND_eth3 -s 1.2.3.4/32,2.3.4.5/32 -p tcp --dport 22 -j ACCEPT. iptables expands a comma-separated address into one rule per address, so that line creates two rules.CsNetfilters.compare()inserts each ACL rule at the chain's current rule count (just ahead of the trailingDROP), and bumps that count by one per fw entry, not by the number of rules iptables actually created. After a multi-CIDR rule the count is short byn-1, so every later rule is inserted between the expanded rules. All but the last CIDR of each multi-CIDR rule drift down behind every rule that follows, including an explicit deny.The effect: an allow silently stops working for every CIDR but one, and a multi-CIDR deny placed ahead of an allow (e.g. "block RFC1918, then allow 0.0.0.0/0") stops denying. Single-CIDR rules are unaffected. A lone multi-CIDR rule also looks fine, since there is nothing after it to land in the gap, which is why this can be hard to reproduce. Both ingress (
filter) and egress (mangle) chains are affected.IPv6, and IPv4 in routed mode (nftables):
__process_ip6()and__process_routing_ip4()buildip6 saddr a,b .../ip saddr a,b .... nft rejects a bare comma list:The rules are added one
nft add ruleat a time and the failure is only logged, so each multi-CIDR rule is silently missing while the rest of the list loads.How it is fixed
AclDevice.process()emits one fw entry per CIDR, in list order, skipping any empty element. Each entry is then exactly one iptables rule, so the insert positioncompare()tracks stays correct. (This is simpler and safer than teachingcompare()how iptables expands-s/-d. It also lifts a limit: a single-swith several hundred CIDRs fails outright withiptables: Message too long.)ip saddr { a, b }, via a smallnftAddrSet()helper. The routed-network firewall rules (add_routing_rules) andCsIpv6Firewallin the same file already use nft sets forsource_cidr_list. The set is written with spaces so that no shell can brace-expand it on its way tonft. nft also accepts overlapping or duplicate elements in it and merges them.How to reproduce the old behaviour
1.2.3.4/32,2.3.4.5/323.4.5.6/320.0.0.0/0iptables -S ACL_INBOUND_ethXfor that tier's interface.Before (the first CIDR of rule 1 sits behind the explicit deny and never matches):
After:
For the IPv6 side, give the tier an IPv6 CIDR and add a rule with two IPv6 CIDRs. Before the fix it is absent from
nft list chain ip6 ip6_acl ethX_ingress_policy, and/var/log/cloud.logshows thenft add rulefailing with a non-zero exit status (nft's syntax error itself goes to stderr). After the fix it is listed asip6 saddr { a, b } ....Until this lands, the workaround is one CIDR per ACL rule.
Types of changes
Feature/Enhancement Scale or Bug Severity
Bug Severity
How Has This Been Tested?
systemvm/test/TestCsAcl.pycovers iptables ingress and egress, the IPv4 and IPv6 halves of a mixed-family rule, routed IPv4, and empty list elements. The multi-CIDR tests fail on the current4.22code and all pass with this change. (Nothing in.github/workflowsrunssystemvm/test, so these run viasystemvm/test/runtests.sh.)AclDevicegenerates through the unmodifiedCsNetfilters().compare()against real iptables (nf_tables backend) in a network namespace. Re-applying the same ACL a second time leaves the chain unchanged.nft add rulefor bothipandip6on nft 1.0.6 (the version in the Debian 12 systemvm template) and 1.0.9, including overlapping, duplicate and0.0.0.0/0/::/0elements. The old bare-list form was confirmed to fail.pycodestyleandpylint(as insystemvm/test/runtests.sh) report nothing new.How did you try to break this feature and the system with this change?
removeUndesiredCidrs). Each family only gets its own CIDRs, and a single-CIDR rule produces exactly the same output as before.all) are unchanged.