mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-21 20:18:06 +02:00
authorize: log JWT groups filtering
This commit is contained in:
parent
8bc86fe06f
commit
e7831cc299
6 changed files with 64 additions and 16 deletions
|
@ -84,10 +84,11 @@ type RequestSession struct {
|
||||||
|
|
||||||
// Result is the result of evaluation.
|
// Result is the result of evaluation.
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Allow RuleResult
|
Allow RuleResult
|
||||||
Deny RuleResult
|
Deny RuleResult
|
||||||
Headers http.Header
|
Headers http.Header
|
||||||
Traces []contextutil.PolicyEvaluationTrace
|
Traces []contextutil.PolicyEvaluationTrace
|
||||||
|
AdditionalLogFields map[log.AuthorizeLogField]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// An Evaluator evaluates policies.
|
// An Evaluator evaluates policies.
|
||||||
|
@ -228,10 +229,11 @@ func (e *Evaluator) Evaluate(ctx context.Context, req *Request) (*Result, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
res := &Result{
|
res := &Result{
|
||||||
Allow: policyOutput.Allow,
|
Allow: policyOutput.Allow,
|
||||||
Deny: policyOutput.Deny,
|
Deny: policyOutput.Deny,
|
||||||
Headers: headersOutput.Headers,
|
Headers: headersOutput.Headers,
|
||||||
Traces: policyOutput.Traces,
|
Traces: policyOutput.Traces,
|
||||||
|
AdditionalLogFields: headersOutput.AdditionalLogFields,
|
||||||
}
|
}
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,14 @@ import (
|
||||||
"github.com/open-policy-agent/opa/rego"
|
"github.com/open-policy-agent/opa/rego"
|
||||||
|
|
||||||
"github.com/pomerium/pomerium/authorize/internal/store"
|
"github.com/pomerium/pomerium/authorize/internal/store"
|
||||||
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
"github.com/pomerium/pomerium/internal/telemetry/trace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HeadersResponse is the output from the headers.rego script.
|
// HeadersResponse is the output from the headers.rego script.
|
||||||
type HeadersResponse struct {
|
type HeadersResponse struct {
|
||||||
Headers http.Header
|
Headers http.Header
|
||||||
|
AdditionalLogFields map[log.AuthorizeLogField]any
|
||||||
}
|
}
|
||||||
|
|
||||||
// A HeadersEvaluator evaluates the headers.rego script.
|
// A HeadersEvaluator evaluates the headers.rego script.
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||||
"github.com/go-jose/go-jose/v3"
|
"github.com/go-jose/go-jose/v3"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/rs/zerolog"
|
||||||
"google.golang.org/protobuf/types/known/structpb"
|
"google.golang.org/protobuf/types/known/structpb"
|
||||||
|
|
||||||
"github.com/pomerium/datasource/pkg/directory"
|
"github.com/pomerium/datasource/pkg/directory"
|
||||||
|
@ -23,7 +24,7 @@ import (
|
||||||
"github.com/pomerium/pomerium/pkg/cryptutil"
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||||
"github.com/pomerium/pomerium/pkg/slices"
|
"github.com/pomerium/pomerium/pkg/telemetry/requestid"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A headersEvaluatorEvaluation is a single evaluation of the headers evaluator.
|
// A headersEvaluatorEvaluation is a single evaluation of the headers evaluator.
|
||||||
|
@ -57,8 +58,11 @@ func newHeadersEvaluatorEvaluation(evaluator *HeadersEvaluator, request *Request
|
||||||
return &headersEvaluatorEvaluation{
|
return &headersEvaluatorEvaluation{
|
||||||
evaluator: evaluator,
|
evaluator: evaluator,
|
||||||
request: request,
|
request: request,
|
||||||
response: &HeadersResponse{Headers: make(http.Header)},
|
response: &HeadersResponse{
|
||||||
now: now,
|
Headers: make(http.Header),
|
||||||
|
AdditionalLogFields: make(map[log.AuthorizeLogField]any),
|
||||||
|
},
|
||||||
|
now: now,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +341,8 @@ func (e *headersEvaluatorEvaluation) getFilteredGroups(ctx context.Context) []st
|
||||||
if len(filters) == 0 {
|
if len(filters) == 0 {
|
||||||
return groups
|
return groups
|
||||||
}
|
}
|
||||||
return slices.Filter(groups, func(g string) bool {
|
|
||||||
|
filterFn := func(g string) bool {
|
||||||
// A group should be included if it appears in either the global or the route-level filter list.
|
// A group should be included if it appears in either the global or the route-level filter list.
|
||||||
for _, f := range filters {
|
for _, f := range filters {
|
||||||
if f.IsAllowed(g) {
|
if f.IsAllowed(g) {
|
||||||
|
@ -345,7 +350,30 @@ func (e *headersEvaluatorEvaluation) getFilteredGroups(ctx context.Context) []st
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
})
|
}
|
||||||
|
var included []string
|
||||||
|
|
||||||
|
// Log the specifics of which groups were filtered out at Debug level.
|
||||||
|
var excluded *zerolog.Array
|
||||||
|
if log.Ctx(ctx).GetLevel() <= zerolog.DebugLevel {
|
||||||
|
excluded = zerolog.Arr()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, g := range groups {
|
||||||
|
if filterFn(g) {
|
||||||
|
included = append(included, g)
|
||||||
|
} else if excluded != nil {
|
||||||
|
excluded.Str(g)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if removedCount := len(groups) - len(included); removedCount > 0 {
|
||||||
|
log.Ctx(ctx).Debug().
|
||||||
|
Str("request-id", requestid.FromContext(ctx)).
|
||||||
|
Array("removed-groups", excluded).
|
||||||
|
Msg("group filtering removed groups")
|
||||||
|
e.response.AdditionalLogFields[log.AuthorizeLogFieldRemovedGroupsCount] = removedCount
|
||||||
|
}
|
||||||
|
return included
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAllGroups returns the full group names/IDs list (without any filtering).
|
// getAllGroups returns the full group names/IDs list (without any filtering).
|
||||||
|
|
|
@ -33,7 +33,7 @@ func (a *Authorize) logAuthorizeCheck(
|
||||||
evt := log.Ctx(ctx).Info().Str("service", "authorize")
|
evt := log.Ctx(ctx).Info().Str("service", "authorize")
|
||||||
fields := a.currentOptions.Load().GetAuthorizeLogFields()
|
fields := a.currentOptions.Load().GetAuthorizeLogFields()
|
||||||
for _, field := range fields {
|
for _, field := range fields {
|
||||||
evt = populateLogEvent(ctx, field, evt, in, s, u, hdrs, impersonateDetails)
|
evt = populateLogEvent(ctx, field, evt, in, s, u, hdrs, impersonateDetails, res)
|
||||||
}
|
}
|
||||||
evt = log.HTTPHeaders(evt, fields, hdrs)
|
evt = log.HTTPHeaders(evt, fields, hdrs)
|
||||||
|
|
||||||
|
@ -132,6 +132,7 @@ func populateLogEvent(
|
||||||
u *user.User,
|
u *user.User,
|
||||||
hdrs map[string]string,
|
hdrs map[string]string,
|
||||||
impersonateDetails *impersonateDetails,
|
impersonateDetails *impersonateDetails,
|
||||||
|
res *evaluator.Result,
|
||||||
) *zerolog.Event {
|
) *zerolog.Event {
|
||||||
path, query, _ := strings.Cut(in.GetAttributes().GetRequest().GetHttp().GetPath(), "?")
|
path, query, _ := strings.Cut(in.GetAttributes().GetRequest().GetHttp().GetPath(), "?")
|
||||||
|
|
||||||
|
@ -198,6 +199,11 @@ func populateLogEvent(
|
||||||
}
|
}
|
||||||
return evt.Str(string(field), userID)
|
return evt.Str(string(field), userID)
|
||||||
default:
|
default:
|
||||||
|
if res != nil {
|
||||||
|
if v, ok := res.AdditionalLogFields[field]; ok {
|
||||||
|
evt = evt.Interface(string(field), v)
|
||||||
|
}
|
||||||
|
}
|
||||||
return evt
|
return evt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ import (
|
||||||
"github.com/rs/zerolog"
|
"github.com/rs/zerolog"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/pomerium/pomerium/authorize/evaluator"
|
||||||
"github.com/pomerium/pomerium/internal/log"
|
"github.com/pomerium/pomerium/internal/log"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/session"
|
"github.com/pomerium/pomerium/pkg/grpc/session"
|
||||||
"github.com/pomerium/pomerium/pkg/grpc/user"
|
"github.com/pomerium/pomerium/pkg/grpc/user"
|
||||||
|
@ -64,6 +65,11 @@ func Test_populateLogEvent(t *testing.T) {
|
||||||
sessionID: "IMPERSONATE-SESSION-ID",
|
sessionID: "IMPERSONATE-SESSION-ID",
|
||||||
userID: "IMPERSONATE-USER-ID",
|
userID: "IMPERSONATE-USER-ID",
|
||||||
}
|
}
|
||||||
|
res := &evaluator.Result{
|
||||||
|
AdditionalLogFields: map[log.AuthorizeLogField]any{
|
||||||
|
log.AuthorizeLogFieldRemovedGroupsCount: 42,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
field log.AuthorizeLogField
|
field log.AuthorizeLogField
|
||||||
|
@ -82,6 +88,7 @@ func Test_populateLogEvent(t *testing.T) {
|
||||||
{log.AuthorizeLogFieldMethod, s, `{"method":"GET"}`},
|
{log.AuthorizeLogFieldMethod, s, `{"method":"GET"}`},
|
||||||
{log.AuthorizeLogFieldPath, s, `{"path":"https://www.example.com/some/path"}`},
|
{log.AuthorizeLogFieldPath, s, `{"path":"https://www.example.com/some/path"}`},
|
||||||
{log.AuthorizeLogFieldQuery, s, `{"query":"a=b"}`},
|
{log.AuthorizeLogFieldQuery, s, `{"query":"a=b"}`},
|
||||||
|
{log.AuthorizeLogFieldRemovedGroupsCount, s, `{"removed-groups-count":42}`},
|
||||||
{log.AuthorizeLogFieldRequestID, s, `{"request-id":"REQUEST-ID"}`},
|
{log.AuthorizeLogFieldRequestID, s, `{"request-id":"REQUEST-ID"}`},
|
||||||
{log.AuthorizeLogFieldServiceAccountID, sa, `{"service-account-id":"SERVICE-ACCOUNT-ID"}`},
|
{log.AuthorizeLogFieldServiceAccountID, sa, `{"service-account-id":"SERVICE-ACCOUNT-ID"}`},
|
||||||
{log.AuthorizeLogFieldSessionID, s, `{"session-id":"SESSION-ID"}`},
|
{log.AuthorizeLogFieldSessionID, s, `{"session-id":"SESSION-ID"}`},
|
||||||
|
@ -97,7 +104,7 @@ func Test_populateLogEvent(t *testing.T) {
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
log := zerolog.New(&buf)
|
log := zerolog.New(&buf)
|
||||||
evt := log.Log()
|
evt := log.Log()
|
||||||
evt = populateLogEvent(ctx, tc.field, evt, checkRequest, tc.s, u, headers, impersonateDetails)
|
evt = populateLogEvent(ctx, tc.field, evt, checkRequest, tc.s, u, headers, impersonateDetails, res)
|
||||||
evt.Send()
|
evt.Send()
|
||||||
|
|
||||||
assert.Equal(t, tc.expect, strings.TrimSpace(buf.String()))
|
assert.Equal(t, tc.expect, strings.TrimSpace(buf.String()))
|
||||||
|
|
|
@ -23,6 +23,7 @@ const (
|
||||||
AuthorizeLogFieldMethod AuthorizeLogField = "method"
|
AuthorizeLogFieldMethod AuthorizeLogField = "method"
|
||||||
AuthorizeLogFieldPath AuthorizeLogField = "path"
|
AuthorizeLogFieldPath AuthorizeLogField = "path"
|
||||||
AuthorizeLogFieldQuery AuthorizeLogField = "query"
|
AuthorizeLogFieldQuery AuthorizeLogField = "query"
|
||||||
|
AuthorizeLogFieldRemovedGroupsCount AuthorizeLogField = "removed-groups-count"
|
||||||
AuthorizeLogFieldRequestID AuthorizeLogField = "request-id"
|
AuthorizeLogFieldRequestID AuthorizeLogField = "request-id"
|
||||||
AuthorizeLogFieldServiceAccountID AuthorizeLogField = "service-account-id"
|
AuthorizeLogFieldServiceAccountID AuthorizeLogField = "service-account-id"
|
||||||
AuthorizeLogFieldSessionID AuthorizeLogField = "session-id"
|
AuthorizeLogFieldSessionID AuthorizeLogField = "session-id"
|
||||||
|
@ -41,6 +42,7 @@ var DefaultAuthorizeLogFields = []AuthorizeLogField{
|
||||||
AuthorizeLogFieldImpersonateSessionID,
|
AuthorizeLogFieldImpersonateSessionID,
|
||||||
AuthorizeLogFieldImpersonateUserID,
|
AuthorizeLogFieldImpersonateUserID,
|
||||||
AuthorizeLogFieldImpersonateEmail,
|
AuthorizeLogFieldImpersonateEmail,
|
||||||
|
AuthorizeLogFieldRemovedGroupsCount,
|
||||||
AuthorizeLogFieldServiceAccountID,
|
AuthorizeLogFieldServiceAccountID,
|
||||||
AuthorizeLogFieldUser,
|
AuthorizeLogFieldUser,
|
||||||
AuthorizeLogFieldEmail,
|
AuthorizeLogFieldEmail,
|
||||||
|
@ -63,6 +65,7 @@ var authorizeLogFieldLookup = map[AuthorizeLogField]struct{}{
|
||||||
AuthorizeLogFieldMethod: {},
|
AuthorizeLogFieldMethod: {},
|
||||||
AuthorizeLogFieldPath: {},
|
AuthorizeLogFieldPath: {},
|
||||||
AuthorizeLogFieldQuery: {},
|
AuthorizeLogFieldQuery: {},
|
||||||
|
AuthorizeLogFieldRemovedGroupsCount: {},
|
||||||
AuthorizeLogFieldRequestID: {},
|
AuthorizeLogFieldRequestID: {},
|
||||||
AuthorizeLogFieldServiceAccountID: {},
|
AuthorizeLogFieldServiceAccountID: {},
|
||||||
AuthorizeLogFieldSessionID: {},
|
AuthorizeLogFieldSessionID: {},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue