Block IPv6 addresses that reach internal IPs in the SSRF guard#2988
Conversation
The guard blocked the usual private, loopback, and link-local ranges (and the IPv4-mapped/-compatible IPv6 forms) and CGNAT, but let through NAT64, 6to4, and Teredo addresses, which can point at an internal IPv4. Now it pulls the IPv4 out of a NAT64 address and checks that (so NAT64 to a public site still works), blocks 6to4 and Teredo outright, and adds the missing IPv4 and IPv6 ranges.
There was a problem hiding this comment.
Pull request overview
This PR strengthens SsrfProtection by expanding the set of blocked IP ranges and closing IPv6 transition-mechanism gaps (NAT64/6to4/Teredo) that can be used to reach internal IPv4 targets via IPv6 addresses.
Changes:
- Extracts and re-checks embedded IPv4s from well-known NAT64 IPv6 addresses.
- Blocks 6to4 and Teredo IPv6 ranges outright and expands IPv4/IPv6 special-use range blocking.
- Adds test coverage for NAT64, 6to4, Teredo, and additional IPv6 special-use cases.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| app/models/ssrf_protection.rb | Expands blocked special-use ranges and adds NAT64/6to4/Teredo-aware blocking logic. |
| test/models/ssrf_protection_test.rb | Adds regression tests covering NAT64 embedded IPv4 checks and additional IPv6 transition/special-use cases. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Well-known NAT64 prefix (RFC 6052/6146). An address here embeds an IPv4 | ||
| # target in its low 32 bits; extract it and re-check against the IPv4 rules so | ||
| # NAT64 to a public address still resolves while NAT64 to an internal address | ||
| # is blocked. | ||
| NAT64_WELL_KNOWN = IPAddr.new("64:ff9b::/96") |
There was a problem hiding this comment.
Done in 95b2d43 — NAT64_PREFIXES now covers the RFC 8215 local-use prefix 64:ff9b:1::/48 alongside the well-known prefix, with the same embedded-IPv4 recheck and regression tests for both the private-embed (blocked) and public-embed (allowed) cases.
|
🤖 One gap worth closing while you're in here: Suggest extending the well-known-prefix match (or the Repro address: |
The local-use NAT64 prefix (64:ff9b:1::/48, RFC 8215) embeds an IPv4 target in its low 32 bits just like the well-known prefix, so run it through the same embedded-IPv4 recheck: local-use NAT64 to a public address still resolves while local-use NAT64 to an internal address is blocked. Also block the IPv6 benchmarking range (2001:2::/48, RFC 5180) to match the IPv4 benchmarking block on 198.18.0.0/15.
|
Fixed in 95b2d43. Also added |
The SSRF guard blocked private, loopback, and link-local ranges (and the
IPv4-mapped/-compatible IPv6 forms) and CGNAT, but let through NAT64, 6to4,
and Teredo addresses, which can carry an internal IPv4.
This pulls the IPv4 out of a NAT64 address and checks that (so NAT64 to a
public site still works), blocks 6to4 and Teredo outright, and adds the
missing IPv4 and IPv6 special-use ranges.