mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-03 16:59:22 +02:00
Optimize policy iterators (#5184)
* Optimize policy iterators (go1.23) This modifies (*Options).GetAllPolicies() to use a go 1.23 iterator instead of copying all policies on every call, which can be extremely expensive. All existing usages of this function were updated as necessary. Additionally, a new (*Options).NumPolicies() method was added which quickly computes the number of policies that would be given by GetAllPolicies(), since there were several usages where only the number of policies was needed. * Fix race condition when assigning default envoy opts to a policy
This commit is contained in:
parent
3961098681
commit
56ba07e53e
16 changed files with 136 additions and 87 deletions
|
@ -5,6 +5,7 @@ package authorize
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -91,7 +92,7 @@ func newPolicyEvaluator(
|
|||
opts *config.Options, store *store.Store, previous *evaluator.Evaluator,
|
||||
) (*evaluator.Evaluator, error) {
|
||||
metrics.AddPolicyCountCallback("pomerium-authorize", func() int64 {
|
||||
return int64(len(opts.GetAllPolicies()))
|
||||
return int64(opts.NumPolicies())
|
||||
})
|
||||
ctx := log.WithContext(context.Background(), func(c zerolog.Context) zerolog.Context {
|
||||
return c.Str("service", "authorize")
|
||||
|
@ -131,8 +132,9 @@ func newPolicyEvaluator(
|
|||
"authorize: internal error: couldn't build client cert constraints: %w", err)
|
||||
}
|
||||
|
||||
allPolicies := slices.Collect(opts.GetAllPolicies())
|
||||
return evaluator.New(ctx, store, previous,
|
||||
evaluator.WithPolicies(opts.GetAllPolicies()),
|
||||
evaluator.WithPolicies(allPolicies),
|
||||
evaluator.WithClientCA(clientCA),
|
||||
evaluator.WithAddDefaultClientCertificateRule(addDefaultClientCertificateRule),
|
||||
evaluator.WithClientCRL(clientCRL),
|
||||
|
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
type evaluatorConfig struct {
|
||||
Policies []config.Policy `hash:"-"`
|
||||
Policies []*config.Policy `hash:"-"`
|
||||
ClientCA []byte
|
||||
ClientCRL []byte
|
||||
AddDefaultClientCertificateRule bool
|
||||
|
@ -34,7 +34,7 @@ func getConfig(options ...Option) *evaluatorConfig {
|
|||
}
|
||||
|
||||
// WithPolicies sets the policies in the config.
|
||||
func WithPolicies(policies []config.Policy) Option {
|
||||
func WithPolicies(policies []*config.Policy) Option {
|
||||
return func(cfg *evaluatorConfig) {
|
||||
cfg.Policies = policies
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ func getOrCreatePolicyEvaluators(
|
|||
continue
|
||||
}
|
||||
builders = append(builders, func(ctx context.Context) (*routeEvaluator, error) {
|
||||
evaluator, err := NewPolicyEvaluator(ctx, store, &configPolicy, cfg.AddDefaultClientCertificateRule)
|
||||
evaluator, err := NewPolicyEvaluator(ctx, store, configPolicy, cfg.AddDefaultClientCertificateRule)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("authorize: error building evaluator for route id=%s: %w", configPolicy.ID, err)
|
||||
}
|
||||
|
|
|
@ -41,7 +41,7 @@ func TestEvaluator(t *testing.T) {
|
|||
return e.Evaluate(ctx, req)
|
||||
}
|
||||
|
||||
policies := []config.Policy{
|
||||
policies := []*config.Policy{
|
||||
{
|
||||
To: config.WeightedURLs{{URL: *mustParseURL("https://to1.example.com")}},
|
||||
AllowPublicUnauthenticatedAccess: true,
|
||||
|
@ -145,14 +145,14 @@ func TestEvaluator(t *testing.T) {
|
|||
WithAddDefaultClientCertificateRule(true))
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[0],
|
||||
Policy: policies[0],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, NewRuleResult(true, criteria.ReasonClientCertificateRequired), res.Deny)
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[0],
|
||||
Policy: policies[0],
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: ClientCertificateInfo{Presented: true},
|
||||
},
|
||||
|
@ -162,7 +162,7 @@ func TestEvaluator(t *testing.T) {
|
|||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[0],
|
||||
Policy: policies[0],
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: validCertInfo,
|
||||
},
|
||||
|
@ -177,14 +177,14 @@ func TestEvaluator(t *testing.T) {
|
|||
options = append(options, WithAddDefaultClientCertificateRule(true))
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[10],
|
||||
Policy: policies[10],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, NewRuleResult(true, criteria.ReasonClientCertificateRequired), res.Deny)
|
||||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[10],
|
||||
Policy: policies[10],
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: ClientCertificateInfo{
|
||||
Presented: true,
|
||||
|
@ -197,7 +197,7 @@ func TestEvaluator(t *testing.T) {
|
|||
})
|
||||
t.Run("valid", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[10],
|
||||
Policy: policies[10],
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: validCertInfo,
|
||||
},
|
||||
|
@ -213,7 +213,7 @@ func TestEvaluator(t *testing.T) {
|
|||
options = append(options, WithClientCA([]byte(testCA)))
|
||||
t.Run("invalid but allowed", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[0], // no explicit deny rule
|
||||
Policy: policies[0], // no explicit deny rule
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: ClientCertificateInfo{
|
||||
Presented: true,
|
||||
|
@ -226,7 +226,7 @@ func TestEvaluator(t *testing.T) {
|
|||
})
|
||||
t.Run("invalid", func(t *testing.T) {
|
||||
res, err := eval(t, options, nil, &Request{
|
||||
Policy: &policies[11], // policy has explicit deny rule
|
||||
Policy: policies[11], // policy has explicit deny rule
|
||||
HTTP: RequestHTTP{
|
||||
ClientCertificate: ClientCertificateInfo{
|
||||
Presented: true,
|
||||
|
@ -250,7 +250,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[1],
|
||||
Policy: policies[1],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -274,7 +274,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[2],
|
||||
Policy: policies[2],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -300,7 +300,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[3],
|
||||
Policy: policies[3],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -323,7 +323,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[4],
|
||||
Policy: policies[4],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -346,7 +346,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "b@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[3],
|
||||
Policy: policies[3],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -376,7 +376,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[3],
|
||||
Policy: policies[3],
|
||||
Session: RequestSession{
|
||||
ID: "session2",
|
||||
},
|
||||
|
@ -400,7 +400,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[5],
|
||||
Policy: policies[5],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -423,7 +423,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[6],
|
||||
Policy: policies[6],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -451,7 +451,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Email: "a@example.com",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[6],
|
||||
Policy: policies[6],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -473,7 +473,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Id: "user1",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[7],
|
||||
Policy: policies[7],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -509,7 +509,7 @@ func TestEvaluator(t *testing.T) {
|
|||
Id: "user1",
|
||||
},
|
||||
}, &Request{
|
||||
Policy: &policies[8],
|
||||
Policy: policies[8],
|
||||
Session: RequestSession{
|
||||
ID: "session1",
|
||||
},
|
||||
|
@ -526,7 +526,7 @@ func TestEvaluator(t *testing.T) {
|
|||
})
|
||||
t.Run("http method", func(t *testing.T) {
|
||||
res, err := eval(t, options, []proto.Message{}, &Request{
|
||||
Policy: &policies[8],
|
||||
Policy: policies[8],
|
||||
HTTP: NewRequestHTTP(
|
||||
http.MethodGet,
|
||||
*mustParseURL("https://from.example.com/"),
|
||||
|
@ -540,7 +540,7 @@ func TestEvaluator(t *testing.T) {
|
|||
})
|
||||
t.Run("http path", func(t *testing.T) {
|
||||
res, err := eval(t, options, []proto.Message{}, &Request{
|
||||
Policy: &policies[9],
|
||||
Policy: policies[9],
|
||||
HTTP: NewRequestHTTP(
|
||||
"POST",
|
||||
*mustParseURL("https://from.example.com/test"),
|
||||
|
@ -559,7 +559,7 @@ func TestPolicyEvaluatorReuse(t *testing.T) {
|
|||
|
||||
store := store.New()
|
||||
|
||||
policies := []config.Policy{
|
||||
policies := []*config.Policy{
|
||||
{To: singleToURL("https://to1.example.com")},
|
||||
{To: singleToURL("https://to2.example.com")},
|
||||
{To: singleToURL("https://to3.example.com")},
|
||||
|
@ -600,7 +600,7 @@ func TestPolicyEvaluatorReuse(t *testing.T) {
|
|||
e, err := New(ctx, store, initial, options...)
|
||||
require.NoError(t, err)
|
||||
for i := range policies {
|
||||
assertPolicyEvaluatorReused(t, e, &policies[i])
|
||||
assertPolicyEvaluatorReused(t, e, policies[i])
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -608,7 +608,7 @@ func TestPolicyEvaluatorReuse(t *testing.T) {
|
|||
e, err := New(ctx, store, initial, append(options, o)...)
|
||||
require.NoError(t, err)
|
||||
for i := range policies {
|
||||
assertPolicyEvaluatorUpdated(t, e, &policies[i])
|
||||
assertPolicyEvaluatorUpdated(t, e, policies[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -647,7 +647,7 @@ func TestPolicyEvaluatorReuse(t *testing.T) {
|
|||
// identical, only evaluators for the changed policies should be updated.
|
||||
t.Run("policies changed", func(t *testing.T) {
|
||||
// Make changes to some of the policies.
|
||||
newPolicies := []config.Policy{
|
||||
newPolicies := []*config.Policy{
|
||||
{To: singleToURL("https://to1.example.com")},
|
||||
{
|
||||
To: singleToURL("https://to2.example.com"),
|
||||
|
@ -662,9 +662,9 @@ func TestPolicyEvaluatorReuse(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
// Only the first and the third policy evaluators should be reused.
|
||||
assertPolicyEvaluatorReused(t, e, &newPolicies[0])
|
||||
assertPolicyEvaluatorUpdated(t, e, &newPolicies[1])
|
||||
assertPolicyEvaluatorReused(t, e, &newPolicies[2])
|
||||
assertPolicyEvaluatorReused(t, e, newPolicies[0])
|
||||
assertPolicyEvaluatorUpdated(t, e, newPolicies[1])
|
||||
assertPolicyEvaluatorReused(t, e, newPolicies[2])
|
||||
|
||||
// The last policy shouldn't correspond with any of the initial policy
|
||||
// evaluators.
|
||||
|
|
|
@ -121,10 +121,10 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
|
|||
func (a *Authorize) getMatchingPolicy(routeID uint64) *config.Policy {
|
||||
options := a.currentOptions.Load()
|
||||
|
||||
for _, p := range options.GetAllPolicies() {
|
||||
for p := range options.GetAllPolicies() {
|
||||
id, _ := p.RouteID()
|
||||
if id == routeID {
|
||||
return &p
|
||||
return p
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ func (s *Store) UpdateJWTClaimHeaders(jwtClaimHeaders map[string]string) {
|
|||
}
|
||||
|
||||
// UpdateRoutePolicies updates the route policies in the store.
|
||||
func (s *Store) UpdateRoutePolicies(routePolicies []config.Policy) {
|
||||
func (s *Store) UpdateRoutePolicies(routePolicies []*config.Policy) {
|
||||
s.write("/route_policies", routePolicies)
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue