From d310348fedf154729a11932b37c4a2b9d9a61e56 Mon Sep 17 00:00:00 2001 From: Daniele Sluijters Date: Thu, 6 Jul 2023 17:41:11 +0200 Subject: [PATCH] Add a way to allow for NAT64 mapped addresses This introduces the IPv6NAT64Prefix constant that can be passed to WithAllowedV6Prefixes if so desired. --- cmd/ssrfgen/templates/ssrf.tmpl | 5 +++++ ssrf.go | 11 +++++++++++ ssrf_gen.go | 5 +++++ ssrf_test.go | 12 ++++++++++++ 4 files changed, 33 insertions(+) diff --git a/cmd/ssrfgen/templates/ssrf.tmpl b/cmd/ssrfgen/templates/ssrf.tmpl index 95985f4..b1d56bd 100644 --- a/cmd/ssrfgen/templates/ssrf.tmpl +++ b/cmd/ssrfgen/templates/ssrf.tmpl @@ -42,6 +42,11 @@ var ( // assignments, i.e "the internet" IPv6GlobalUnicast = netip.MustParsePrefix("{{ .V6GlobalUnicast }}") + // IPv6NAT64Prefix is the prefix set aside for NAT64. This allows a server + // to only have an IPv6 address but still be able to talk to an IPv4-only + // server through DNS64+NAT64 + IPv6NAT64Prefix = netip.MustParsePrefix("64:ff9b::/96") + // IPv6DeniedPrefixes contains IPv6 special purpose IP prefixes from IANA // within the IPv6 Global Unicast range that we wish to block by default // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml diff --git a/ssrf.go b/ssrf.go index d2e722a..2063c56 100644 --- a/ssrf.go +++ b/ssrf.go @@ -48,6 +48,17 @@ func WithAllowedV4Prefixes(prefixes ...netip.Prefix) Option { // ranges outside of the global unicast range or connections to // otherwise denied prefixes within the global unicast range. // +// This function should be called with [IPv6NAT64Prefix] as one of the +// prefixes, if you run in an IPv6-only environment but provide IPv4 +// connectivity through a combination of DNS64+NAT64. The NAT64 prefix +// is outside of the IPv6 global unicast range and as such blocked by +// default. Allowing it is typically harmless in dual-stack setups as +// your clients need an explicit route for 64:ff9b::/96 configured which +// won't be the case by default. Beware that allowing this prefix may +// allow for an address like 64:ff9b::7f00:1, i.e 127.0.0.1 mapped to +// NAT64. A NAT64 gateway should drop this. Ideally a DNS64 server +// would never generate an address for an RFC1918 IP in an A-record. +// // This function overrides the allowed IPv6 prefixes, it does not accumulate. func WithAllowedV6Prefixes(prefixes ...netip.Prefix) Option { return func(g *Guardian) { diff --git a/ssrf_gen.go b/ssrf_gen.go index 9c9ee7c..41afe37 100644 --- a/ssrf_gen.go +++ b/ssrf_gen.go @@ -57,6 +57,11 @@ var ( // assignments, i.e "the internet" IPv6GlobalUnicast = netip.MustParsePrefix("2000::/3") + // IPv6NAT64Prefix is the prefix set aside for NAT64. This allows a server + // to only have an IPv6 address but still be able to talk to an IPv4-only + // server through DNS64+NAT64 + IPv6NAT64Prefix = netip.MustParsePrefix("64:ff9b::/96") + // IPv6DeniedPrefixes contains IPv6 special purpose IP prefixes from IANA // within the IPv6 Global Unicast range that we wish to block by default // https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml diff --git a/ssrf_test.go b/ssrf_test.go index f8c8ea0..62d36fa 100644 --- a/ssrf_test.go +++ b/ssrf_test.go @@ -76,6 +76,11 @@ func TestOptions(t *testing.T) { Options: []Option{WithAllowedV6Prefixes(netip.MustParsePrefix("2002::/8"))}, Result: &Guardian{networks: []string{"tcp4", "tcp6"}, ports: []uint16{80, 443}, allowedv6Prefixes: []netip.Prefix{netip.MustParsePrefix("2002::/8")}, deniedv4Prefixes: IPv4DeniedPrefixes, deniedv6Prefixes: IPv6DeniedPrefixes, strNetworks: "tcp4, tcp6", strPorts: "80, 443"}, }, + { + Name: "with allowed NAT64 prefix", + Options: []Option{WithAllowedV6Prefixes(IPv6NAT64Prefix)}, + Result: &Guardian{networks: []string{"tcp4", "tcp6"}, ports: []uint16{80, 443}, allowedv6Prefixes: []netip.Prefix{IPv6NAT64Prefix}, deniedv4Prefixes: IPv4DeniedPrefixes, deniedv6Prefixes: IPv6DeniedPrefixes, strNetworks: "tcp4, tcp6", strPorts: "80, 443"}, + }, { Name: "with multiple allowed v6 prefix calls", Options: []Option{WithAllowedV6Prefixes(netip.MustParsePrefix("2002::/23")), WithAllowedV6Prefixes(netip.MustParsePrefix("2002::/8"))}, @@ -155,6 +160,7 @@ func TestDefaultGuardian(t *testing.T) { {Addr: "invalid network", Network: "udp6", Err: ErrProhibitedNetwork}, {Addr: "invalid address", Network: "tcp4", Err: ErrInvalidHostPort}, {Addr: "[::ffff:129.144.52.38]:80", Network: "tcp6", Err: ErrProhibitedIP}, + {Addr: "[64:ff9b::7f00:1]:80", Network: "tcp6", Err: ErrProhibitedIP}, } s := New() @@ -224,6 +230,12 @@ func TestCustomGuardian(t *testing.T) { Addr: "[2001::1]:80", Network: "tcp6", }, + { + Name: "allow IPv6 NAT64 prefix", + Guardian: New(WithAllowedV6Prefixes(IPv6NAT64Prefix)), + Addr: "[64:ff9b::7f00:1]:80", + Network: "tcp6", + }, { Name: "deny IPv4 prefix", Guardian: New(WithDeniedV4Prefixes(netip.MustParsePrefix("8.8.8.0/24"))),