mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-06 19:38:09 +02:00
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:
parent
eacf19cd64
commit
9363457849
14 changed files with 271 additions and 82 deletions
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue