pomerium/authorize/evaluator/headers_evaluator.go
Kenneth Jenkins 5e45fa4b0d
authorize: log JWT groups filtering (#5432) (#5454)
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.
2025-01-28 14:43:27 -08:00

100 lines
3.5 KiB
Go

package evaluator
import (
"context"
"fmt"
"net/http"
"time"
envoy_config_cluster_v3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
"github.com/open-policy-agent/opa/rego"
"github.com/pomerium/pomerium/authorize/internal/store"
"github.com/pomerium/pomerium/config"
"github.com/pomerium/pomerium/internal/log"
"github.com/pomerium/pomerium/internal/telemetry/trace"
)
// HeadersRequest is the input to the headers.rego script.
type HeadersRequest struct {
EnableGoogleCloudServerlessAuthentication bool `json:"enable_google_cloud_serverless_authentication"`
EnableRoutingKey bool `json:"enable_routing_key"`
Issuer string `json:"issuer"`
Audience string `json:"audience"`
KubernetesServiceAccountToken string `json:"kubernetes_service_account_token"`
ToAudience string `json:"to_audience"`
Session RequestSession `json:"session"`
ClientCertificate ClientCertificateInfo `json:"client_certificate"`
SetRequestHeaders map[string]string `json:"set_request_headers"`
JWTGroupsFilter config.JWTGroupsFilter `json:"-"`
}
// NewHeadersRequestFromPolicy creates a new HeadersRequest from a policy.
func NewHeadersRequestFromPolicy(policy *config.Policy, http RequestHTTP) (*HeadersRequest, error) {
input := new(HeadersRequest)
input.Audience = http.Hostname
var issuerFormat string
if policy != nil {
issuerFormat = policy.JWTIssuerFormat
}
switch issuerFormat {
case "", "hostOnly":
input.Issuer = http.Hostname
case "uri":
input.Issuer = fmt.Sprintf("https://%s/", http.Hostname)
default:
return nil, fmt.Errorf("invalid issuer format: %q", policy.JWTIssuerFormat)
}
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
var err error
input.KubernetesServiceAccountToken, err = policy.GetKubernetesServiceAccountToken()
if err != nil {
return nil, err
}
for _, wu := range policy.To {
input.ToAudience = "https://" + wu.URL.Hostname()
}
input.ClientCertificate = http.ClientCertificate
input.SetRequestHeaders = policy.SetRequestHeaders
input.JWTGroupsFilter = policy.JWTGroupsFilter
}
return input, nil
}
// HeadersResponse is the output from the headers.rego script.
type HeadersResponse struct {
Headers http.Header
AdditionalLogFields map[log.AuthorizeLogField]any
}
// A HeadersEvaluator evaluates the headers.rego script.
type HeadersEvaluator struct {
store *store.Store
}
// NewHeadersEvaluator creates a new HeadersEvaluator.
func NewHeadersEvaluator(store *store.Store) *HeadersEvaluator {
return &HeadersEvaluator{
store: store,
}
}
// Evaluate evaluates the headers.rego script.
func (e *HeadersEvaluator) Evaluate(ctx context.Context, req *HeadersRequest, options ...rego.EvalOption) (*HeadersResponse, error) {
ctx, span := trace.StartSpan(ctx, "authorize.HeadersEvaluator.Evaluate")
defer span.End()
ectx := new(rego.EvalContext)
for _, option := range options {
option(ectx)
}
now := ectx.Time()
if now.IsZero() {
now = time.Now()
}
return newHeadersEvaluatorEvaluation(e, req, now).execute(ctx)
}