Allow empty policies at startup

This commit is contained in:
Travis Groth 2019-07-01 21:23:14 -04:00
parent b8463e30c1
commit 989062db8e
9 changed files with 30 additions and 11 deletions

View file

@ -10,6 +10,7 @@
### CHANGED
- Proxy's sign out handler `{}/.pomerium/sign_out` now accepts an optional `redirect_uri` parameter which can be used to specify a custom redirect page, so long as it is under the same top-level domain. [GH-183]
- Policy configuration can now be empty at startup [GH-190]
### FIXED

View file

@ -2,7 +2,6 @@ package authorize // import "github.com/pomerium/pomerium/authorize"
import (
"encoding/base64"
"errors"
"fmt"
"github.com/pomerium/pomerium/internal/log"
@ -22,9 +21,6 @@ func ValidateOptions(o config.Options) error {
if len(decoded) != 32 {
return fmt.Errorf("authorize: `SHARED_SECRET` want 32 but got %d bytes", len(decoded))
}
if len(o.Policies) == 0 {
return errors.New("missing setting: no policies defined")
}
return nil
}

View file

@ -22,8 +22,8 @@ func TestNew(t *testing.T) {
{"bad shared secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", policies, true},
{"really bad shared secret", "sup", policies, true},
{"validation error, short secret", "AZA85podM73CjLCjViDNz1EUvvejKpWp7Hysr0knXA==", policies, true},
{"empty options", "", []policy.Policy{}, true}, // special case
{"missing policies", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", []policy.Policy{}, true}, // special case
{"empty options", "", []policy.Policy{}, true}, // special case
{"missing policies", "gXK6ggrlIW2HyKyUF9rUO4azrDgxhDPWqw9y+lJU7B8=", []policy.Policy{}, false}, // special case
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {

View file

@ -56,6 +56,13 @@ type whitelist struct {
// newIdentityWhitelistMap takes a slice of policies and creates a hashmap of identity
// authorizations per-route for each allowed group, domain, and email.
func newIdentityWhitelistMap(policies []policy.Policy, admins []string) *whitelist {
policyCount := len(policies)
if policyCount == 0 {
log.Warn().Msg("authorize: loaded configuration with no policies specified")
}
log.Info().Int("policy-count", policyCount).Msg("authorize: updated policies")
var wl whitelist
wl.access = make(map[string]bool, len(policies)*3)
for _, p := range policies {

View file

@ -50,6 +50,7 @@ func Test_IdentityWhitelistMap(t *testing.T) {
{"valid user email", []policy.Policy{{From: "example.com", AllowedEmails: []string{"user@example.com"}}}, "example.com", &Identity{Email: "user@example.com"}, nil, true},
{"invalid user email", []policy.Policy{{From: "example.com", AllowedEmails: []string{"user@example.com"}}}, "example.com", &Identity{Email: "user2@example.com"}, nil, false},
{"empty everything", []policy.Policy{{From: "example.com"}}, "example.com", &Identity{Email: "user2@example.com"}, nil, false},
{"empty policy", []policy.Policy{}, "example.com", &Identity{Email: "user@example.com"}, nil, false},
// impersonation related
{"admin not impersonating allowed", []policy.Policy{{From: "example.com", AllowedDomains: []string{"example.com"}}}, "example.com", &Identity{Email: "admin@example.com"}, []string{"admin@example.com"}, true},
{"admin not impersonating denied", []policy.Policy{{From: "example.com", AllowedDomains: []string{"example.com"}}}, "example.com", &Identity{Email: "admin@admin-domain.com"}, []string{"admin@admin-domain.com"}, false},

View file

@ -194,7 +194,8 @@ Expose a prometheus format HTTP endpoint on the specified port. Disabled by def
- Environmental Variable: `POLICY`
- Config File Key: `policy`
- Type: [base64 encoded] `string` or inline policy structure in config file
- Required
- Required
- Required to forward traffic. Pomerium will safely start without a policy configured, but will be unable to authorize or proxy traffic until the configuration is updated to contain a policy.
Policy contains route specific settings, and access control details. If you are configuring via POLICY environment variable, just the contents of the policy needs to be passed. If you are configuring via file, the policy should be present under the policy key. For example,

View file

@ -242,6 +242,7 @@ func TestProxy_router(t *testing.T) {
{"good with slash", "https://corp.example.com/", policies, nil, true},
{"good with path", "https://corp.example.com/123", policies, nil, true},
// {"multiple", "https://corp.example.com/", map[string]string{"corp.unrelated.com": "unrelated.com", "corp.example.com": "example.com"}, nil, true},
{"no policies", "https://notcorp.example.com/123", []policy.Policy{}, nil, false},
{"bad corp", "https://notcorp.example.com/123", policies, nil, false},
{"bad sub-sub", "https://notcorp.corp.example.com/123", policies, nil, false},
}
@ -280,6 +281,7 @@ func TestProxy_Proxy(t *testing.T) {
opts, optsWs := testOptionsTestServer(ts.URL), testOptionsTestServer(ts.URL)
optsCORS := testOptionsWithCORS(ts.URL)
optsPublic := testOptionsWithPublicAccess(ts.URL)
optsNoPolicies := testOptionsWithEmptyPolicies(ts.URL)
optsWs.AllowWebsockets = true
defaultHeaders, goodCORSHeaders, badCORSHeaders, headersWs := http.Header{}, http.Header{}, http.Header{}, http.Header{}
@ -327,6 +329,7 @@ func TestProxy_Proxy(t *testing.T) {
{"ws supported, ws connection", optsWs, http.MethodGet, headersWs, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
{"ws supported, http connection", optsWs, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusOK},
{"ws unsupported, ws connection", opts, http.MethodGet, headersWs, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusBadRequest},
{"No policies", optsNoPolicies, http.MethodGet, defaultHeaders, "https://httpbin.corp.example", &sessions.MockSessionStore{Session: goodSession}, clients.MockAuthenticate{ValidateResponse: true}, clients.MockAuthorize{AuthorizeResponse: true}, http.StatusNotFound},
}
for _, tt := range tests {

View file

@ -44,9 +44,6 @@ func ValidateOptions(o config.Options) error {
if len(decoded) != 32 {
return fmt.Errorf("`SHARED_SECRET` want 32 but got %d bytes", len(decoded))
}
if len(o.Policies) == 0 {
return errors.New("missing setting: no policies defined")
}
if o.AuthenticateURL.String() == "" {
return errors.New("missing setting: authenticate-service-url")
}
@ -182,6 +179,13 @@ func New(opts config.Options) (*Proxy, error) {
// UpdatePolicies updates the handlers based on the configured policies
func (p *Proxy) UpdatePolicies(opts config.Options) error {
routeConfigs := make(map[string]*routeConfig)
policyCount := len(opts.Policies)
if policyCount == 0 {
log.Warn().Msg("proxy: loaded configuration with no policies specified")
}
log.Info().Int("policy-count", policyCount).Msg("proxy: updated policies")
for _, route := range opts.Policies {
proxy := NewReverseProxy(route.Destination)
handler, err := NewReverseProxyHandler(opts, proxy, &route)

View file

@ -149,6 +149,12 @@ func testOptionsWithPublicAccessAndWhitelist(uri string) config.Options {
return opts
}
func testOptionsWithEmptyPolicies(uri string) config.Options {
opts := testOptionsTestServer(uri)
opts.Policies = []policy.Policy{}
return opts
}
func TestOptions_Validate(t *testing.T) {
good := testOptions()
badAuthURL := testOptions()
@ -191,7 +197,7 @@ func TestOptions_Validate(t *testing.T) {
{"short cookie secret", shortCookieLength, true},
{"no shared secret", badSharedKey, true},
{"invalid signing key", invalidSignKey, true},
{"missing policy", missingPolicy, true},
{"missing policy", missingPolicy, false},
{"shared secret bad base64", sharedKeyBadBas64, true},
}
for _, tt := range tests {