pomerium/authorize/log.go
Joe Kralicky 396c35b6b4
New tracing system (#5388)
* update tracing config definitions

* new tracing system

* performance improvements

* only configure tracing in envoy if it is enabled in pomerium

* [tracing] refactor to use custom extension for trace id editing (#5420)

refactor to use custom extension for trace id editing

* set default tracing sample rate to 1.0

* fix proxy service http middleware

* improve some existing auth related traces

* test fixes

* bump envoyproxy/go-control-plane

* code cleanup

* test fixes

* Fix missing spans for well-known endpoints

* import extension apis from pomerium/envoy-custom
2025-01-21 13:26:32 -05:00

210 lines
6 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)
}
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,
) *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:
return evt
}
}