config: use getters for authenticate, signout and forward auth urls (#2000)

This commit is contained in:
Caleb Doxsey 2021-03-19 14:49:25 -06:00 committed by GitHub
parent 1febaa82ff
commit 3690a32855
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 152 additions and 136 deletions

View file

@ -25,8 +25,8 @@ func ValidateOptions(o *config.Options) error {
if _, err := cryptutil.NewAEADCipherFromBase64(o.CookieSecret); err != nil {
return fmt.Errorf("authenticate: 'COOKIE_SECRET' invalid %w", err)
}
if err := urlutil.ValidateURL(o.AuthenticateURL); err != nil {
return fmt.Errorf("authenticate: invalid 'AUTHENTICATE_SERVICE_URL': %w", err)
if _, err := o.GetAuthenticateURL(); err != nil {
return fmt.Errorf("authenticate: 'AUTHENTICATE_SERVICE_URL' invalid: %w", err)
}
if o.Provider == "" {
return errors.New("authenticate: 'IDP_PROVIDER' is required")
@ -93,7 +93,12 @@ func (a *Authenticate) OnConfigChange(cfg *config.Config) {
}
func (a *Authenticate) updateProvider(cfg *config.Config) error {
redirectURL, _ := urlutil.DeepCopy(cfg.Options.AuthenticateURL)
u, err := cfg.Options.GetAuthenticateURL()
if err != nil {
return err
}
redirectURL, _ := urlutil.DeepCopy(u)
redirectURL.Path = cfg.Options.AuthenticateCallbackPath
// configure our identity provider

View file

@ -26,10 +26,8 @@ func newTestOptions(t *testing.T) *config.Options {
func TestOptions_Validate(t *testing.T) {
good := newTestOptions(t)
badRedirectURL := newTestOptions(t)
badRedirectURL.AuthenticateURL = nil
badScheme := newTestOptions(t)
badScheme.AuthenticateURL.Scheme = ""
badScheme.AuthenticateURLString = "BAD_SCHEME://"
emptyClientID := newTestOptions(t)
emptyClientID.ClientID = ""
emptyClientSecret := newTestOptions(t)
@ -43,7 +41,7 @@ func TestOptions_Validate(t *testing.T) {
badSharedKey := newTestOptions(t)
badSharedKey.SharedKey = ""
badAuthenticateURL := newTestOptions(t)
badAuthenticateURL.AuthenticateURL = nil
badAuthenticateURL.AuthenticateURLString = "BAD_URL"
badCallbackPath := newTestOptions(t)
badCallbackPath.AuthenticateCallbackPath = ""
@ -54,7 +52,6 @@ func TestOptions_Validate(t *testing.T) {
}{
{"minimum options", good, false},
{"nil options", &config.Options{}, true},
{"bad redirect url", badRedirectURL, true},
{"bad scheme", badScheme, true},
{"no cookie secret", emptyCookieSecret, true},
{"invalid cookie secret", invalidCookieSecret, true},
@ -79,7 +76,7 @@ func TestNew(t *testing.T) {
good.CookieName = "A"
badRedirectURL := newTestOptions(t)
badRedirectURL.AuthenticateURL = nil
badRedirectURL.AuthenticateURLString = "BAD URL"
badRedirectURL.CookieName = "B"
badProvider := newTestOptions(t)

View file

@ -250,8 +250,12 @@ func (a *Authenticate) SignOut(w http.ResponseWriter, r *http.Request) error {
rawIDToken := a.revokeSession(ctx, w, r)
redirectString := ""
if sru := a.options.Load().SignOutRedirectURL; sru != nil {
redirectString = sru.String()
signOutURL, err := a.options.Load().GetSignOutRedirectURL()
if err != nil {
return err
}
if signOutURL != nil {
redirectString = signOutURL.String()
}
if uri := r.FormValue(urlutil.QueryRedirectURI); uri != "" {
redirectString = uri

View file

@ -274,7 +274,7 @@ func TestAuthenticate_SignOut(t *testing.T) {
}
if tt.signoutRedirectURL != "" {
opts := a.options.Load()
opts.SignOutRedirectURL, _ = url.Parse(tt.signoutRedirectURL)
opts.SignOutRedirectURLString = tt.signoutRedirectURL
a.options.Store(opts)
}
a.provider.Store(tt.provider)

View file

@ -65,7 +65,11 @@ func newAuthenticateStateFromConfig(cfg *config.Config) (*authenticateState, err
state := &authenticateState{}
state.redirectURL, _ = urlutil.DeepCopy(cfg.Options.AuthenticateURL)
authenticateURL, err := cfg.Options.GetAuthenticateURL()
if err != nil {
return nil, err
}
state.redirectURL, _ = urlutil.DeepCopy(authenticateURL)
state.redirectURL.Path = cfg.Options.AuthenticateCallbackPath
// shared state encoder setup

View file

@ -13,7 +13,6 @@ import (
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/metrics"
"github.com/pomerium/pomerium/internal/telemetry/trace"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
@ -65,7 +64,7 @@ func validateOptions(o *config.Options) error {
if _, err := cryptutil.NewAEADCipherFromBase64(o.SharedKey); err != nil {
return fmt.Errorf("authorize: bad 'SHARED_SECRET': %w", err)
}
if err := urlutil.ValidateURL(o.AuthenticateURL); err != nil {
if _, err := o.GetAuthenticateURL(); err != nil {
return fmt.Errorf("authorize: invalid 'AUTHENTICATE_SERVICE_URL': %w", err)
}
return nil

View file

@ -20,7 +20,7 @@ func TestNew(t *testing.T) {
{
"good",
config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: "2p/Wi2Q6bYDfzmoSEbKqYKtg+DUoLWTEHHs7vOhvL7w=",
Policies: policies,
@ -30,7 +30,7 @@ func TestNew(t *testing.T) {
{
"bad shared secret",
config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==",
Policies: policies,
@ -40,7 +40,7 @@ func TestNew(t *testing.T) {
{
"really bad shared secret",
config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: "sup",
Policies: policies,
@ -50,7 +50,7 @@ func TestNew(t *testing.T) {
{
"validation error, short secret",
config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==",
Policies: policies,
@ -61,7 +61,7 @@ func TestNew(t *testing.T) {
{
"bad databroker url",
config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "BAD",
SharedKey: "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==",
Policies: policies,
@ -99,7 +99,7 @@ func TestAuthorize_OnConfigChange(t *testing.T) {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
o := &config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: tc.SharedKey,
Policies: tc.Policies,

View file

@ -26,7 +26,7 @@ import (
func TestAuthorize_okResponse(t *testing.T) {
opt := &config.Options{
AuthenticateURL: mustParseURL("https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
Policies: []config.Policy{{
Source: &config.StringURL{URL: &url.URL{Host: "example.com"}},
SubPolicies: []config.SubPolicy{{

View file

@ -43,7 +43,12 @@ func New(options *config.Options, store *Store) (*Evaluator, error) {
return nil, fmt.Errorf("error loading rego policy: %w", err)
}
store.UpdateIssuer(options.AuthenticateURL.Host)
authenticateURL, err := options.GetAuthenticateURL()
if err != nil {
return nil, fmt.Errorf("authorize: invalid authenticate URL: %w", err)
}
store.UpdateIssuer(authenticateURL.Host)
store.UpdateGoogleCloudServerlessAuthenticationServiceAccount(options.GoogleCloudServerlessAuthenticationServiceAccount)
store.UpdateJWTClaimHeaders(options.JWTClaimsHeaders)
store.UpdateRoutePolicies(options.GetAllPolicies())

View file

@ -22,7 +22,7 @@ import (
func TestJSONMarshal(t *testing.T) {
opt := config.NewDefaultOptions()
opt.AuthenticateURL = mustParseURL("https://authenticate.example.com")
opt.AuthenticateURLString = "https://authenticate.example.com"
e, err := New(opt, NewStoreFromProtos(
&session.Session{
UserId: "user1",
@ -133,7 +133,7 @@ func TestEvaluator_Evaluate(t *testing.T) {
})
e, err := New(&config.Options{
AuthenticateURL: mustParseURL("https://authn.example.com"),
AuthenticateURLString: "https://authn.example.com",
Policies: tc.policies,
}, store)
require.NoError(t, err)
@ -160,7 +160,7 @@ func mustParseURL(str string) *url.URL {
func BenchmarkEvaluator_Evaluate(b *testing.B) {
store := NewStore()
e, err := New(&config.Options{
AuthenticateURL: mustParseURL("https://authn.example.com"),
AuthenticateURLString: "https://authn.example.com",
}, store)
if !assert.NoError(b, err) {
return

View file

@ -182,12 +182,8 @@ func getForwardAuthURL(r *http.Request) *url.URL {
func (a *Authorize) isForwardAuth(req *envoy_service_auth_v3.CheckRequest) bool {
opts := a.currentOptions.Load()
if opts.ForwardAuthURL == nil {
return false
}
forwardAuthURL, err := opts.GetForwardAuthURL()
if err != nil {
if err != nil || forwardAuthURL == nil {
return false
}

View file

@ -251,11 +251,7 @@ func Test_handleForwardAuth(t *testing.T) {
tc := tc
t.Run(tc.name, func(t *testing.T) {
a := &Authorize{currentOptions: config.NewAtomicOptions(), state: newAtomicAuthorizeState(new(authorizeState))}
var fau *url.URL
if tc.forwardAuthURL != "" {
fau = mustParseURL(tc.forwardAuthURL)
}
a.currentOptions.Store(&config.Options{ForwardAuthURL: fau})
a.currentOptions.Store(&config.Options{ForwardAuthURLString: tc.forwardAuthURL})
got := a.isForwardAuth(tc.checkReq)
@ -361,7 +357,7 @@ func TestSync(t *testing.T) {
},
}
o := &config.Options{
AuthenticateURL: mustParseURL("https://authN.example.com"),
AuthenticateURLString: "https://authN.example.com",
DataBrokerURLString: "https://databroker.example.com",
SharedKey: "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=",
Policies: testPolicies(t),
@ -443,14 +439,6 @@ func TestSync(t *testing.T) {
}
}
func mustParseURL(str string) *url.URL {
u, err := url.Parse(str)
if err != nil {
panic(err)
}
return u
}
type mockDataBrokerServiceClient struct {
databroker.DataBrokerServiceClient
@ -463,14 +451,14 @@ func (m mockDataBrokerServiceClient) Get(ctx context.Context, in *databroker.Get
func TestAuthorize_Check(t *testing.T) {
opt := config.NewDefaultOptions()
opt.AuthenticateURL = mustParseURL("https://authenticate.example.com")
opt.AuthenticateURLString = "https://authenticate.example.com"
opt.DataBrokerURLString = "https://databroker.example.com"
opt.SharedKey = "E8wWIMnihUx+AUfRegAQDNs8eRb3UrB5G3zlJW9XJDM="
a, err := New(&config.Config{Options: opt})
if err != nil {
t.Fatal(err)
}
a.currentOptions.Store(&config.Options{ForwardAuthURL: mustParseURL("https://forward-auth.example.com")})
a.currentOptions.Store(&config.Options{ForwardAuthURLString: "https://forward-auth.example.com"})
cmpOpts := []cmp.Option{
cmpopts.IgnoreUnexported(envoy_service_auth_v3.CheckResponse{}),

View file

@ -116,10 +116,8 @@ type Options struct {
// AuthenticateURL represents the externally accessible http endpoints
// used for authentication requests and callbacks
AuthenticateURLString string `mapstructure:"authenticate_service_url" yaml:"authenticate_service_url,omitempty"`
AuthenticateURL *url.URL `yaml:"-"`
// SignOutRedirectURL represents the url that user will be redirected to after signing out.
SignOutRedirectURLString string `mapstructure:"signout_redirect_url" yaml:"signout_redirect_url,omitempty"`
SignOutRedirectURL *url.URL `yaml:"-"`
// AuthenticateCallbackPath is the path to the HTTP endpoint that will
// receive the response from your identity provider. The value must exactly
@ -243,7 +241,6 @@ type Options struct {
// with an external server or service. Pomerium can be configured to accept
// these requests with this switch
ForwardAuthURLString string `mapstructure:"forward_auth_url" yaml:"forward_auth_url,omitempty"`
ForwardAuthURL *url.URL `yaml:",omitempty"`
// DataBrokerURLString is the routable destination of the databroker service's gRPC endpiont.
DataBrokerURLString string `mapstructure:"databroker_service_url" yaml:"databroker_service_url,omitempty"`
@ -544,19 +541,17 @@ func (o *Options) Validate() error {
}
if o.AuthenticateURLString != "" {
u, err := urlutil.ParseAndValidateURL(o.AuthenticateURLString)
_, err := urlutil.ParseAndValidateURL(o.AuthenticateURLString)
if err != nil {
return fmt.Errorf("config: bad authenticate-url %s : %w", o.AuthenticateURLString, err)
}
o.AuthenticateURL = u
}
if o.SignOutRedirectURLString != "" {
u, err := urlutil.ParseAndValidateURL(o.SignOutRedirectURLString)
_, err := urlutil.ParseAndValidateURL(o.SignOutRedirectURLString)
if err != nil {
return fmt.Errorf("config: bad signout-redirect-url %s : %w", o.SignOutRedirectURLString, err)
}
o.SignOutRedirectURL = u
}
if o.AuthorizeURLString != "" {
@ -574,11 +569,10 @@ func (o *Options) Validate() error {
}
if o.ForwardAuthURLString != "" {
u, err := urlutil.ParseAndValidateURL(o.ForwardAuthURLString)
_, err := urlutil.ParseAndValidateURL(o.ForwardAuthURLString)
if err != nil {
return fmt.Errorf("config: bad forward-auth-url %s : %w", o.ForwardAuthURLString, err)
}
o.ForwardAuthURL = u
}
if o.PolicyFile != "" {
@ -735,10 +729,11 @@ func (o *Options) Validate() error {
// GetAuthenticateURL returns the AuthenticateURL in the options or 127.0.0.1.
func (o *Options) GetAuthenticateURL() (*url.URL, error) {
if o != nil && o.AuthenticateURL != nil {
return o.AuthenticateURL, nil
rawurl := o.AuthenticateURLString
if rawurl == "" {
rawurl = "https://127.0.0.1"
}
return url.Parse("https://127.0.0.1")
return urlutil.ParseAndValidateURL(rawurl)
}
// GetAuthorizeURLs returns the AuthorizeURLs in the options or 127.0.0.1:5443.
@ -772,12 +767,22 @@ func (o *Options) getURLs(strs ...string) ([]*url.URL, error) {
return urls, nil
}
// GetForwardAuthURL returns the ForwardAuthURL in the options or 127.0.0.1.
// GetForwardAuthURL returns the ForwardAuthURL.
func (o *Options) GetForwardAuthURL() (*url.URL, error) {
if o != nil && o.ForwardAuthURL != nil {
return o.ForwardAuthURL, nil
rawurl := o.ForwardAuthURLString
if rawurl == "" {
return nil, nil
}
return url.Parse("https://127.0.0.1")
return urlutil.ParseAndValidateURL(rawurl)
}
// GetSignOutRedirectURL gets the SignOutRedirectURL.
func (o *Options) GetSignOutRedirectURL() (*url.URL, error) {
rawurl := o.SignOutRedirectURLString
if rawurl == "" {
return nil, nil
}
return urlutil.ParseAndValidateURL(rawurl)
}
// GetMetricsCertificate returns the metrics certificate to use for TLS. `nil` will be

View file

@ -516,10 +516,10 @@ func TestOptions_DefaultURL(t *testing.T) {
defaultOptions := &Options{}
opts := &Options{
AuthenticateURL: mustParseURL("https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthorizeURLString: "https://authorize.example.com",
DataBrokerURLString: "https://databroker.example.com",
ForwardAuthURL: mustParseURL("https://forwardauth.example.com"),
ForwardAuthURLString: "https://forwardauth.example.com",
}
tests := []struct {
name string
@ -547,21 +547,15 @@ func TestOptions_DefaultURL(t *testing.T) {
}
}
func mustParseURL(str string) *url.URL {
u, err := url.Parse(str)
if err != nil {
panic(err)
}
return u
}
func TestOptions_GetOauthOptions(t *testing.T) {
opts := &Options{AuthenticateURL: mustParseURL("https://authenticate.example.com")}
opts := &Options{AuthenticateURLString: "https://authenticate.example.com"}
oauthOptions, err := opts.GetOauthOptions()
require.NoError(t, err)
// Test that oauth redirect url hostname must point to authenticate url hostname.
assert.Equal(t, opts.AuthenticateURL.Hostname(), oauthOptions.RedirectURL.Hostname())
u, err := opts.GetAuthenticateURL()
require.NoError(t, err)
assert.Equal(t, u.Hostname(), oauthOptions.RedirectURL.Hostname())
}
func mustParseWeightedURLs(t *testing.T, urls ...string) []WeightedURL {

View file

@ -289,8 +289,11 @@ func sourceHostnames(cfg *config.Config) []string {
for _, p := range policies {
dedupe[p.Source.Hostname()] = struct{}{}
}
if cfg.Options.AuthenticateURL != nil {
dedupe[cfg.Options.AuthenticateURL.Hostname()] = struct{}{}
if cfg.Options.AuthenticateURLString != "" {
u, _ := cfg.Options.GetAuthenticateURL()
if u != nil {
dedupe[u.Hostname()] = struct{}{}
}
}
var h []string

View file

@ -709,7 +709,7 @@ func getAllRouteableDomains(options *config.Options, addr string) ([]string, err
lookup[h] = struct{}{}
}
}
if options.ForwardAuthURL != nil {
if forwardAuthURL != nil {
for _, h := range urlutil.GetDomainsForURL(*forwardAuthURL) {
lookup[h] = struct{}{}
}
@ -759,6 +759,10 @@ func hostsMatchDomain(urls []*url.URL, host string) bool {
}
func hostMatchesDomain(u *url.URL, host string) bool {
if u == nil {
return false
}
var defaultPort string
if u.Scheme == "http" {
defaultPort = "80"

View file

@ -594,7 +594,7 @@ func Test_getAllDomains(t *testing.T) {
Addr: "127.0.0.1:9000",
GRPCAddr: "127.0.0.1:9001",
Services: "all",
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthorizeURLString: "https://authorize.example.com:9001",
DataBrokerURLString: "https://cache.example.com:9001",
Policies: []config.Policy{

View file

@ -122,7 +122,7 @@ func (srv *Server) buildPomeriumHTTPRoutes(options *config.Options, domain strin
if err != nil {
return nil, err
}
if config.IsProxy(options.Services) && options.ForwardAuthURL != nil && hostMatchesDomain(forwardAuthURL, domain) {
if config.IsProxy(options.Services) && hostMatchesDomain(forwardAuthURL, domain) {
// disable ext_authz and pass request to proxy handlers that enable authN flow
r, err := srv.buildControlPlanePathAndQueryRoute("/verify", []string{urlutil.QueryForwardAuthURI, urlutil.QuerySessionEncrypted, urlutil.QueryRedirectURI})
if err != nil {

View file

@ -80,9 +80,9 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
t.Run("authenticate", func(t *testing.T) {
options := &config.Options{
Services: "all",
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthenticateCallbackPath: "/oauth2/callback",
ForwardAuthURL: mustParseURL(t, "https://forward-auth.example.com"),
ForwardAuthURLString: "https://forward-auth.example.com",
}
routes, err := srv.buildPomeriumHTTPRoutes(options, "authenticate.example.com")
require.NoError(t, err)
@ -102,7 +102,7 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
t.Run("proxy fronting authenticate", func(t *testing.T) {
options := &config.Options{
Services: "proxy",
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthenticateCallbackPath: "/oauth2/callback",
}
routes, err := srv.buildPomeriumHTTPRoutes(options, "authenticate.example.com")
@ -113,9 +113,9 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
t.Run("with robots", func(t *testing.T) {
options := &config.Options{
Services: "all",
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthenticateCallbackPath: "/oauth2/callback",
ForwardAuthURL: mustParseURL(t, "https://forward-auth.example.com"),
ForwardAuthURLString: "https://forward-auth.example.com",
Policies: []config.Policy{{
From: "https://from.example.com",
To: mustParseWeightedURLs(t, "https://to.example.com"),
@ -140,9 +140,9 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
t.Run("without robots", func(t *testing.T) {
options := &config.Options{
Services: "all",
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
AuthenticateCallbackPath: "/oauth2/callback",
ForwardAuthURL: mustParseURL(t, "https://forward-auth.example.com"),
ForwardAuthURLString: "https://forward-auth.example.com",
Policies: []config.Policy{{
From: "https://from.example.com",
To: mustParseWeightedURLs(t, "https://to.example.com"),
@ -504,7 +504,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
t.Run("fronting-authenticate", func(t *testing.T) {
routes, err := srv.buildPolicyRoutes(&config.Options{
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
AuthenticateURLString: "https://authenticate.example.com",
Services: "proxy",
CookieName: "pomerium",
DefaultUpstreamTimeout: time.Second * 3,

View file

@ -23,7 +23,7 @@ func (p *Proxy) registerDashboardHandlers(r *mux.Router) *mux.Router {
// special pomerium endpoints for users to view their session
h.Path("/").HandlerFunc(p.userInfo).Methods(http.MethodGet)
h.Path("/sign_out").HandlerFunc(p.SignOut).Methods(http.MethodGet, http.MethodPost)
h.Path("/sign_out").Handler(httputil.HandlerFunc(p.SignOut)).Methods(http.MethodGet, http.MethodPost)
h.Path("/jwt").Handler(httputil.HandlerFunc(p.jwtAssertion)).Methods(http.MethodGet)
// called following authenticate auth flow to grab a new or existing session
@ -54,12 +54,16 @@ func (p *Proxy) RobotsTxt(w http.ResponseWriter, _ *http.Request) {
// SignOut clears the local session and redirects the request to the sign out url.
// It's the responsibility of the authenticate service to revoke the remote session and clear
// the authenticate service's session state.
func (p *Proxy) SignOut(w http.ResponseWriter, r *http.Request) {
func (p *Proxy) SignOut(w http.ResponseWriter, r *http.Request) error {
state := p.state.Load()
redirectURL := &url.URL{Scheme: "https", Host: r.Host, Path: "/"}
if sru := p.currentOptions.Load().SignOutRedirectURL; sru != nil {
redirectURL = sru
signOutURL, err := p.currentOptions.Load().GetSignOutRedirectURL()
if err != nil {
return httputil.NewError(http.StatusInternalServerError, err)
}
if signOutURL != nil {
redirectURL = signOutURL
}
if uri, err := urlutil.ParseAndValidateURL(r.FormValue(urlutil.QueryRedirectURI)); err == nil && uri.String() != "" {
redirectURL = uri
@ -72,6 +76,7 @@ func (p *Proxy) SignOut(w http.ResponseWriter, r *http.Request) {
state.sessionStore.ClearSession(w, r)
httputil.Redirect(w, r, urlutil.NewSignedURL(state.sharedKey, &dashboardURL).String(), http.StatusFound)
return nil
}
func (p *Proxy) userInfo(w http.ResponseWriter, r *http.Request) {

View file

@ -17,7 +17,6 @@ import (
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/metrics"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
@ -39,7 +38,7 @@ func ValidateOptions(o *config.Options) error {
return fmt.Errorf("proxy: invalid 'COOKIE_SECRET': %w", err)
}
if err := urlutil.ValidateURL(o.AuthenticateURL); err != nil {
if _, err := o.GetAuthenticateURL(); err != nil {
return fmt.Errorf("proxy: invalid 'AUTHENTICATE_SERVICE_URL': %w", err)
}
@ -83,7 +82,9 @@ func (p *Proxy) OnConfigChange(cfg *config.Config) {
}
p.currentOptions.Store(cfg.Options)
p.setHandlers(cfg.Options)
if err := p.setHandlers(cfg.Options); err != nil {
log.Error().Err(err).Msg("proxy: failed to update proxy handlers from configuration settings")
}
if state, err := newProxyStateFromConfig(cfg); err != nil {
log.Error().Err(err).Msg("proxy: failed to update proxy state from configuration settings")
} else {
@ -91,7 +92,7 @@ func (p *Proxy) OnConfigChange(cfg *config.Config) {
}
}
func (p *Proxy) setHandlers(opts *config.Options) {
func (p *Proxy) setHandlers(opts *config.Options) error {
if len(opts.GetAllPolicies()) == 0 {
log.Warn().Msg("proxy: configuration has no policies")
}
@ -105,13 +106,18 @@ func (p *Proxy) setHandlers(opts *config.Options) {
// dashboard handlers are registered to all routes
r = p.registerDashboardHandlers(r)
if opts.ForwardAuthURL != nil {
forwardAuthURL, err := opts.GetForwardAuthURL()
if err != nil {
return err
}
if forwardAuthURL != nil {
// if a forward auth endpoint is set, register its handlers
h := r.Host(opts.ForwardAuthURL.Hostname()).Subrouter()
h := r.Host(forwardAuthURL.Hostname()).Subrouter()
h.PathPrefix("/").Handler(p.registerFwdAuthHandlers())
}
p.currentRouter.Store(r)
return nil
}
func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {

View file

@ -37,10 +37,9 @@ func TestOptions_Validate(t *testing.T) {
good := testOptions(t)
badAuthURL := testOptions(t)
badAuthURL.AuthenticateURL = nil
authurl, _ := url.Parse("authenticate.corp.beyondperimeter.com")
badAuthURL.AuthenticateURLString = "BAD_URL"
authenticateBadScheme := testOptions(t)
authenticateBadScheme.AuthenticateURL = authurl
authenticateBadScheme.AuthenticateURLString = "authenticate.corp.beyondperimeter.com"
emptyCookieSecret := testOptions(t)
emptyCookieSecret.CookieSecret = ""
invalidCookieSecret := testOptions(t)
@ -161,7 +160,7 @@ func Test_UpdateOptions(t *testing.T) {
disableAuth := testOptions(t)
disableAuth.Policies = []config.Policy{{To: toFoo, From: "http://bar.example", AllowPublicUnauthenticatedAccess: true}}
fwdAuth := testOptions(t)
fwdAuth.ForwardAuthURL = &url.URL{Scheme: "https", Host: "corp.example.example"}
fwdAuth.ForwardAuthURLString = "https://corp.example.example"
reqHeaders := testOptions(t)
reqHeaders.Policies = []config.Policy{{To: toFoo, From: "http://bar.example", SetRequestHeaders: map[string]string{"x": "y"}}}
preserveHostHeader := testOptions(t)

View file

@ -15,7 +15,6 @@ import (
"github.com/pomerium/pomerium/internal/sessions/cookie"
"github.com/pomerium/pomerium/internal/sessions/header"
"github.com/pomerium/pomerium/internal/sessions/queryparam"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/cryptutil"
)
@ -57,7 +56,10 @@ func newProxyStateFromConfig(cfg *config.Config) (*proxyState, error) {
state.jwtClaimHeaders = cfg.Options.JWTClaimsHeaders
// errors checked in ValidateOptions
state.authenticateURL, _ = urlutil.DeepCopy(cfg.Options.AuthenticateURL)
state.authenticateURL, err = cfg.Options.GetAuthenticateURL()
if err != nil {
return nil, err
}
state.authenticateDashboardURL = state.authenticateURL.ResolveReference(&url.URL{Path: dashboardPath})
state.authenticateSigninURL = state.authenticateURL.ResolveReference(&url.URL{Path: signinURL})
state.authenticateRefreshURL = state.authenticateURL.ResolveReference(&url.URL{Path: refreshURL})