diff --git a/lib/restricted_http/private_network_guard.rb b/lib/restricted_http/private_network_guard.rb index 4226e35..193264f 100644 --- a/lib/restricted_http/private_network_guard.rb +++ b/lib/restricted_http/private_network_guard.rb @@ -20,20 +20,20 @@ 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 64:ff9b:1::/48 100::/64 2001::/32 2001:2::/48 2001:db8::/32 - 2002::/16 fec0::/10 ff00::/8 + ::/128 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 - # target in its low 32 bits; we 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") + # NAT64 prefixes: the well-known prefix (RFC 6052/6146) and the local-use + # prefix (RFC 8215). An address here embeds an IPv4 target in its low 32 + # bits; we 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_PREFIXES = [ + IPAddr.new("64:ff9b::/96"), + IPAddr.new("64:ff9b:1::/48") + ].freeze def resolve(hostname) Resolv.getaddress(hostname).tap do |ip| @@ -50,7 +50,7 @@ module RestrictedHTTP true elsif ipaddr.ipv4? disallowed_ipv4?(ipaddr) - elsif NAT64_WELL_KNOWN.include?(ipaddr) + elsif NAT64_PREFIXES.any? { |prefix| prefix.include?(ipaddr) } disallowed_ipv4?(embedded_ipv4(ipaddr)) else disallowed_ipv6?(ipaddr) diff --git a/test/lib/restricted_http/private_network_guard_test.rb b/test/lib/restricted_http/private_network_guard_test.rb index 81f11e3..336bf9d 100644 --- a/test/lib/restricted_http/private_network_guard_test.rb +++ b/test/lib/restricted_http/private_network_guard_test.rb @@ -74,9 +74,12 @@ 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 + test "private_ip? returns true for local-use NAT64 addresses embedding a private IPv4 (RFC8215)" do + assert_private_ip "64:ff9b:1::a00:1" # local-use NAT64 -> 10.0.0.1 + end + + test "private_ip? returns false for local-use NAT64 addresses embedding a public IPv4 (RFC8215)" do + assert_not RestrictedHTTP::PrivateNetworkGuard.private_ip?("64:ff9b:1::808:808") # -> 8.8.8.8 end test "private_ip? returns false for NAT64 addresses embedding a public IPv4" do