authorize: audit logging (#2050)

* authorize: add databroker server and record version to result, force sync via polling

* authorize: audit logging
This commit is contained in:
Caleb Doxsey 2021-04-05 09:58:55 -06:00 committed by GitHub
parent 00e56212ec
commit f4c4fe314a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 1395 additions and 1390 deletions

View file

@ -8,23 +8,19 @@ import (
"net/url"
"strings"
"github.com/rs/zerolog"
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/httputil"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/sessions"
"github.com/pomerium/pomerium/internal/telemetry/requestid"
"github.com/pomerium/pomerium/internal/telemetry/trace"
"github.com/pomerium/pomerium/internal/urlutil"
"github.com/pomerium/pomerium/pkg/grpc/user"
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
)
// Check implements the envoy auth server gRPC endpoint.
func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRequest) (*envoy_service_auth_v3.CheckResponse, error) {
func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRequest) (out *envoy_service_auth_v3.CheckResponse, err error) {
ctx, span := trace.StartSpan(ctx, "authorize.grpc.Check")
defer span.End()
@ -65,7 +61,9 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
log.Error().Err(err).Msg("error during OPA evaluation")
return nil, err
}
logAuthorizeCheck(ctx, in, reply, u)
defer func() {
a.logAuthorizeCheck(ctx, in, out, reply, u)
}()
switch {
case reply.Status == http.StatusOK:
@ -226,45 +224,3 @@ func getPeerCertificate(in *envoy_service_auth_v3.CheckRequest) string {
cert, _ := url.QueryUnescape(in.GetAttributes().GetSource().GetCertificate())
return cert
}
func logAuthorizeCheck(
ctx context.Context,
in *envoy_service_auth_v3.CheckRequest,
reply *evaluator.Result,
u *user.User,
) {
hdrs := getCheckRequestHeaders(in)
hattrs := in.GetAttributes().GetRequest().GetHttp()
evt := log.Info().Str("service", "authorize")
// request
evt = evt.Str("request-id", requestid.FromContext(ctx))
evt = evt.Str("check-request-id", hdrs["X-Request-Id"])
evt = evt.Str("method", hattrs.GetMethod())
evt = evt.Str("path", stripQueryString(hattrs.GetPath()))
evt = evt.Str("host", hattrs.GetHost())
evt = evt.Str("query", hattrs.GetQuery())
// reply
if reply != nil {
evt = evt.Bool("allow", reply.Status == http.StatusOK)
evt = evt.Int("status", reply.Status)
evt = evt.Str("message", reply.Message)
evt = evt.Str("user", u.GetId())
evt = evt.Str("email", u.GetEmail())
evt = evt.Uint64("databroker_server_version", reply.DataBrokerServerVersion)
evt = evt.Uint64("databroker_record_version", reply.DataBrokerRecordVersion)
}
// potentially sensitive, only log if debug mode
if zerolog.GlobalLevel() <= zerolog.DebugLevel {
evt = evt.Interface("headers", hdrs)
}
evt.Msg("authorize check")
}
func stripQueryString(str string) string {
if idx := strings.Index(str, "?"); idx != -1 {
str = str[:idx]
}
return str
}

77
authorize/log.go Normal file
View file

@ -0,0 +1,77 @@
package authorize
import (
"context"
"net/http"
"strings"
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
"github.com/rs/zerolog"
"github.com/pomerium/pomerium/authorize/evaluator"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/requestid"
"github.com/pomerium/pomerium/pkg/grpc/audit"
"github.com/pomerium/pomerium/pkg/grpc/user"
)
func (a *Authorize) logAuthorizeCheck(
ctx context.Context,
in *envoy_service_auth_v3.CheckRequest, out *envoy_service_auth_v3.CheckResponse,
reply *evaluator.Result, u *user.User,
) {
hdrs := getCheckRequestHeaders(in)
hattrs := in.GetAttributes().GetRequest().GetHttp()
evt := log.Info().Str("service", "authorize")
// request
evt = evt.Str("request-id", requestid.FromContext(ctx))
evt = evt.Str("check-request-id", hdrs["X-Request-Id"])
evt = evt.Str("method", hattrs.GetMethod())
evt = evt.Str("path", stripQueryString(hattrs.GetPath()))
evt = evt.Str("host", hattrs.GetHost())
evt = evt.Str("query", hattrs.GetQuery())
// reply
if reply != nil {
evt = evt.Bool("allow", reply.Status == http.StatusOK)
evt = evt.Int("status", reply.Status)
evt = evt.Str("message", reply.Message)
evt = evt.Str("user", u.GetId())
evt = evt.Str("email", u.GetEmail())
evt = evt.Uint64("databroker_server_version", reply.DataBrokerServerVersion)
evt = evt.Uint64("databroker_record_version", reply.DataBrokerRecordVersion)
}
// potentially sensitive, only log if debug mode
if zerolog.GlobalLevel() <= zerolog.DebugLevel {
evt = evt.Interface("headers", hdrs)
}
evt.Msg("authorize check")
if enc := a.state.Load().auditEncryptor; enc != nil {
record := &audit.Record{
Request: in,
Response: out,
}
if reply != nil {
record.DatabrokerServerVersion = reply.DataBrokerServerVersion
record.DatabrokerRecordVersion = reply.DataBrokerRecordVersion
}
sealed, err := enc.Encrypt(record)
if err != nil {
log.Warn().Err(err).Msg("authorize: error encrypting audit record")
return
}
log.Info().
Str("request-id", requestid.FromContext(ctx)).
EmbedObject(sealed).
Msg("audit log")
}
}
func stripQueryString(str string) string {
if idx := strings.Index(str, "?"); idx != -1 {
str = str[:idx]
}
return str
}

View file

@ -11,12 +11,14 @@ import (
"github.com/pomerium/pomerium/internal/encoding/jws"
"github.com/pomerium/pomerium/pkg/grpc"
"github.com/pomerium/pomerium/pkg/grpc/databroker"
"github.com/pomerium/pomerium/pkg/protoutil"
)
type authorizeState struct {
evaluator *evaluator.Evaluator
encoder encoding.MarshalUnmarshaler
dataBrokerClient databroker.DataBrokerServiceClient
auditEncryptor *protoutil.Encryptor
}
func newAuthorizeStateFromConfig(cfg *config.Config, store *evaluator.Store) (*authorizeState, error) {
@ -61,6 +63,14 @@ func newAuthorizeStateFromConfig(cfg *config.Config, store *evaluator.Store) (*a
}
state.dataBrokerClient = databroker.NewDataBrokerServiceClient(cc)
auditKey, err := cfg.Options.GetAuditKey()
if err != nil {
return nil, fmt.Errorf("authorize: invalid audit key: %w", err)
}
if auditKey != nil {
state.auditEncryptor = protoutil.NewEncryptor(auditKey)
}
return state, nil
}