mirror of
https://github.com/basecamp/once-campfire.git
synced 2026-08-07 15:28:45 +09:00
Re-check the IPv4 embedded in local-use NAT64 instead of blocking outright
Matches the fizzy guard: 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 now resolves (keeping unfurls working for self-hosters on such networks) while local-use NAT64 to an internal address stays blocked.
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user