diff --git a/.github/PULL_REQUEST_TEMPLATE b/.github/PULL_REQUEST_TEMPLATE index 03bc6a816..87b830a54 100644 --- a/.github/PULL_REQUEST_TEMPLATE +++ b/.github/PULL_REQUEST_TEMPLATE @@ -1,6 +1,7 @@ - - **Checklist**: -- [ ] documentation updated +- [ ] updated docs - [ ] unit tests added - [ ] related issues referenced - [ ] ready for review diff --git a/CHANGELOG.md b/CHANGELOG.md index 217dd00ca..33893fb36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,19 @@ # Pomerium Changelog +## vUNRELEASED + +### FEATURES + +### CHANGED + +- Removed `PROXY_ROOT_DOMAIN` config option which is now inferred from `AUTHENTICATE_SERVICE_URL`. Only callback requests originating from a URL on the same sub-domain are permitted. +- Removed `REDIRECT_URL` config option which is now inferred from `AUTHENTICATE_SERVICE_URL` (e.g. `https://$AUTHENTICATE_SERVICE_URL/oauth2/callback`). + +### FIXED + ## v0.0.3 -**FEATURES:** +### FEATURES - **Authorization** : The authorization module adds support for per-route access policy. In this release we support the most common forms of identity based access policy: `allowed_users`, `allowed_groups`, and `allowed_domains`. In future versions, the authorization module will also support context and device based authorization policy and decisions. See website documentation for more details. - **Group Support** : The authenticate service now retrieves a user's group membership information during authentication and refresh. This change may require additional identity provider configuration; all of which are described in the [updated docs](https://www.pomerium.io/docs/identity-providers.html). A brief summary of the requirements for each IdP are as follows: @@ -14,13 +25,13 @@ - **WebSocket Support** : With [Go 1.12](https://golang.org/doc/go1.12#net/http/httputil) pomerium automatically proxies WebSocket requests. -**CHANGED**: -- Add `LOG_LEVEL` config setting that allows for setting the desired minimum log level for an event to be logged. [GH-74] +### CHANGED + +- Add `LOG_LEVEL` config setting that allows for setting the desired minimum log level for an event to be logged. [GH-74] - Changed `POMERIUM_DEBUG` config setting to just do console-pretty printing. No longer sets log level. [GH-74] - Updated `generate_wildcard_cert.sh` to generate a elliptic curve 256 cert by default. - Updated `env.example` to include a `POLICY` setting example. - Added `IDP_SERVICE_ACCOUNT` to `env.example` . -- Removed `PROXY_ROOT_DOMAIN` settings which has been replaced by `POLICY`. - Removed `ALLOWED_DOMAINS` settings which has been replaced by `POLICY`. Authorization is now handled by the authorization service and is defined in the policy configuration files. - Removed `ROUTES` settings which has been replaced by `POLICY`. - Add refresh endpoint `${url}/.pomerium/refresh` which forces a token refresh and responds with the json result. @@ -32,6 +43,6 @@ - **Removed gitlab provider**. We can't support groups until [this gitlab bug](https://gitlab.com/gitlab-org/gitlab-ce/issues/44435#note_88150387) is fixed. - Request context is now maintained throughout request-flow via the [context package](https://golang.org/pkg/context/) enabling timeouts, request tracing, and cancellation. -**FIXED:** +### FIXED - `http.Server` and `httputil.NewSingleHostReverseProxy` now uses pomerium's logging package instead of the standard library's built in one. [GH-58] diff --git a/authenticate/authenticate.go b/authenticate/authenticate.go index d10a8465b..8e46cfb56 100644 --- a/authenticate/authenticate.go +++ b/authenticate/authenticate.go @@ -6,7 +6,6 @@ import ( "fmt" "html/template" "net/url" - "strings" "time" "github.com/pomerium/envconfig" @@ -27,13 +26,10 @@ var defaultOptions = &Options{ // Options details the available configuration settings for the authenticate service type Options struct { + AuthenticateURL *url.URL `envconfig:"AUTHENTICATE_SERVICE_URL"` + // SharedKey is used to authenticate requests between services SharedKey string `envconfig:"SHARED_SECRET"` - - // RedirectURL specifies the callback url following third party authentication - RedirectURL *url.URL `envconfig:"REDIRECT_URL"` - ProxyRootDomains []string `envconfig:"PROXY_ROOT_DOMAIN"` - // Session/Cookie management // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie CookieName string @@ -67,47 +63,38 @@ func OptionsFromEnvConfig() (*Options, error) { // The checks do not modify the internal state of the Option structure. Returns // on first error found. func (o *Options) Validate() error { - if o.RedirectURL == nil { - return errors.New("missing setting: identity provider redirect url") - } - redirectPath := "/oauth2/callback" - if o.RedirectURL.Path != redirectPath { - return fmt.Errorf("`setting` redirect-url was %s path should be %s", o.RedirectURL.Path, redirectPath) + if o.AuthenticateURL == nil { + return errors.New("authenticate: 'AUTHENTICATE_SERVICE_URL' missing") } if o.ClientID == "" { - return errors.New("missing setting: client id") + return errors.New("authenticate: 'IDP_CLIENT_ID' missing") } if o.ClientSecret == "" { - return errors.New("missing setting: client secret") - } - if len(o.ProxyRootDomains) == 0 { - return errors.New("missing setting: proxy root domain") + return errors.New("authenticate: 'IDP_CLIENT_SECRET' missing") } if o.SharedKey == "" { - return errors.New("missing setting: shared secret") + return errors.New("authenticate: 'SHARED_SECRET' missing") } decodedCookieSecret, err := base64.StdEncoding.DecodeString(o.CookieSecret) if err != nil { - return fmt.Errorf("cookie secret is invalid base64: %v", err) + return fmt.Errorf("authenticate: 'COOKIE_SECRET' must be base64 encoded: %v", err) } if len(decodedCookieSecret) != 32 { - return fmt.Errorf("cookie secret expects 32 bytes but got %d", len(decodedCookieSecret)) + return fmt.Errorf("authenticate: 'COOKIE_SECRET' should be 32; got %d", len(decodedCookieSecret)) } return nil } // Authenticate validates a user's identity type Authenticate struct { - SharedKey string - RedirectURL *url.URL - ProxyRootDomains []string + SharedKey string + RedirectURL *url.URL templates *template.Template csrfStore sessions.CSRFStore sessionStore sessions.SessionStore cipher cryptutil.Cipher - - provider identity.Authenticator + provider identity.Authenticator } // New validates and creates a new authenticate service from a set of Options @@ -118,7 +105,6 @@ func New(opts *Options) (*Authenticate, error) { if err := opts.Validate(); err != nil { return nil, err } - // checked by validate decodedCookieSecret, _ := base64.StdEncoding.DecodeString(opts.CookieSecret) cipher, err := cryptutil.NewCipher([]byte(decodedCookieSecret)) if err != nil { @@ -136,11 +122,12 @@ func New(opts *Options) (*Authenticate, error) { if err != nil { return nil, err } - + redirectURL := opts.AuthenticateURL + redirectURL.Path = "/oauth2/callback" provider, err := identity.New( opts.Provider, &identity.Provider{ - RedirectURL: opts.RedirectURL, + RedirectURL: redirectURL, ProviderName: opts.Provider, ProviderURL: opts.ProviderURL, ClientID: opts.ClientID, @@ -152,26 +139,13 @@ func New(opts *Options) (*Authenticate, error) { return nil, err } - p := &Authenticate{ - SharedKey: opts.SharedKey, - RedirectURL: opts.RedirectURL, - ProxyRootDomains: dotPrependDomains(opts.ProxyRootDomains), - + return &Authenticate{ + SharedKey: opts.SharedKey, + RedirectURL: redirectURL, templates: templates.New(), csrfStore: cookieStore, sessionStore: cookieStore, cipher: cipher, provider: provider, - } - - return p, nil -} - -func dotPrependDomains(d []string) []string { - for i := range d { - if d[i] != "" && !strings.HasPrefix(d[i], ".") { - d[i] = fmt.Sprintf(".%s", d[i]) - } - } - return d + }, nil } diff --git a/authenticate/authenticate_test.go b/authenticate/authenticate_test.go index 1f12fa549..408f98be5 100644 --- a/authenticate/authenticate_test.go +++ b/authenticate/authenticate_test.go @@ -11,31 +11,25 @@ import ( func testOptions() *Options { redirectURL, _ := url.Parse("https://example.com/oauth2/callback") return &Options{ - ProxyRootDomains: []string{"example.com"}, - RedirectURL: redirectURL, - SharedKey: "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ=", - ClientID: "test-client-id", - ClientSecret: "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw=", - CookieSecret: "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw=", - CookieRefresh: time.Duration(1) * time.Hour, - CookieExpire: time.Duration(168) * time.Hour, - CookieName: "pomerium", + AuthenticateURL: redirectURL, + SharedKey: "80ldlrU2d7w+wVpKNfevk6fmb8otEx6CqOfshj2LwhQ=", + ClientID: "test-client-id", + ClientSecret: "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw=", + CookieSecret: "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw=", + CookieRefresh: time.Duration(1) * time.Hour, + CookieExpire: time.Duration(168) * time.Hour, + CookieName: "pomerium", } } func TestOptions_Validate(t *testing.T) { good := testOptions() badRedirectURL := testOptions() - badRedirectURL.RedirectURL = nil - malformedRedirectURL := testOptions() - redirectURL, _ := url.Parse("https://example.com/oauth3/callback") - malformedRedirectURL.RedirectURL = redirectURL + badRedirectURL.AuthenticateURL = nil emptyClientID := testOptions() emptyClientID.ClientID = "" emptyClientSecret := testOptions() emptyClientSecret.ClientSecret = "" - proxyRootDomains := testOptions() - proxyRootDomains.ProxyRootDomains = nil emptyCookieSecret := testOptions() emptyCookieSecret.CookieSecret = "" invalidCookieSecret := testOptions() @@ -53,14 +47,12 @@ func TestOptions_Validate(t *testing.T) { {"minimum options", good, false}, {"nil options", &Options{}, true}, {"bad redirect url", badRedirectURL, true}, - {"malformed redirect url", malformedRedirectURL, true}, {"no cookie secret", emptyCookieSecret, true}, {"invalid cookie secret", invalidCookieSecret, true}, {"short cookie secret", shortCookieLength, true}, {"no shared secret", badSharedKey, true}, {"no client id", emptyClientID, true}, {"no client secret", emptyClientSecret, true}, - {"empty root domains", proxyRootDomains, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -83,7 +75,7 @@ func TestOptionsFromEnvConfig(t *testing.T) { wantErr bool }{ {"good default, no env settings", defaultOptions, "", "", false}, - {"bad url", nil, "REDIRECT_URL", "%.rjlw", true}, + {"bad url", nil, "AUTHENTICATE_SERVICE_URL", "%.rjlw", true}, {"good duration", defaultOptions, "COOKIE_EXPIRE", "1m", false}, {"bad duration", nil, "COOKIE_REFRESH", "1sm", true}, } @@ -105,33 +97,12 @@ func TestOptionsFromEnvConfig(t *testing.T) { } } -func Test_dotPrependDomains(t *testing.T) { - - tests := []struct { - name string - d []string - want []string - }{ - {"single domain", []string{"google.com"}, []string{".google.com"}}, - {"multiple domain", []string{"google.com", "bing.com"}, []string{".google.com", ".bing.com"}}, - {"empty", []string{""}, []string{""}}, - {"nested subdomain", []string{"some.really.long.domain.com"}, []string{".some.really.long.domain.com"}}, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := dotPrependDomains(tt.d); !reflect.DeepEqual(got, tt.want) { - t.Errorf("dotPrependDomains() = %v, want %v", got, tt.want) - } - }) - } -} - func TestNew(t *testing.T) { good := testOptions() good.Provider = "google" badRedirectURL := testOptions() - badRedirectURL.RedirectURL = nil + badRedirectURL.AuthenticateURL = nil tests := []struct { name string diff --git a/authenticate/handlers.go b/authenticate/handlers.go index dc0372468..74be885f8 100644 --- a/authenticate/handlers.go +++ b/authenticate/handlers.go @@ -53,7 +53,7 @@ func (a *Authenticate) Handler() http.Handler { stdMiddleware = stdMiddleware.Append(middleware.RequestIDHandler("req_id", "Request-Id")) validateSignatureMiddleware := stdMiddleware.Append( middleware.ValidateSignature(a.SharedKey), - middleware.ValidateRedirectURI(a.ProxyRootDomains)) + middleware.ValidateRedirectURI(a.RedirectURL)) mux := http.NewServeMux() mux.Handle("/robots.txt", stdMiddleware.ThenFunc(a.RobotsTxt)) @@ -190,7 +190,7 @@ func (a *Authenticate) SignOut(w http.ResponseWriter, r *http.Request) { httputil.ErrorResponse(w, r, "No session found to log out", http.StatusBadRequest) return } - if r.Method == "GET" { + if r.Method == http.MethodGet { signature := r.Form.Get("sig") timestamp := r.Form.Get("ts") destinationURL, err := url.Parse(redirectURI) @@ -237,13 +237,13 @@ func (a *Authenticate) OAuthStart(w http.ResponseWriter, r *http.Request) { a.csrfStore.SetCSRF(w, r, nonce) // verify redirect uri is from the root domain - if !middleware.ValidRedirectURI(authRedirectURL.String(), a.ProxyRootDomains) { + if !middleware.SameSubdomain(authRedirectURL, a.RedirectURL) { httputil.ErrorResponse(w, r, "Invalid redirect parameter", http.StatusBadRequest) return } // verify proxy url is from the root domain proxyRedirectURL, err := url.Parse(authRedirectURL.Query().Get("redirect_uri")) - if err != nil || !middleware.ValidRedirectURI(proxyRedirectURL.String(), a.ProxyRootDomains) { + if err != nil || !middleware.SameSubdomain(proxyRedirectURL, a.RedirectURL) { httputil.ErrorResponse(w, r, "Invalid redirect parameter", http.StatusBadRequest) return } @@ -329,8 +329,14 @@ func (a *Authenticate) getOAuthCallback(w http.ResponseWriter, r *http.Request) return "", httputil.HTTPError{Code: http.StatusForbidden, Message: "CSRF failed"} } - if !middleware.ValidRedirectURI(redirect, a.ProxyRootDomains) { - return "", httputil.HTTPError{Code: http.StatusForbidden, Message: "Invalid Redirect URI"} + redirectURL, err := url.Parse(redirect) + if err != nil { + log.FromRequest(r).Error().Err(err).Msg("authenticate: couldn't parse redirect url") + return "", httputil.HTTPError{Code: http.StatusForbidden, Message: "Couldn't parse redirect url"} + } + + if !middleware.SameSubdomain(redirectURL, a.RedirectURL) { + return "", httputil.HTTPError{Code: http.StatusForbidden, Message: "Invalid Redirect URI domain"} } err = a.sessionStore.SaveSession(w, r, session) diff --git a/authenticate/handlers_test.go b/authenticate/handlers_test.go index 64d12d78f..81cc19601 100644 --- a/authenticate/handlers_test.go +++ b/authenticate/handlers_test.go @@ -21,7 +21,6 @@ func testAuthenticate() *Authenticate { var auth Authenticate auth.RedirectURL, _ = url.Parse("https://auth.example.com/oauth/callback") auth.SharedKey = "IzY7MOZwzfOkmELXgozHDKTxoT3nOYhwkcmUVINsRww=" - auth.ProxyRootDomains = []string{"example.com"} auth.templates = templates.New() return &auth } @@ -443,10 +442,9 @@ func TestAuthenticate_OAuthStart(t *testing.T) { name string method string - redirectURL string - sig string - ts string - allowedDomains []string + redirectURL string + sig string + ts string provider identity.Authenticator csrfStore sessions.MockCSRFStore @@ -458,7 +456,6 @@ func TestAuthenticate_OAuthStart(t *testing.T) { "https://corp.pomerium.io/", redirectURLSignature("https://corp.pomerium.io/", time.Now(), "secret"), fmt.Sprint(time.Now().Unix()), - []string{".pomerium.io"}, identity.MockProvider{}, sessions.MockCSRFStore{}, http.StatusFound, @@ -468,17 +465,6 @@ func TestAuthenticate_OAuthStart(t *testing.T) { "https://corp.pomerium.io/", redirectURLSignature("https://corp.pomerium.io/", time.Now(), "secret"), fmt.Sprint(time.Now().Add(10 * time.Hour).Unix()), - []string{".pomerium.io"}, - identity.MockProvider{}, - sessions.MockCSRFStore{}, - http.StatusBadRequest, - }, - {"domain not in allowed domains", - http.MethodGet, - "https://corp.pomerium.io/", - redirectURLSignature("https://corp.pomerium.io/", time.Now(), "secret"), - fmt.Sprint(time.Now().Unix()), - []string{"not.pomerium.io"}, identity.MockProvider{}, sessions.MockCSRFStore{}, http.StatusBadRequest, @@ -488,7 +474,6 @@ func TestAuthenticate_OAuthStart(t *testing.T) { "", redirectURLSignature("https://corp.pomerium.io/", time.Now(), "secret"), fmt.Sprint(time.Now().Unix()), - []string{".pomerium.io"}, identity.MockProvider{}, sessions.MockCSRFStore{}, http.StatusBadRequest, @@ -498,7 +483,6 @@ func TestAuthenticate_OAuthStart(t *testing.T) { "https://pomerium.com%zzzzz", redirectURLSignature("https://corp.pomerium.io/", time.Now(), "secret"), fmt.Sprint(time.Now().Unix()), - []string{".pomerium.io"}, identity.MockProvider{}, sessions.MockCSRFStore{}, http.StatusBadRequest, @@ -507,12 +491,11 @@ func TestAuthenticate_OAuthStart(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { a := &Authenticate{ - ProxyRootDomains: tt.allowedDomains, - RedirectURL: uriParse("http://www.pomerium.io"), - csrfStore: tt.csrfStore, - provider: tt.provider, - SharedKey: "secret", - cipher: mockCipher{}, + RedirectURL: uriParse("http://www.pomerium.io"), + csrfStore: tt.csrfStore, + provider: tt.provider, + SharedKey: "secret", + cipher: mockCipher{}, } u, _ := url.Parse("/oauth_start") params, _ := url.ParseQuery(u.RawQuery) @@ -540,13 +523,13 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { method string // url params - paramErr string - code string - state string - validDomains []string - session sessions.SessionStore - provider identity.MockProvider - csrfStore sessions.MockCSRFStore + paramErr string + code string + state string + authenticateURL string + session sessions.SessionStore + provider identity.MockProvider + csrfStore sessions.MockCSRFStore want string wantErr bool @@ -556,8 +539,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -578,8 +560,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -601,8 +582,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -623,8 +603,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateError: errors.New("error"), @@ -640,8 +619,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{SaveError: errors.New("error")}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -663,8 +641,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "idp error", "code", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -685,8 +662,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "", base64.URLEncoding.EncodeToString([]byte("nonce:https://corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -707,8 +683,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", "nonce:https://corp.pomerium.io", - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -729,8 +704,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -751,8 +725,7 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { "", "code", base64.URLEncoding.EncodeToString([]byte("nonce:corp.pomerium.io")), - []string{"pomerium.io"}, - + "https://authenticate.pomerium.io", &sessions.MockSessionStore{}, identity.MockProvider{ AuthenticateResponse: sessions.SessionState{ @@ -771,11 +744,12 @@ func TestAuthenticate_getOAuthCallback(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + authURL, _ := url.Parse(tt.authenticateURL) a := &Authenticate{ - sessionStore: tt.session, - csrfStore: tt.csrfStore, - provider: tt.provider, - ProxyRootDomains: tt.validDomains, + RedirectURL: authURL, + sessionStore: tt.session, + csrfStore: tt.csrfStore, + provider: tt.provider, } u, _ := url.Parse("/oauthGet") params, _ := url.ParseQuery(u.RawQuery) diff --git a/cmd/pomerium/main.go b/cmd/pomerium/main.go index 7e17864d0..baa9dcd7f 100644 --- a/cmd/pomerium/main.go +++ b/cmd/pomerium/main.go @@ -40,7 +40,7 @@ func main() { fmt.Printf("%s", version.FullVersion()) os.Exit(0) } - log.Info().Str("version", version.FullVersion()).Str("service", mainOpts.Services).Msg("cmd/pomerium") + log.Info().Str("version", version.FullVersion()).Str("user-agent", version.UserAgent()).Str("service", mainOpts.Services).Msg("cmd/pomerium") grpcAuth := middleware.NewSharedSecretCred(mainOpts.SharedKey) grpcOpts := []grpc.ServerOption{grpc.UnaryInterceptor(grpcAuth.ValidateRequest)} @@ -57,7 +57,7 @@ func main() { if err != nil { log.Fatal().Err(err).Msg("cmd/pomerium: new authenticate") } - authHost = opts.RedirectURL.Host + authHost = opts.AuthenticateURL.Host pbAuthenticate.RegisterAuthenticatorServer(grpcServer, authenticateService) } diff --git a/docs/docs/config-reference.md b/docs/docs/config-reference.md index 5f25ef3bd..a4e97bb95 100644 --- a/docs/docs/config-reference.md +++ b/docs/docs/config-reference.md @@ -87,7 +87,7 @@ If `false` - Options: `debug` `info` `warn` `error` - Default: `debug` -Log level sets the global logging level for pomerium. Only logs of the desired level and above will be logged. +Log level sets the global logging level for pomerium. Only logs of the desired level and above will be logged. ### Certificate @@ -107,22 +107,14 @@ Certificate key is the x509 _private-key_ used to establish secure HTTP and gRPC ## Authenticate Service -### Redirect URL +### Authenticate Service URL -- Environmental Variable: `REDIRECT_URL` +- Environmental Variable: `AUTHENTICATE_SERVICE_URL` - Type: `URL` - Required -- Example: `https://auth.corp.example.com/oauth2/callback` +- Example: `https://authenticate.corp.example.com` -Redirect URL is the url the user will be redirected to following authentication with the third-party identity provider (IdP). Note the URL ends with `/oauth2/callback`. This setting will mirror the URL set when configuring your [identity provider]. Typically, on the provider side, this is called an _authorized callback url_. - -### Proxy Root Domains - -- Environmental Variable: `PROXY_ROOT_DOMAIN` -- Type: `[]string` (e.g. comma separated list of strings) -- Required - -Proxy Root Domains specifies the sub-domains that can proxy requests. For example, `httpbin.corp.example.com` would be a valid domain under the proxy root domain `corp.example.com`. If a proxy service attempts to authenticate a user from a non-whitelisted domain, an error will be returned. +Authenticate Service URL is the externally accessible URL for the authenticate service. ### Identity Provider Name @@ -191,7 +183,7 @@ Signing key is the base64 encoded key used to sign outbound requests. For more i - Environmental Variable: `AUTHENTICATE_SERVICE_URL` - Type: `URL` - Required -- Example: `https://auth.corp.example.com` +- Example: `https://authenticate.corp.example.com` Authenticate Service URL is the externally accessible URL for the authenticate service. @@ -227,7 +219,7 @@ Authorize Internal Service URL is the internally routed dns name of the authoriz - Environmental Variable: `OVERRIDE_CERTIFICATE_NAME` - Type: `int` - Optional (but typically required if Authenticate Internal Service Address is set) -- Example: `*.corp.example.com` if wild card or `authenticate.corp.example.com` +- Example: `*.corp.example.com` if wild card or `authenticate.corp.example.com`/`authorize.corp.example.com` When Authenticate Internal Service Address is set, secure service communication can fail because the external certificate name will not match the internally routed service url. This setting allows you to override that check. diff --git a/docs/docs/examples/docker/basic.docker-compose.yml b/docs/docs/examples/docker/basic.docker-compose.yml index 8da3dd65f..41606d72c 100644 --- a/docs/docs/examples/docker/basic.docker-compose.yml +++ b/docs/docs/examples/docker/basic.docker-compose.yml @@ -13,18 +13,16 @@ services: environment: - POMERIUM_DEBUG=true - SERVICES=all - - REDIRECT_URL=https://auth.corp.beyondperimeter.com/oauth2/callback - IDP_PROVIDER=google - IDP_PROVIDER_URL=https://accounts.google.com - IDP_CLIENT_ID=REPLACE_ME.apps.googleusercontent.com - IDP_CLIENT_SECRET=REPLACE_ME - - PROXY_ROOT_DOMAIN=beyondperimeter.com - SHARED_SECRET=aDducXQzK2tPY3R4TmdqTGhaYS80eGYxcTUvWWJDb2M= - COOKIE_SECRET=V2JBZk0zWGtsL29UcFUvWjVDWWQ2UHExNXJ0b2VhcDI= - CERTIFICATE_FILE=cert.pem - CERTIFICATE_KEY_FILE=privkey.pem - - AUTHENTICATE_SERVICE_URL=https://auth.corp.beyondperimeter.com - - AUTHORIZE_SERVICE_URL=https://access.corp.beyondperimeter.com + - AUTHENTICATE_SERVICE_URL=https://authenticate.corp.beyondperimeter.com + - AUTHORIZE_SERVICE_URL=https://authorize.corp.beyondperimeter.com - POLICY_FILE=./policy.yaml volumes: - ./cert.pem:/pomerium/cert.pem:ro diff --git a/docs/docs/examples/docker/nginx.docker-compose.yml b/docs/docs/examples/docker/nginx.docker-compose.yml index 8a116275e..581635ece 100644 --- a/docs/docs/examples/docker/nginx.docker-compose.yml +++ b/docs/docs/examples/docker/nginx.docker-compose.yml @@ -19,18 +19,16 @@ services: environment: - POMERIUM_DEBUG=true - SERVICES=authenticate - - REDIRECT_URL=https://auth.corp.beyondperimeter.com/oauth2/callback # Identity Provider Settings (Must be changed!) - IDP_PROVIDER=google - IDP_PROVIDER_URL=https://accounts.google.com - IDP_CLIENT_ID=REPLACE_ME.apps.googleusercontent.com - IDP_CLIENT_SECRET=REPLACE_ME - - PROXY_ROOT_DOMAIN=corp.beyondperimeter.com - SHARED_SECRET=aDducXQzK2tPY3R4TmdqTGhaYS80eGYxcTUvWWJDb2M= - COOKIE_SECRET=V2JBZk0zWGtsL29UcFUvWjVDWWQ2UHExNXJ0b2VhcDI= # nginx settings - VIRTUAL_PROTO=https - - VIRTUAL_HOST=auth.corp.beyondperimeter.com + - VIRTUAL_HOST=authenticate.corp.beyondperimeter.com - VIRTUAL_PORT=443 volumes: - ./cert.pem:/pomerium/cert.pem:ro @@ -45,8 +43,8 @@ services: - POMERIUM_DEBUG=true - SERVICES=proxy - POLICY_FILE=policy.yaml - - AUTHENTICATE_SERVICE_URL=https://auth.corp.beyondperimeter.com - - AUTHORIZE_SERVICE_URL=https://access.corp.beyondperimeter.com + - AUTHENTICATE_SERVICE_URL=https://authenticate.corp.beyondperimeter.com + - AUTHORIZE_SERVICE_URL=https://authorize.corp.beyondperimeter.com # IMPORTANT! If you are running pomerium behind another ingress (loadbalancer/firewall/etc) # you must tell pomerium proxy how to communicate using an internal hostname for RPC - AUTHENTICATE_INTERNAL_URL=pomerium-authenticate:443 @@ -77,7 +75,7 @@ services: - SHARED_SECRET=aDducXQzK2tPY3R4TmdqTGhaYS80eGYxcTUvWWJDb2M= # nginx settings - VIRTUAL_PROTO=https - - VIRTUAL_HOST=access.corp.beyondperimeter.com + - VIRTUAL_HOST=authorize.corp.beyondperimeter.com - VIRTUAL_PORT=443 volumes: - ./cert.pem:/pomerium/cert.pem:ro diff --git a/docs/docs/examples/kubernetes/authenticate.deploy.yml b/docs/docs/examples/kubernetes/authenticate.deploy.yml index d6b4f4acc..ddb1a6441 100644 --- a/docs/docs/examples/kubernetes/authenticate.deploy.yml +++ b/docs/docs/examples/kubernetes/authenticate.deploy.yml @@ -25,16 +25,12 @@ spec: env: - name: SERVICES value: authenticate - - name: REDIRECT_URL - value: https://auth.corp.beyondperimeter.com/oauth2/callback - name: IDP_PROVIDER value: google - name: IDP_PROVIDER_URL value: https://accounts.google.com - name: IDP_CLIENT_ID value: 851877082059-bfgkpj09noog7as3gpc3t7r6n9sjbgs6.apps.googleusercontent.com - - name: PROXY_ROOT_DOMAIN - value: beyondperimeter.com - name: SHARED_SECRET valueFrom: secretKeyRef: diff --git a/docs/docs/examples/kubernetes/ingress.nginx.yml b/docs/docs/examples/kubernetes/ingress.nginx.yml index feea07f01..f68012a92 100644 --- a/docs/docs/examples/kubernetes/ingress.nginx.yml +++ b/docs/docs/examples/kubernetes/ingress.nginx.yml @@ -16,8 +16,8 @@ spec: - secretName: pomerium-tls hosts: - "*.corp.beyondperimeter.com" - - "auth.corp.beyondperimeter.com" - - "access.corp.beyondperimeter.com" + - "authenticate.corp.beyondperimeter.com" + - "authorize.corp.beyondperimeter.com" rules: - host: "*.corp.beyondperimeter.com" @@ -28,14 +28,14 @@ spec: serviceName: pomerium-proxy-service servicePort: https - - host: "auth.corp.beyondperimeter.com" + - host: "authenticate.corp.beyondperimeter.com" http: paths: - paths: backend: serviceName: pomerium-authenticate-service servicePort: https - - host: "access.corp.beyondperimeter.com" + - host: "authorize.corp.beyondperimeter.com" http: paths: - paths: diff --git a/docs/docs/examples/kubernetes/ingress.yml b/docs/docs/examples/kubernetes/ingress.yml index 2e2b935e2..7b3f72aef 100644 --- a/docs/docs/examples/kubernetes/ingress.yml +++ b/docs/docs/examples/kubernetes/ingress.yml @@ -12,8 +12,8 @@ spec: - secretName: pomerium-tls hosts: - "*.corp.beyondperimeter.com" - - "auth.corp.beyondperimeter.com" - - "access.corp.beyondperimeter.com" + - "authenticate.corp.beyondperimeter.com" + - "authorize.corp.beyondperimeter.com" rules: - host: "*.corp.beyondperimeter.com" @@ -23,14 +23,14 @@ spec: backend: serviceName: pomerium-proxy-service servicePort: https - - host: "auth.corp.beyondperimeter.com" + - host: "authenticate.corp.beyondperimeter.com" http: paths: - paths: backend: serviceName: pomerium-authenticate-service servicePort: https - - host: "access.corp.beyondperimeter.com" + - host: "authorize.corp.beyondperimeter.com" http: paths: - paths: diff --git a/docs/docs/examples/kubernetes/proxy.deploy.yml b/docs/docs/examples/kubernetes/proxy.deploy.yml index 7db765805..633741280 100644 --- a/docs/docs/examples/kubernetes/proxy.deploy.yml +++ b/docs/docs/examples/kubernetes/proxy.deploy.yml @@ -26,11 +26,11 @@ spec: - name: SERVICES value: proxy - name: AUTHORIZE_SERVICE_URL - value: https://access.corp.beyondperimeter.com + value: https://authorize.corp.beyondperimeter.com - name: AUTHORIZE_INTERNAL_URL value: "pomerium-authorize-service.pomerium.svc.cluster.local" - name: AUTHENTICATE_SERVICE_URL - value: https://auth.corp.beyondperimeter.com + value: https://authenticate.corp.beyondperimeter.com - name: AUTHENTICATE_INTERNAL_URL value: "pomerium-authenticate-service.pomerium.svc.cluster.local" - name: OVERRIDE_CERTIFICATE_NAME diff --git a/docs/docs/identity-providers.md b/docs/docs/identity-providers.md index a9637c091..762cbe186 100644 --- a/docs/docs/identity-providers.md +++ b/docs/docs/identity-providers.md @@ -14,7 +14,7 @@ There are a few configuration steps required for identity provider integration. In this guide we'll cover how to do the following for each identity provider: -1. Set a **[Redirect URL]** pointing back to Pomerium. +1. Set a **Redirect URL** pointing back to Pomerium. That is, `https://${AUTHENTICATE_SERVICE_URL}/oauth2/callback` 2. Generate a **[Client ID]** and **[Client Secret]**. 3. Configure Pomerium to use the **[Client ID]** and **[Client Secret]** keys. @@ -69,7 +69,7 @@ Click on **Save** and the key will be displayed. **Make sure to copy the value o ![Creating a Key](./microsoft/azure-create-key.png) -Next you need to ensure that the Pomerium's Redirect URL is listed in allowed reply URLs for the created application. Navigate to **Azure Active Directory** -> **Apps registrations** and select your app. Then click **Settings** -> **Reply URLs** and add Pomerium's redirect URL. For example, `https://sso-auth.corp.beyondperimeter.com/oauth2/callback`. +Next you need to ensure that the Pomerium's Redirect URL is listed in allowed reply URLs for the created application. Navigate to **Azure Active Directory** -> **Apps registrations** and select your app. Then click **Settings** -> **Reply URLs** and add Pomerium's redirect URL. For example, `https://authenticate.corp.beyondperimeter.com/oauth2/callback`. ![Add Reply URL](./microsoft/azure-redirect-url.png) @@ -109,7 +109,6 @@ Finally, configure Pomerium with the identity provider settings retrieved in the ```bash # Azure -REDIRECT_URL="https://sso-auth.corp.beyondperimeter.com/oauth2/callback" IDP_PROVIDER="azure" IDP_PROVIDER_URL="https://login.microsoftonline.com/{REPLACE-ME-SEE-ABOVE}/v2.0" IDP_CLIENT_ID="REPLACE-ME" @@ -133,7 +132,7 @@ On the **Applications** page, add a new application by setting the following par Field | Description ------------ | -------------------------------------------------------------------- Name | The name of your web app -Redirect URI | [Redirect URL] (e.g.`https://auth.corp.example.com/oauth2/callback`) +Redirect URI | Redirect URL (e.g.`https://authenticate.corp.example.com/oauth2/callback`) Scopes | **Must** select **read_user** and **openid** ![Create New Credentials](./gitlab/gitlab-create-application.png) @@ -147,7 +146,6 @@ Your [Client ID] and [Client Secret] will be displayed: Set [Client ID] and [Client Secret] in Pomerium's settings. Your [environmental variables] should look something like this. ```bash -REDIRECT_URL="https://sso-auth.corp.beyondperimeter.com/oauth2/callback" IDP_PROVIDER="gitlab" # NOTE!!! Provider url is optional, but should be set if you are running an on-premise instance # defaults to : https://gitlab.com, a local copy would look something like `http://gitlab.corp.beyondperimeter.com` @@ -175,7 +173,7 @@ On the **Create [Client ID]** page, select **Web application**. In the new field Field | Description ------------------------ | -------------------------------------------------------------------- Name | The name of your web app -Authorized redirect URIs | [Redirect URL] (e.g.`https://auth.corp.example.com/oauth2/callback`) +Authorized redirect URIs | Redirect URL (e.g.`https://authenticate.corp.example.com/oauth2/callback`) ![Web App Credentials Configuration](./google/google-create-client-id-config.png) @@ -229,7 +227,6 @@ Next we'll delegate G-suite group membership access to the service account we ju Your [environmental variables] should look something like this. ```bash -REDIRECT_URL="https://sso-auth.corp.beyondperimeter.com/oauth2/callback" IDP_PROVIDER="google" IDP_PROVIDER_URL="https://accounts.google.com" IDP_CLIENT_ID="yyyy.apps.googleusercontent.com" @@ -253,7 +250,7 @@ Field | Description ---------------------------- | --------------------------------------------------------------------- Name | The name of your application. Base URIs (optional) | The domain(s) of your application. -Login redirect URIs | [Redirect URL] (e.g.`https://auth.corp.example.com/oauth2/callback`). +Login redirect URIs | Redirect URL (e.g.`https://authenticate.corp.example.com/oauth2/callback`). Group assignments (optional) | The user groups that can sign in to this application. Grant type allowed | **You must enable Refresh Token.** @@ -296,7 +293,6 @@ Include in | Any scope Finally, configure Pomerium with the identity provider settings retrieved in the pervious steps. Your [environmental variables] should look something like this. ```bash -REDIRECT_URL="https://sso-auth.corp.beyondperimeter.com/oauth2/callback" IDP_PROVIDER="okta" IDP_PROVIDER_URL="https://dev-108295-admin.oktapreview.com/" IDP_CLIENT_ID="0oairksnr0C0fEJ7l0h7" @@ -319,7 +315,7 @@ On the App Configuration page, **name the app** and **select a logo**. Select ** ![One Login select logo](./one-login/one-login-select-logo.png) -Next, set set the **Redirect URI's** setting to be Pomerium's [redirect url]. +Next, set set the **Redirect URI's** setting to be Pomerium's redirect url `https://${AUTHENTICATE_SERVICE_URL}/oauth2/callback`. ![One Login set callback url](./one-login/one-login-callback-url.png) @@ -345,7 +341,6 @@ To return the user's Active Directory field, configure the group to return `memb Finally, configure Pomerium with the identity provider settings retrieved in the pervious steps. Your [environmental variables] should look something like this. ```bash -REDIRECT_URL="https://auth.corp.beyondperimeter.com/oauth2/callback" IDP_PROVIDER="onelogin" IDP_PROVIDER_URL="https://openid-connect.onelogin.com/oidc" IDP_CLIENT_ID="9e613ce0-1622-0137-452d-0a93c31f8392142934" @@ -361,4 +356,3 @@ After reloading Pomerium, you should be able to see any login events from your O [environmental variables]: https://en.wikipedia.org/wiki/Environment_variable [oauth2]: https://oauth.net/2/ [openid connect]: https://en.wikipedia.org/wiki/OpenID_Connect -[redirect url]: ./config-reference.html#redirect-url diff --git a/docs/guide/synology.md b/docs/guide/synology.md index ff371d34c..e1db299cd 100644 --- a/docs/guide/synology.md +++ b/docs/guide/synology.md @@ -173,10 +173,8 @@ OVERRIDE_CERTIFICATE_NAME | `*.int.nas.example.com` IDP_CLIENT_SECRET | Values from setting up your [identity provider] IDP_CLIENT_ID | Values from setting up your [identity provider] IDP_PROVIDER | Values from setting up your [identity provider] (e.g. `google`) -REDIRECT_URL | `https://authenticate.int.nas.example.com/oauth2/callback` COOKIE_SECRET | output of `head -c32 /dev/urandom | base64` SHARED_SECRET | output of `head -c32 /dev/urandom | base64` -PROXY_ROOT_DOMAIN | `int.nas.example.com` AUTHORIZE_SERVICE_URL | `https://authorize.int.nas.example.com` AUTHENTICATE_SERVICE_URL | `https://authenticate.int.nas.example.com` AUTHORIZE_INTERNAL_URL | `localhost:443` diff --git a/env.example b/env.example index cd56cbd6b..1091c0793 100644 --- a/env.example +++ b/env.example @@ -3,7 +3,11 @@ # Main configuration flags # export ADDRESS=":8443" # optional, default is 443 # export POMERIUM_DEBUG=true # optional, default is false -# export SERVICE="all" # optional, default is all. +# export SERVICE="all" # optional, default is all +# export LOG_LEVEL="info" # optional, default is debug + +export AUTHENTICATE_SERVICE_URL=https://authenticate.corp.example.com +export AUTHORIZE_SERVICE_URL=https://authorize.corp.example.com # Certificates can be loaded as files or base64 encoded bytes. If neither is set, a # pomerium will attempt to locate a pair in the root directory @@ -12,8 +16,6 @@ export CERTIFICATE_KEY_FILE="./privkey.pem" # optional, defaults to `./certprivk # export CERTIFICATE="xxxxxx" # base64 encoded cert, eg. `base64 -i cert.pem` # export CERTIFICATE_KEY="xxxx" # base64 encoded key, eg. `base64 -i privkey.pem` -# The URL that the identity provider will call back after authenticating the user -export REDIRECT_URL="https://sso-auth.corp.example.com/oauth2/callback" # Generate 256 bit random keys e.g. `head -c32 /dev/urandom | base64` export SHARED_SECRET=9wiTZq4qvmS/plYQyvzGKWPlH/UBy0DMYMA2x/zngrM= export COOKIE_SECRET=uPGHo1ujND/k3B9V6yr52Gweq3RRYfFho98jxDG5Br8= diff --git a/internal/middleware/middleware.go b/internal/middleware/middleware.go index cfd686fb0..8f04680da 100644 --- a/internal/middleware/middleware.go +++ b/internal/middleware/middleware.go @@ -53,7 +53,7 @@ func ValidateClientSecret(sharedSecret string) func(next http.Handler) http.Hand // ValidateRedirectURI checks the redirect uri in the query parameters and ensures that // the its domain is in the list of proxy root domains. -func ValidateRedirectURI(proxyRootDomains []string) func(next http.Handler) http.Handler { +func ValidateRedirectURI(rootDomain *url.URL) func(next http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { err := r.ParseForm() @@ -61,8 +61,12 @@ func ValidateRedirectURI(proxyRootDomains []string) func(next http.Handler) http httputil.ErrorResponse(w, r, err.Error(), http.StatusBadRequest) return } - redirectURI := r.Form.Get("redirect_uri") - if !ValidRedirectURI(redirectURI, proxyRootDomains) { + redirectURI, err := url.Parse(r.Form.Get("redirect_uri")) + if err != nil { + httputil.ErrorResponse(w, r, err.Error(), http.StatusBadRequest) + return + } + if !SameSubdomain(redirectURI, rootDomain) { httputil.ErrorResponse(w, r, "Invalid redirect parameter", http.StatusBadRequest) return } @@ -71,24 +75,22 @@ func ValidateRedirectURI(proxyRootDomains []string) func(next http.Handler) http } } -// ValidRedirectURI checks if a URL's domain is one in the list of proxy root domains. -func ValidRedirectURI(uri string, rootDomains []string) bool { - if uri == "" || len(rootDomains) == 0 { +// SameSubdomain checks to see if two URLs share the same root domain. +func SameSubdomain(u, j *url.URL) bool { + if u.Hostname() == "" || j.Hostname() == "" { return false } - redirectURL, err := url.Parse(uri) - if err != nil || redirectURL.Host == "" { + uParts := strings.Split(u.Hostname(), ".") + jParts := strings.Split(j.Hostname(), ".") + if len(uParts) != len(jParts) { return false } - for _, domain := range rootDomains { - if domain == "" { + for i := 1; i < len(uParts); i++ { + if uParts[i] != jParts[i] { return false } - if strings.HasSuffix(redirectURL.Hostname(), domain) { - return true - } } - return false + return true } // ValidateSignature ensures the request is valid and has been signed with diff --git a/internal/middleware/middleware_test.go b/internal/middleware/middleware_test.go index 9377589ea..1ba0f841b 100644 --- a/internal/middleware/middleware_test.go +++ b/internal/middleware/middleware_test.go @@ -10,25 +10,29 @@ import ( "time" ) -func Test_ValidRedirectURI(t *testing.T) { +func Test_SameSubdomain(t *testing.T) { tests := []struct { name string uri string - rootDomains []string + rootDomains string want bool }{ - {"good url redirect", "https://example.com/redirect", []string{"example.com"}, true}, - {"bad domain", "https://example.com/redirect", []string{"notexample.com"}, false}, - {"malformed url", "^example.com/redirect", []string{"notexample.com"}, false}, - {"empty domain list", "https://example.com/redirect", []string{}, false}, - {"empty domain", "https://example.com/redirect", []string{""}, false}, - {"empty url", "", []string{"example.com"}, false}, + {"good url redirect", "https://example.com/redirect", "https://example.com", true}, + {"simple sub", "https://auth.example.com", "https://test.example.com", true}, + {"mismatched lengths", "https://auth.auth.example.com", "https://test.example.com", false}, + {"bad domain", "https://auth.example.com/redirect", "https://test.notexample.com", false}, + {"malformed url", "^example.com/redirect", "https://notexample.com", false}, + {"empty domain list", "https://example.com/redirect", ".com", false}, + {"empty domain", "https://example.com/redirect", "", false}, + {"empty url", "", "example.com", false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := ValidRedirectURI(tt.uri, tt.rootDomains); got != tt.want { - t.Errorf("ValidRedirectURI() = %v, want %v", got, tt.want) + u, _ := url.Parse(tt.uri) + j, _ := url.Parse(tt.rootDomains) + if got := SameSubdomain(u, j); got != tt.want { + t.Errorf("SameSubdomain() = %v, want %v", got, tt.want) } }) } @@ -118,17 +122,22 @@ func TestSetHeaders(t *testing.T) { func TestValidateRedirectURI(t *testing.T) { tests := []struct { - name string - proxyRootDomains []string - redirectURI string - status int + name string + rootDomain string + redirectURI string + status int }{ - {"simple", []string{"google.com"}, "https://google.com", http.StatusOK}, - {"bad match", []string{"aol.com"}, "https://google.com", http.StatusBadRequest}, - {"with cname", []string{"google.com"}, "https://www.google.com", http.StatusOK}, - {"with path", []string{"google.com"}, "https://www.google.com/path", http.StatusOK}, - {"http", []string{"google.com"}, "http://www.google.com/path", http.StatusOK}, - {"malformed, invalid hex digits", []string{"google.com"}, "%zzzzz", http.StatusBadRequest}, + {"simple", "https://auth.google.com", "https://b.google.com", http.StatusOK}, + {"deep ok", "https://a.some.really.deep.sub.domain.google.com", "https://b.some.really.deep.sub.domain.google.com", http.StatusOK}, + {"bad match", "https://auth.aol.com", "https://test.google.com", http.StatusBadRequest}, + {"bad simple", "https://auth.corp.aol.com", "https://test.corp.google.com", http.StatusBadRequest}, + {"deep bad", "https://a.some.really.deep.sub.domain.scroogle.com", "https://b.some.really.deep.sub.domain.google.com", http.StatusBadRequest}, + {"with cname", "https://auth.google.com", "https://www.google.com", http.StatusOK}, + {"with path", "https://auth.google.com", "https://www.google.com/path", http.StatusOK}, + {"http mistmatch", "https://auth.google.com", "http://www.google.com/path", http.StatusOK}, + {"http", "http://auth.google.com", "http://www.google.com/path", http.StatusOK}, + {"ip", "http://1.1.1.1", "http://8.8.8.8", http.StatusBadRequest}, + {"malformed, invalid hex digits", "https://auth.google.com", "%zzzzz", http.StatusBadRequest}, } for _, tt := range tests { @@ -141,7 +150,8 @@ func TestValidateRedirectURI(t *testing.T) { w.Write([]byte("Hi")) }) rr := httptest.NewRecorder() - handler := ValidateRedirectURI(tt.proxyRootDomains)(testHandler) + u, _ := url.Parse(tt.rootDomain) + handler := ValidateRedirectURI(u)(testHandler) handler.ServeHTTP(rr, req) if rr.Code != tt.status { t.Errorf("Status code differs. got %d want %d", rr.Code, tt.status) diff --git a/policy.example.yaml b/policy.example.yaml index f91b4b032..210e14fcc 100644 --- a/policy.example.yaml +++ b/policy.example.yaml @@ -1,19 +1,19 @@ - from: httpbin.corp.beyondperimeter.com to: http://httpbin allowed_domains: - - pomerium.io + - pomerium.io - from: external-httpbin.corp.beyondperimeter.com to: httpbin.org allowed_domains: - - gmail.com + - gmail.com - from: weirdlyssl.corp.beyondperimeter.com to: http://neverssl.com allowed_users: - - bdd@pomerium.io + - bdd@pomerium.io allowed_groups: - - admins - - developers + - admins + - developers - from: hello.corp.beyondperimeter.com to: http://hello:8080 allowed_groups: - - admins + - admins diff --git a/scripts/helm_gke.sh b/scripts/helm_gke.sh index 940e3b5d6..ec147260b 100755 --- a/scripts/helm_gke.sh +++ b/scripts/helm_gke.sh @@ -37,7 +37,6 @@ echo " replace configuration settings to meet your specific needs and identity p helm install ./helm/ \ --set service.type="NodePort" \ - --set config.rootDomain="corp.pomerium.io" \ --set ingress.secret.name="pomerium-tls" \ --set ingress.secret.cert=$(base64 -i "$HOME/.acme.sh/*.corp.pomerium.io_ecc/*.corp.pomerium.io.cer") \ --set ingress.secret.key=$(base64 -i "$HOME/.acme.sh/*.corp.pomerium.io_ecc/*.corp.pomerium.io.key") \