Merge pull request from GHSA-pvrc-wvj2-f59p

* authorize: normalize URL query params

* config: enable envoy normalize_path option

* authorize: use route id from envoy for policy evaluation
This commit is contained in:
Kenneth Jenkins 2023-05-26 13:34:21 -07:00 committed by GitHub
parent 32985aabe6
commit 2bc2be793f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 255 additions and 208 deletions

View file

@ -31,9 +31,10 @@ var notFoundOutput = &Result{
// Request contains the inputs needed for evaluation.
type Request struct {
Policy *config.Policy
HTTP RequestHTTP
Session RequestSession
IsInternal bool
Policy *config.Policy
HTTP RequestHTTP
Session RequestSession
}
// RequestHTTP is the HTTP field in the request.
@ -124,42 +125,48 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
ctx, span := trace.StartSpan(ctx, "authorize.Evaluator.Evaluate")
defer span.End()
if req.Policy == nil {
return notFoundOutput, nil
}
id, err := req.Policy.RouteID()
if err != nil {
return nil, fmt.Errorf("authorize: error computing policy route id: %w", err)
}
policyEvaluator, ok := e.policyEvaluators[id]
if !ok {
return notFoundOutput, nil
}
clientCA, err := e.getClientCA(req.Policy)
if err != nil {
return nil, err
}
isValidClientCertificate, err := isValidClientCertificate(clientCA, req.HTTP.ClientCertificate)
if err != nil {
return nil, fmt.Errorf("authorize: error validating client certificate: %w", err)
}
eg, ectx := errgroup.WithContext(ctx)
var policyOutput *PolicyResponse
eg.Go(func() error {
if req.IsInternal {
var err error
policyOutput, err = policyEvaluator.Evaluate(ectx, &PolicyRequest{
HTTP: req.HTTP,
Session: req.Session,
IsValidClientCertificate: isValidClientCertificate,
policyOutput, err = e.evaluateInternal(ctx, req)
if err != nil {
return nil, err
}
} else if req.Policy == nil {
return notFoundOutput, nil
} else {
id, err := req.Policy.RouteID()
if err != nil {
return nil, fmt.Errorf("authorize: error computing policy route id: %w", err)
}
policyEvaluator, ok := e.policyEvaluators[id]
if !ok {
return notFoundOutput, nil
}
clientCA, err := e.getClientCA(req.Policy)
if err != nil {
return nil, err
}
isValidClientCertificate, err := isValidClientCertificate(clientCA, req.HTTP.ClientCertificate)
if err != nil {
return nil, fmt.Errorf("authorize: error validating client certificate: %w", err)
}
eg.Go(func() error {
var err error
policyOutput, err = policyEvaluator.Evaluate(ectx, &PolicyRequest{
HTTP: req.HTTP,
Session: req.Session,
IsValidClientCertificate: isValidClientCertificate,
})
return err
})
return err
})
}
var headersOutput *HeadersResponse
eg.Go(func() error {
@ -170,7 +177,7 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
return err
})
err = eg.Wait()
err := eg.Wait()
if err != nil {
return nil, err
}
@ -186,6 +193,21 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
return res, nil
}
func (e *Evaluator) evaluateInternal(_ context.Context, req *Request) (*PolicyResponse, error) {
// these endpoints require a logged-in user
if req.HTTP.Path == "/.pomerium/jwt" {
if req.Session.ID == "" {
return &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated),
}, nil
}
}
return &PolicyResponse{
Allow: NewRuleResult(true, criteria.ReasonPomeriumRoute),
}, nil
}
func (e *Evaluator) getClientCA(policy *config.Policy) (string, error) {
if policy != nil && policy.TLSDownstreamClientCA != "" {
bs, err := base64.StdEncoding.DecodeString(policy.TLSDownstreamClientCA)

View file

@ -31,18 +31,20 @@ type HeadersRequest struct {
// NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy.
func NewHeadersRequestFromPolicy(policy *config.Policy) *HeadersRequest {
input := new(HeadersRequest)
input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil {
input.FromAudience = u.Hostname()
if policy != nil {
input.EnableGoogleCloudServerlessAuthentication = policy.EnableGoogleCloudServerlessAuthentication
input.EnableRoutingKey = policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_RING_HASH ||
policy.EnvoyOpts.GetLbPolicy() == envoy_config_cluster_v3.Cluster_MAGLEV
if u, err := urlutil.ParseAndValidateURL(policy.From); err == nil {
input.FromAudience = u.Hostname()
}
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
for _, wu := range policy.To {
input.ToAudience = "https://" + wu.URL.Hostname()
}
input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
}
input.KubernetesServiceAccountToken = policy.KubernetesServiceAccountToken
for _, wu := range policy.To {
input.ToAudience = "https://" + wu.URL.Hostname()
}
input.PassAccessToken = policy.GetSetAuthorizationHeader() == configpb.Route_ACCESS_TOKEN
input.PassIDToken = policy.GetSetAuthorizationHeader() == configpb.Route_ID_TOKEN
return input
}

View file

@ -111,7 +111,7 @@ func TestPolicyEvaluator(t *testing.T) {
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthorized),
Allow: NewRuleResult(false, criteria.ReasonEmailUnauthorized, criteria.ReasonUserUnauthorized),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired),
Traces: []contextutil.PolicyEvaluationTrace{{}},
}, output)
@ -172,7 +172,7 @@ func TestPolicyEvaluator(t *testing.T) {
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
Allow: NewRuleResult(false),
Deny: NewRuleResult(true, criteria.ReasonAccept),
Traces: []contextutil.PolicyEvaluationTrace{{}, {ID: "p1", Deny: true}},
}, output)
@ -203,7 +203,7 @@ func TestPolicyEvaluator(t *testing.T) {
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonNonPomeriumRoute),
Allow: NewRuleResult(false),
Deny: NewRuleResult(true, criteria.ReasonAccept, criteria.ReasonInvalidClientCertificate),
Traces: []contextutil.PolicyEvaluationTrace{{Deny: true}, {ID: "p1", Deny: true}},
}, output)
@ -289,7 +289,7 @@ func TestPolicyEvaluator(t *testing.T) {
})
require.NoError(t, err)
assert.Equal(t, &PolicyResponse{
Allow: NewRuleResult(false, criteria.ReasonNonPomeriumRoute, criteria.ReasonUserUnauthenticated),
Allow: NewRuleResult(false, criteria.ReasonUserUnauthenticated),
Deny: NewRuleResult(false, criteria.ReasonValidClientCertificateOrNoneRequired),
Traces: []contextutil.PolicyEvaluationTrace{{Allow: false}},
}, output)

View file

@ -11,6 +11,7 @@ import (
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/config/envoyconfig"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/telemetry/requestid"
@ -117,6 +118,7 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
) (*evaluator.Request, error) {
requestURL := getCheckRequestURL(in)
req := &evaluator.Request{
IsInternal: envoyconfig.ExtAuthzContextExtensionsIsInternal(in.GetAttributes().GetContextExtensions()),
HTTP: evaluator.NewRequestHTTP(
in.GetAttributes().GetRequest().GetHttp().GetMethod(),
requestURL,
@ -183,6 +185,7 @@ func getCheckRequestURL(req *envoy_service_auth_v3.CheckRequest) url.URL {
path := h.GetPath()
if idx := strings.Index(path, "?"); idx != -1 {
u.RawPath, u.RawQuery = path[:idx], path[idx+1:]
u.RawQuery = u.Query().Encode()
} else {
u.RawPath = path
}