Skip to content

Commit

Permalink
fix: add exceptions for internal IP addresses (#3608)
Browse files Browse the repository at this point in the history
  • Loading branch information
hperl authored Aug 16, 2023
1 parent 9f1c8d1 commit 1f1121c
Show file tree
Hide file tree
Showing 10 changed files with 492 additions and 15 deletions.
4 changes: 2 additions & 2 deletions client/validator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -273,13 +273,13 @@ func TestValidateIPRanges(t *testing.T) {
reg := internal.NewRegistryMemory(t, c, &contextx.Static{C: c.Source(ctx)})

v := NewValidator(reg)
c.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true)
c.MustSet(ctx, config.KeyClientHTTPNoPrivateIPRanges, true)
require.NoError(t, v.ValidateDynamicRegistration(ctx, &Client{}))
require.ErrorContains(t, v.ValidateDynamicRegistration(ctx, &Client{JSONWebKeysURI: "https://localhost:1234"}), "invalid_client_metadata")
require.ErrorContains(t, v.ValidateDynamicRegistration(ctx, &Client{BackChannelLogoutURI: "https://localhost:1234"}), "invalid_client_metadata")
require.ErrorContains(t, v.ValidateDynamicRegistration(ctx, &Client{RequestURIs: []string{"https://google", "https://localhost:1234"}}), "invalid_client_metadata")

c.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, false)
c.MustSet(ctx, config.KeyClientHTTPNoPrivateIPRanges, false)
require.NoError(t, v.ValidateDynamicRegistration(ctx, &Client{}))
require.NoError(t, v.ValidateDynamicRegistration(ctx, &Client{JSONWebKeysURI: "https://localhost:1234"}))
require.NoError(t, v.ValidateDynamicRegistration(ctx, &Client{BackChannelLogoutURI: "https://localhost:1234"}))
Expand Down
2 changes: 1 addition & 1 deletion consent/strategy_default.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ func (s *DefaultStrategy) verifyAuthentication(
return nil, fosite.ErrAccessDenied.WithHint("The login session cookie was not found or malformed.")
}

loginSession.IdentityProviderSessionID = f.IdentityProviderSessionID
loginSession.IdentityProviderSessionID = sqlxx.NullString(session.IdentityProviderSessionID)
if err := s.r.ConsentManager().ConfirmLoginSession(ctx, loginSession, sessionID, time.Time(session.AuthenticatedAt), session.Subject, session.Remember); err != nil {
return nil, err
}
Expand Down
29 changes: 26 additions & 3 deletions driver/config/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import (
"strings"
"time"

"github.com/pkg/errors"

"github.com/ory/x/hasherx"

"github.com/gofrs/uuid"
Expand Down Expand Up @@ -50,7 +52,8 @@ const (
KeySubjectTypesSupported = "oidc.subject_identifiers.supported_types"
KeyDefaultClientScope = "oidc.dynamic_client_registration.default_scope"
KeyDSN = "dsn"
ViperKeyClientHTTPNoPrivateIPRanges = "clients.http.disallow_private_ip_ranges"
KeyClientHTTPNoPrivateIPRanges = "clients.http.disallow_private_ip_ranges"
KeyClientHTTPPrivateIPExceptionURLs = "clients.http.private_ip_exception_urls"
KeyHasherAlgorithm = "oauth2.hashers.algorithm"
KeyBCryptCost = "oauth2.hashers.bcrypt.cost"
KeyPBKDF2Iterations = "oauth2.hashers.pbkdf2.iterations"
Expand Down Expand Up @@ -80,7 +83,8 @@ const (
KeyPublicURL = "urls.self.public"
KeyAdminURL = "urls.self.admin"
KeyIssuerURL = "urls.self.issuer"
KeyIdentityProviderAdminURL = "urls.identity_provider.admin_base_url"
KeyIdentityProviderAdminURL = "urls.identity_provider.url"
KeyIdentityProviderHeaders = "urls.identity_provider.headers"
KeyAccessTokenStrategy = "strategies.access_token"
KeyJWTScopeClaimStrategy = "strategies.jwt.scope_claim"
KeyDBIgnoreUnknownTableColumns = "db.ignore_unknown_table_columns"
Expand Down Expand Up @@ -200,7 +204,11 @@ func (p *DefaultProvider) WellKnownKeys(ctx context.Context, include ...string)
}

func (p *DefaultProvider) ClientHTTPNoPrivateIPRanges() bool {
return p.getProvider(contextx.RootContext).Bool(ViperKeyClientHTTPNoPrivateIPRanges)
return p.getProvider(contextx.RootContext).Bool(KeyClientHTTPNoPrivateIPRanges)
}

func (p *DefaultProvider) ClientHTTPPrivateIPExceptionURLs() []string {
return p.getProvider(contextx.RootContext).Strings(KeyClientHTTPPrivateIPExceptionURLs)
}

func (p *DefaultProvider) AllowedTopLevelClaims(ctx context.Context) []string {
Expand Down Expand Up @@ -401,6 +409,21 @@ func (p *DefaultProvider) KratosAdminURL(ctx context.Context) (*url.URL, bool) {

return u, u != nil
}
func (p *DefaultProvider) KratosRequestHeader(ctx context.Context) http.Header {
hh := map[string]string{}
if err := p.getProvider(ctx).Unmarshal(KeyIdentityProviderHeaders, &hh); err != nil {
p.l.WithError(errors.WithStack(err)).
Errorf("Configuration value from key %s could not be decoded.", KeyIdentityProviderHeaders)
return nil
}

h := make(http.Header)
for k, v := range hh {
h.Set(k, v)
}

return h
}

func (p *DefaultProvider) OAuth2ClientRegistrationURL(ctx context.Context) *url.URL {
return p.getProvider(ctx).RequestURIF(KeyOAuth2ClientRegistrationURL, new(url.URL))
Expand Down
6 changes: 5 additions & 1 deletion driver/registry_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,11 @@ func (m *RegistryBase) HTTPClient(ctx context.Context, opts ...httpx.ResilientOp
}

if m.Config().ClientHTTPNoPrivateIPRanges() {
opts = append(opts, httpx.ResilientClientDisallowInternalIPs())
opts = append(
opts,
httpx.ResilientClientDisallowInternalIPs(),
httpx.ResilientClientAllowInternalIPRequestsTo(m.Config().ClientHTTPPrivateIPExceptionURLs()...),
)
}
return httpx.NewResilientClient(opts...)
}
Expand Down
35 changes: 34 additions & 1 deletion driver/registry_base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ package driver
import (
"context"
"errors"
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/ory/x/randx"
Expand All @@ -33,7 +36,7 @@ func TestGetJWKSFetcherStrategyHostEnforcment(t *testing.T) {
c := config.MustNew(context.Background(), l, configx.WithConfigFiles("../internal/.hydra.yaml"))
c.MustSet(ctx, config.KeyDSN, "memory")
c.MustSet(ctx, config.HSMEnabled, "false")
c.MustSet(ctx, config.ViperKeyClientHTTPNoPrivateIPRanges, true)
c.MustSet(ctx, config.KeyClientHTTPNoPrivateIPRanges, true)

registry, err := NewRegistryWithoutInit(c, l)
require.NoError(t, err)
Expand Down Expand Up @@ -89,3 +92,33 @@ func TestRegistryBase_CookieStore_MaxAgeZero(t *testing.T) {

assert.Equal(t, cs.Options.MaxAge, 0)
}

func TestRegistryBase_HTTPClient(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
writer.WriteHeader(http.StatusOK)
}))
defer ts.Close()

t.Setenv("CLIENTS_HTTP_PRIVATE_IP_EXCEPTION_URLS", fmt.Sprintf("[%q]", ts.URL+"/exception/*"))

ctx := context.Background()
r := new(RegistryBase)
r.WithConfig(config.MustNew(
ctx,
logrusx.New("", ""),
configx.WithValues(map[string]interface{}{
config.KeyClientHTTPNoPrivateIPRanges: true,
}),
))

t.Run("case=matches exception glob", func(t *testing.T) {
res, err := r.HTTPClient(ctx).Get(ts.URL + "/exception/foo")
require.NoError(t, err)
assert.Equal(t, 200, res.StatusCode)
})

t.Run("case=does not match exception glob", func(t *testing.T) {
_, err := r.HTTPClient(ctx).Get(ts.URL + "/foo")
require.Error(t, err)
})
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ require (
github.com/ory/hydra-client-go/v2 v2.1.1
github.com/ory/jsonschema/v3 v3.0.8
github.com/ory/kratos-client-go v0.13.1
github.com/ory/x v0.0.580
github.com/ory/x v0.0.582-0.20230816082414-f1e6acad79b5
github.com/pborman/uuid v1.2.1
github.com/pkg/errors v0.9.1
github.com/prometheus/client_golang v1.16.0
Expand Down
Loading

0 comments on commit 1f1121c

Please sign in to comment.