Skip to content

xds/rbac: use net.SplitHostPort in IP matchers to handle ip:port addresses#8992

Open
instantraaamen wants to merge 3 commits intogrpc:masterfrom
instantraaamen:fix/rbac-ip-matcher-splithost
Open

xds/rbac: use net.SplitHostPort in IP matchers to handle ip:port addresses#8992
instantraaamen wants to merge 3 commits intogrpc:masterfrom
instantraaamen:fix/rbac-ip-matcher-splithost

Conversation

@instantraaamen
Copy link

@instantraaamen instantraaamen commented Mar 20, 2026

Summary

remoteIPMatcher.match() and localIPMatcher.match() in internal/xds/rbac/matchers.go call netip.ParseAddr() on the string representation of net.Addr, which includes the port number in production (e.g. "10.0.0.5:8080"). Since netip.ParseAddr() only accepts bare IP addresses, the parse always fails on real TCP connections, causing all IP-based RBAC rules (DirectRemoteIp, RemoteIp, DestinationIp) to silently not match.

This fix uses net.SplitHostPort() to extract the host before calling netip.ParseAddr(), with a fallback for addresses that don't include a port. This is consistent with how the same file already handles address parsing for port matching in rbac_engine.go (line 232).

Fixes #8991

Root Cause

// Before (broken): netip.ParseAddr("10.0.0.5:8080") fails
func (sim *remoteIPMatcher) match(data *rpcData) bool {
    ip, _ := netip.ParseAddr(data.peerInfo.Addr.String())
    return sim.ipNet.Contains(ip)
}

In production, peer.Peer.Addr is a *net.TCPAddr whose String() returns "ip:port". The existing unit tests use a custom addr struct that returns only the bare IP, so the bug was not caught.

Changes

  • internal/xds/rbac/matchers.go: Add net.SplitHostPort() before netip.ParseAddr() in both remoteIPMatcher.match() and localIPMatcher.match()
  • internal/xds/rbac/ip_matcher_test.go: Add test cases using *net.TCPAddr to verify correct behavior with ip:port format addresses

Testing

All existing tests continue to pass. New test cases added for both remoteIPMatcher and localIPMatcher using *net.TCPAddr (production behavior):

=== RUN   Test/RemoteIPMatcherWithTCPAddr
    --- PASS: Test/RemoteIPMatcherWithTCPAddr/bare_IP_matching_CIDR (0.00s)
    --- PASS: Test/RemoteIPMatcherWithTCPAddr/TCPAddr_matching_CIDR (0.00s)
    --- PASS: Test/RemoteIPMatcherWithTCPAddr/bare_IP_not_matching_CIDR (0.00s)
    --- PASS: Test/RemoteIPMatcherWithTCPAddr/TCPAddr_not_matching_CIDR (0.00s)
    --- PASS: Test/RemoteIPMatcherWithTCPAddr/IPv6_TCPAddr_matching_CIDR (0.00s)
=== RUN   Test/LocalIPMatcherWithTCPAddr
    --- PASS: Test/LocalIPMatcherWithTCPAddr/bare_IP_matching_CIDR (0.00s)
    --- PASS: Test/LocalIPMatcherWithTCPAddr/TCPAddr_matching_CIDR (0.00s)
    --- PASS: Test/LocalIPMatcherWithTCPAddr/bare_IP_not_matching_CIDR (0.00s)
    --- PASS: Test/LocalIPMatcherWithTCPAddr/TCPAddr_not_matching_CIDR (0.00s)
PASS
ok  	google.golang.org/grpc/internal/xds/rbac	0.539s

RELEASE NOTES:

  • xds/rbac: Fix IP matchers to correctly parse ip:port addresses from net.Addr using net.SplitHostPort.

…esses

remoteIPMatcher.match() and localIPMatcher.match() call
netip.ParseAddr() on net.Addr.String(), which returns "ip:port" format
in production (*net.TCPAddr). Since netip.ParseAddr() only accepts bare
IP addresses, the parse always fails, causing all IP-based RBAC rules
to silently not match.

Use net.SplitHostPort() to extract the host before parsing, consistent
with how rbac_engine.go already handles address parsing for port
matching.

Add test cases using *net.TCPAddr to verify correct behavior with
ip:port format addresses.

Fixes grpc#8991

Made-with: Cursor
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Mar 20, 2026

CLA Signed
The committers listed above are authorized under a signed CLA.

@codecov
Copy link

codecov bot commented Mar 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.94%. Comparing base (12e91dd) to head (2dd19b3).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8992      +/-   ##
==========================================
- Coverage   83.04%   82.94%   -0.11%     
==========================================
  Files         411      411              
  Lines       32892    32900       +8     
==========================================
- Hits        27316    27288      -28     
- Misses       4181     4206      +25     
- Partials     1395     1406      +11     
Files with missing lines Coverage Δ
internal/xds/rbac/matchers.go 78.36% <100.00%> (+0.86%) ⬆️

... and 24 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@instantraaamen instantraaamen changed the title fix: use net.SplitHostPort in RBAC IP matchers to handle ip:port addresses xds/rbac: use net.SplitHostPort in IP matchers to handle ip:port addresses Mar 20, 2026
Replace net.ParseIP (disallowed by vet.sh) with
netip.MustParseAddr(...).AsSlice() in ip_matcher_test.go.

Made-with: Cursor
@instantraaamen
Copy link
Author

I hadn’t noticed #8990 when I opened this PR — sorry about the duplication.

For context, I had shared this privately yesterday and was later advised to open a public PR here, which is why I submitted #8992 today.

One thing I tried to add here is test coverage for the ip:port case with *net.TCPAddr. Happy to rework or consolidate this however maintainers prefer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

RBAC IP matchers always fail to match due to netip.ParseAddr receiving ip:port format

1 participant