diff --git a/lib/restricted_http/private_network_guard.rb b/lib/restricted_http/private_network_guard.rb index b9ea957..4226e35 100644 --- a/lib/restricted_http/private_network_guard.rb +++ b/lib/restricted_http/private_network_guard.rb @@ -20,8 +20,13 @@ module RestrictedHTTP # fetch target, so they are blocked outright. ULA (fc00::/7, incl. the AWS # IMDSv6 address fd00:ec2::254), link-local, and loopback are covered by the # predicates in #disallowed_ipv6?. + # The local-use NAT64 prefix (64:ff9b:1::/48, RFC 8215) also embeds an + # IPv4 target, but at a deployment-chosen position we can't extract, and it + # is only meaningful inside the network that deployed it, so it is blocked + # outright rather than handled like NAT64_WELL_KNOWN below. DISALLOWED_IPV6 = %w[ - ::/128 100::/64 2001::/32 2001:db8::/32 2002::/16 fec0::/10 ff00::/8 + ::/128 64:ff9b:1::/48 100::/64 2001::/32 2001:2::/48 2001:db8::/32 + 2002::/16 fec0::/10 ff00::/8 ].map { |cidr| IPAddr.new(cidr) }.freeze # Well-known NAT64 prefix (RFC 6052/6146). An address here embeds an IPv4 diff --git a/test/lib/restricted_http/private_network_guard_test.rb b/test/lib/restricted_http/private_network_guard_test.rb index f70500b..81f11e3 100644 --- a/test/lib/restricted_http/private_network_guard_test.rb +++ b/test/lib/restricted_http/private_network_guard_test.rb @@ -74,6 +74,11 @@ class RestrictedHTTP::PrivateNetworkGuardTest < ActiveSupport::TestCase assert_private_ip "64:ff9b::a00:5" # NAT64 -> 10.0.0.5 end + test "private_ip? returns true for local-use NAT64 addresses (RFC8215)" do + assert_private_ip "64:ff9b:1:fffe::a00:1" # local-use NAT64 embedding 10.0.0.1 + assert_private_ip "64:ff9b:1::808:808" # blocked even when embedding a public IP + end + test "private_ip? returns false for NAT64 addresses embedding a public IPv4" do # DNS64 legitimately synthesizes these for public sites on IPv6-only hosts. assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("64:ff9b::808:808") # -> 8.8.8.8 @@ -90,9 +95,10 @@ class RestrictedHTTP::PrivateNetworkGuardTest < ActiveSupport::TestCase assert_private_ip "fe80::1" end - test "private_ip? returns true for IPv6 multicast and documentation ranges" do + test "private_ip? returns true for IPv6 multicast, documentation, and benchmarking ranges" do assert_private_ip "ff02::1" assert_private_ip "2001:db8::1" + assert_private_ip "2001:2::1" # benchmarking (RFC5180), matches 198.18.0.0/15 end test "private_ip? returns false for public IPv6 addresses" do