mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 10:26:29 +02:00
Add a new Authorize Log Fields option for logging the number of groups removed during JWT groups filtering. This will be enabled by default. Additionally, when the log level is Debug (or more verbose), store and log the IDs of any groups removed during JWT groups filtering.
216 lines
6.1 KiB
Go
216 lines
6.1 KiB
Go
package authorize
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
|
|
"github.com/go-jose/go-jose/v3/jwt"
|
|
"github.com/rs/zerolog"
|
|
"go.opentelemetry.io/otel/attribute"
|
|
|
|
"github.com/pomerium/pomerium/authorize/evaluator"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/pkg/grpc/databroker"
|
|
"github.com/pomerium/pomerium/pkg/grpc/session"
|
|
"github.com/pomerium/pomerium/pkg/grpc/user"
|
|
"github.com/pomerium/pomerium/pkg/grpcutil"
|
|
"github.com/pomerium/pomerium/pkg/storage"
|
|
"github.com/pomerium/pomerium/pkg/telemetry/requestid"
|
|
)
|
|
|
|
func (a *Authorize) logAuthorizeCheck(
|
|
ctx context.Context,
|
|
in *envoy_service_auth_v3.CheckRequest,
|
|
res *evaluator.Result, s sessionOrServiceAccount, u *user.User,
|
|
) {
|
|
ctx, span := a.tracer.Start(ctx, "authorize.grpc.LogAuthorizeCheck")
|
|
defer span.End()
|
|
|
|
hdrs := getCheckRequestHeaders(in)
|
|
impersonateDetails := a.getImpersonateDetails(ctx, s)
|
|
|
|
evt := log.Ctx(ctx).Info().Str("service", "authorize")
|
|
fields := a.currentOptions.Load().GetAuthorizeLogFields()
|
|
for _, field := range fields {
|
|
evt = populateLogEvent(ctx, field, evt, in, s, u, hdrs, impersonateDetails, res)
|
|
}
|
|
evt = log.HTTPHeaders(evt, fields, hdrs)
|
|
|
|
// result
|
|
if res != nil {
|
|
span.SetAttributes(attribute.Bool("result.allow", res.Allow.Value))
|
|
evt = evt.Bool("allow", res.Allow.Value)
|
|
allowReasons := res.Allow.Reasons.Strings()
|
|
if res.Allow.Value {
|
|
span.SetAttributes(attribute.StringSlice("result.allow-why-true", allowReasons))
|
|
evt = evt.Strs("allow-why-true", allowReasons)
|
|
} else {
|
|
span.SetAttributes(attribute.StringSlice("result.allow-why-false", allowReasons))
|
|
evt = evt.Strs("allow-why-false", allowReasons)
|
|
}
|
|
evt = evt.Bool("deny", res.Deny.Value)
|
|
denyReasons := res.Deny.Reasons.Strings()
|
|
if res.Deny.Value {
|
|
span.SetAttributes(attribute.StringSlice("result.deny-why-true", denyReasons))
|
|
evt = evt.Strs("deny-why-true", denyReasons)
|
|
} else {
|
|
span.SetAttributes(attribute.StringSlice("result.deny-why-false", denyReasons))
|
|
evt = evt.Strs("deny-why-false", denyReasons)
|
|
}
|
|
}
|
|
|
|
evt.Msg("authorize check")
|
|
}
|
|
|
|
type impersonateDetails struct {
|
|
email string
|
|
sessionID string
|
|
userID string
|
|
}
|
|
|
|
func (a *Authorize) getImpersonateDetails(
|
|
ctx context.Context,
|
|
s sessionOrServiceAccount,
|
|
) *impersonateDetails {
|
|
var sessionID string
|
|
if s, ok := s.(*session.Session); ok {
|
|
sessionID = s.GetImpersonateSessionId()
|
|
}
|
|
if sessionID == "" {
|
|
return nil
|
|
}
|
|
|
|
querier := storage.GetQuerier(ctx)
|
|
|
|
req := &databroker.QueryRequest{
|
|
Type: grpcutil.GetTypeURL(new(session.Session)),
|
|
Limit: 1,
|
|
}
|
|
req.SetFilterByID(sessionID)
|
|
res, err := querier.Query(ctx, req)
|
|
if err != nil || len(res.GetRecords()) == 0 {
|
|
return nil
|
|
}
|
|
|
|
impersonatedSessionMsg, err := res.GetRecords()[0].GetData().UnmarshalNew()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
impersonatedSession, ok := impersonatedSessionMsg.(*session.Session)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
userID := impersonatedSession.GetUserId()
|
|
|
|
req = &databroker.QueryRequest{
|
|
Type: grpcutil.GetTypeURL(new(user.User)),
|
|
Limit: 1,
|
|
}
|
|
req.SetFilterByID(userID)
|
|
res, err = querier.Query(ctx, req)
|
|
if err != nil || len(res.GetRecords()) == 0 {
|
|
return nil
|
|
}
|
|
|
|
impersonatedUserMsg, err := res.GetRecords()[0].GetData().UnmarshalNew()
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
impersonatedUser, ok := impersonatedUserMsg.(*user.User)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
email := impersonatedUser.GetEmail()
|
|
|
|
return &impersonateDetails{
|
|
sessionID: sessionID,
|
|
userID: userID,
|
|
email: email,
|
|
}
|
|
}
|
|
|
|
func populateLogEvent(
|
|
ctx context.Context,
|
|
field log.AuthorizeLogField,
|
|
evt *zerolog.Event,
|
|
in *envoy_service_auth_v3.CheckRequest,
|
|
s sessionOrServiceAccount,
|
|
u *user.User,
|
|
hdrs map[string]string,
|
|
impersonateDetails *impersonateDetails,
|
|
res *evaluator.Result,
|
|
) *zerolog.Event {
|
|
path, query, _ := strings.Cut(in.GetAttributes().GetRequest().GetHttp().GetPath(), "?")
|
|
|
|
switch field {
|
|
case log.AuthorizeLogFieldCheckRequestID:
|
|
return evt.Str(string(field), hdrs["X-Request-Id"])
|
|
case log.AuthorizeLogFieldEmail:
|
|
return evt.Str(string(field), u.GetEmail())
|
|
case log.AuthorizeLogFieldHost:
|
|
return evt.Str(string(field), in.GetAttributes().GetRequest().GetHttp().GetHost())
|
|
case log.AuthorizeLogFieldIDToken:
|
|
if s, ok := s.(*session.Session); ok {
|
|
evt = evt.Str(string(field), s.GetIdToken().GetRaw())
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldIDTokenClaims:
|
|
if s, ok := s.(*session.Session); ok {
|
|
if t, err := jwt.ParseSigned(s.GetIdToken().GetRaw()); err == nil {
|
|
var m map[string]any
|
|
_ = t.UnsafeClaimsWithoutVerification(&m)
|
|
evt = evt.Interface(string(field), m)
|
|
}
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldImpersonateEmail:
|
|
if impersonateDetails != nil {
|
|
evt = evt.Str(string(field), impersonateDetails.email)
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldImpersonateSessionID:
|
|
if impersonateDetails != nil {
|
|
evt = evt.Str(string(field), impersonateDetails.sessionID)
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldImpersonateUserID:
|
|
if impersonateDetails != nil {
|
|
evt = evt.Str(string(field), impersonateDetails.userID)
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldIP:
|
|
return evt.Str(string(field), in.GetAttributes().GetSource().GetAddress().GetSocketAddress().GetAddress())
|
|
case log.AuthorizeLogFieldMethod:
|
|
return evt.Str(string(field), in.GetAttributes().GetRequest().GetHttp().GetMethod())
|
|
case log.AuthorizeLogFieldPath:
|
|
return evt.Str(string(field), path)
|
|
case log.AuthorizeLogFieldQuery:
|
|
return evt.Str(string(field), query)
|
|
case log.AuthorizeLogFieldRequestID:
|
|
return evt.Str(string(field), requestid.FromContext(ctx))
|
|
case log.AuthorizeLogFieldServiceAccountID:
|
|
if sa, ok := s.(*user.ServiceAccount); ok {
|
|
evt = evt.Str(string(field), sa.GetId())
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldSessionID:
|
|
if s, ok := s.(*session.Session); ok {
|
|
evt = evt.Str(string(field), s.GetId())
|
|
}
|
|
return evt
|
|
case log.AuthorizeLogFieldUser:
|
|
var userID string
|
|
if s != nil {
|
|
userID = s.GetUserId()
|
|
}
|
|
return evt.Str(string(field), userID)
|
|
default:
|
|
if res != nil {
|
|
if v, ok := res.AdditionalLogFields[field]; ok {
|
|
evt = evt.Interface(string(field), v)
|
|
}
|
|
}
|
|
return evt
|
|
}
|
|
}
|