Skip to content

Commit

Permalink
fix: allow calling internal IP ranges with relevant option
Browse files Browse the repository at this point in the history
The `ResilientClient` options `ResilientClientDisallowInternalIPs`
and `ResilientClientAllowInternalIPRequestsTo` were not allowing to call
certain IP ranges, like 100.64.0.0/10 properly.
  • Loading branch information
David-Wobrock committed Aug 21, 2024
1 parent 52f7d77 commit e39baf3
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 18 deletions.
47 changes: 44 additions & 3 deletions httpx/resilient_client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net/url"
"sync/atomic"
"testing"
"time"

"github.com/hashicorp/go-retryablehttp"
"github.com/stretchr/testify/assert"
Expand All @@ -33,11 +34,13 @@ func TestNoPrivateIPs(t *testing.T) {

allowedURL := "http://localhost:" + port + "/foobar"
allowedGlob := "http://localhost:" + port + "/glob/*"
allowedPrivateIP := "http://100.64.1.1:80" + "/private"

c := NewResilientClient(
ResilientClientWithMaxRetry(1),
ResilientClientWithMaxRetry(0),
ResilientClientWithConnectionTimeout(50*time.Millisecond),
ResilientClientDisallowInternalIPs(),
ResilientClientAllowInternalIPRequestsTo(allowedURL, allowedGlob),
ResilientClientAllowInternalIPRequestsTo(allowedURL, allowedGlob, allowedPrivateIP),
)

for i := 0; i < 10; i++ {
Expand All @@ -49,13 +52,51 @@ func TestNoPrivateIPs(t *testing.T) {
"http://localhost:" + port + "/glob/bar": true,
"http://localhost:" + port + "/glob/bar/baz": false,
"http://localhost:" + port + "/FOOBAR": false,
allowedPrivateIP: true,
"http://100.64.8.8:" + port + "/route": false,
} {
_, err := c.Get(destination)
if !passes {
require.Errorf(t, err, "dest = %s", destination)
assert.Containsf(t, err.Error(), "is not a permitted destination", "dest = %s", destination)
} else if err != nil {
assert.NotContainsf(t, err.Error(), "is not a permitted destination", "dest = %s", destination)
}
}
}
}

func TestAllowPrivateIPs(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
_, _ = w.Write([]byte("Hello, world!"))
}))
t.Cleanup(ts.Close)

target, err := url.ParseRequestURI(ts.URL)
require.NoError(t, err)

_, port, err := net.SplitHostPort(target.Host)
require.NoError(t, err)

c := NewResilientClient(
ResilientClientWithMaxRetry(0),
ResilientClientWithConnectionTimeout(50*time.Millisecond),
)

for i := 0; i < 10; i++ {
for destination, handled := range map[string]bool{
"http://127.0.0.1:" + port: true,
"http://localhost:" + port: true,
"http://192.168.178.5:" + port: false,
"http://localhost:" + port + "/glob/bar": true,
"http://100.64.1.1:" + port + "/route": false,
} {
_, err = c.Get(destination)
if handled {
require.NoError(t, err)
} else {
require.NoErrorf(t, err, "dest = %s", destination)
require.Error(t, err)
assert.NotContainsf(t, err.Error(), "is not a permitted destination", "dest = %s", destination)
}
}
}
Expand Down
19 changes: 4 additions & 15 deletions httpx/ssrf.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"net"
"net/http"
"net/http/httptrace"
"net/netip"
"time"

"code.dny.dev/ssrf"
Expand Down Expand Up @@ -88,15 +87,10 @@ func init() {
ssrf.WithAnyPort(),
ssrf.WithNetworks("tcp4", "tcp6"),
ssrf.WithAllowedV4Prefixes(
netip.MustParsePrefix("10.0.0.0/8"), // Private-Use (RFC 1918)
netip.MustParsePrefix("127.0.0.0/8"), // Loopback (RFC 1122, Section 3.2.1.3))
netip.MustParsePrefix("169.254.0.0/16"), // Link Local (RFC 3927)
netip.MustParsePrefix("172.16.0.0/12"), // Private-Use (RFC 1918)
netip.MustParsePrefix("192.168.0.0/16"), // Private-Use (RFC 1918)
ssrf.IPv4DeniedPrefixes...,
),
ssrf.WithAllowedV6Prefixes(
netip.MustParsePrefix("::1/128"), // Loopback (RFC 4193)
netip.MustParsePrefix("fc00::/7"), // Unique Local (RFC 4193)
ssrf.IPv6DeniedPrefixes...,
),
).Safe
allowInternalAllowIPv6 = otelTransport(t)
Expand All @@ -108,15 +102,10 @@ func init() {
ssrf.WithAnyPort(),
ssrf.WithNetworks("tcp4"),
ssrf.WithAllowedV4Prefixes(
netip.MustParsePrefix("10.0.0.0/8"), // Private-Use (RFC 1918)
netip.MustParsePrefix("127.0.0.0/8"), // Loopback (RFC 1122, Section 3.2.1.3))
netip.MustParsePrefix("169.254.0.0/16"), // Link Local (RFC 3927)
netip.MustParsePrefix("172.16.0.0/12"), // Private-Use (RFC 1918)
netip.MustParsePrefix("192.168.0.0/16"), // Private-Use (RFC 1918)
ssrf.IPv4DeniedPrefixes...,
),
ssrf.WithAllowedV6Prefixes(
netip.MustParsePrefix("::1/128"), // Loopback (RFC 4193)
netip.MustParsePrefix("fc00::/7"), // Unique Local (RFC 4193)
ssrf.IPv6DeniedPrefixes...,
),
).Safe
t.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
Expand Down

0 comments on commit e39baf3

Please sign in to comment.