mcp: add mcp method and tool logging to authorize (#5668)

## Summary

Adds support for extending authorization log with Model Context Protocol
details.

i.e. 
```json
{
  "level": "info",
  "server-name": "all",
  "service": "authorize",
  "mcp-method": "tools/call",
  "mcp-tool": "describe_table",
  "mcp-tool-parameters": { "table_name": "Categories" },
  "allow": true,
  "allow-why-true": ["email-ok", "mcp-tool-ok"],
  "deny": false,
  "deny-why-false": [],
  "time": "2025-06-24T17:40:41-04:00",
  "message": "authorize check"
}
```

## Related issues

Fixes
https://linear.app/pomerium/issue/ENG-2393/mcp-authorize-each-incoming-request-to-an-mcp-route

## User Explanation

<!-- How would you explain this change to the user? If this
change doesn't create any user-facing changes, you can leave
this blank. If filled out, add the `docs` label -->

## Checklist

- [x] reference any related issues
- [x] updated unit tests
- [x] add appropriate label (`enhancement`, `bug`, `breaking`,
`dependencies`, `ci`)
- [x] ready for review
This commit is contained in:
Denis Mishin 2025-06-24 17:58:51 -07:00 committed by GitHub
parent eacf19cd64
commit 9363457849
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 271 additions and 82 deletions

View file

@ -9,6 +9,8 @@ import (
"strings"
envoy_service_auth_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
"go.opentelemetry.io/otel/attribute"
oteltrace "go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@ -37,18 +39,24 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
ctx = a.withQuerierForCheckRequest(ctx)
state := a.state.Load()
mcpEnabled := a.currentConfig.Load().Options.IsRuntimeFlagSet(config.RuntimeFlagMCP)
// convert the incoming envoy-style http request into a go-style http request
hreq := getHTTPRequestFromCheckRequest(in)
requestID := requestid.FromHTTPHeader(hreq.Header)
ctx = requestid.WithValue(ctx, requestID)
req, err := a.getEvaluatorRequestFromCheckRequest(ctx, in)
req, err := a.getEvaluatorRequestFromCheckRequest(ctx, in, mcpEnabled)
if err != nil {
log.Ctx(ctx).Error().Err(err).Str("request-id", requestID).Msg("error building evaluator request")
return nil, err
}
// Add MCP information to trace if available
if mcpEnabled {
updateSpanWithMCPInfo(span, req.MCP)
}
// load the session
s, err := a.loadSession(ctx, hreq, req)
if errors.Is(err, sessions.ErrInvalidSession) {
@ -68,7 +76,7 @@ func (a *Authorize) Check(ctx context.Context, in *envoy_service_auth_v3.CheckRe
// For MCP routes that only require authentication (not full authorization),
// if we have a valid session, allow the request without running policy evaluation
// as policy for MCP may contain check for i.e. tool calls that are not relevant at this stage.
if a.currentConfig.Load().Options.IsRuntimeFlagSet(config.RuntimeFlagMCP) {
if mcpEnabled {
if req.Policy.IsMCPServer() && strings.HasPrefix(hreq.URL.Path, mcp.DefaultPrefix) {
if s != nil {
return a.requireLoginResponse(ctx, in, req)
@ -196,6 +204,7 @@ func (a *Authorize) getMCPSession(
func (a *Authorize) getEvaluatorRequestFromCheckRequest(
ctx context.Context,
in *envoy_service_auth_v3.CheckRequest,
mcpEnabled bool,
) (*evaluator.Request, error) {
attrs := in.GetAttributes()
req := &evaluator.Request{
@ -206,7 +215,7 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
}
req.Policy = a.getMatchingPolicy(req.EnvoyRouteID)
if req.Policy.IsMCPServer() {
if mcpEnabled && req.Policy.IsMCPServer() {
var ok bool
req.MCP, ok = evaluator.RequestMCPFromCheckRequest(in)
if !ok {
@ -214,13 +223,6 @@ func (a *Authorize) getEvaluatorRequestFromCheckRequest(
Str("request-id", requestid.FromContext(ctx)).
Str("route_id", req.EnvoyRouteID).
Msg("failed to parse MCP request from check request")
} else {
log.Ctx(ctx).Debug().
Str("request-id", requestid.FromContext(ctx)).
Str("route_id", req.EnvoyRouteID).
Str("mcp_tool", req.MCP.Tool).
Str("mcp_method", req.MCP.Method).
Msg("authorize request from check request")
}
}
@ -280,3 +282,13 @@ func getCheckRequestHeaders(req *envoy_service_auth_v3.CheckRequest) map[string]
}
return hdrs
}
func updateSpanWithMCPInfo(span oteltrace.Span, mcp evaluator.RequestMCP) {
if mcp.Method == "" {
return
}
span.SetAttributes(attribute.String("mcp.method", mcp.Method))
if tc := mcp.ToolCall; tc != nil {
span.SetAttributes(attribute.String("mcp.tool", tc.Name))
}
}