mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-15 07:46:10 +02:00
mcp: send request body to authorize (#5660)
## Summary In order to inspect the MCP requests and use the request payload in the authorization decisions, configure `ext_authz` to send the request payload as well. the body then would be available for inspection as it would contain the json-rpc message like ```json {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{"_meta":{"progressToken":1},"name":"list_tables","arguments":{}}} ``` ## Related issues Ref: 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 - [ ] reference any related issues - [ ] updated unit tests - [ ] add appropriate label (`enhancement`, `bug`, `breaking`, `dependencies`, `ci`) - [ ] ready for review
This commit is contained in:
parent
b0c2e2dede
commit
55dd6ba7d0
3 changed files with 31 additions and 1 deletions
|
@ -21,6 +21,22 @@ func PerFilterConfigExtAuthzContextExtensions(authzContextExtensions map[string]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PerFilterConfigExtAuthzContextExtensionsWithBody returns a per-filter config for ext authz that
|
||||||
|
// sets context extensions and includes the request body.
|
||||||
|
func PerFilterConfigExtAuthzContextExtensionsWithBody(mcpRequestBodyMaxBytes uint32, authzContextExtensions map[string]string) *anypb.Any {
|
||||||
|
return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
|
||||||
|
Override: &envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute_CheckSettings{
|
||||||
|
CheckSettings: &envoy_extensions_filters_http_ext_authz_v3.CheckSettings{
|
||||||
|
ContextExtensions: authzContextExtensions,
|
||||||
|
WithRequestBody: &envoy_extensions_filters_http_ext_authz_v3.BufferSettings{
|
||||||
|
MaxRequestBytes: mcpRequestBodyMaxBytes,
|
||||||
|
AllowPartialMessage: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// PerFilterConfigExtAuthzDisabled returns a per-filter config for ext authz that disables ext-authz.
|
// PerFilterConfigExtAuthzDisabled returns a per-filter config for ext authz that disables ext-authz.
|
||||||
func PerFilterConfigExtAuthzDisabled() *anypb.Any {
|
func PerFilterConfigExtAuthzDisabled() *anypb.Any {
|
||||||
return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
|
return marshalAny(&envoy_extensions_filters_http_ext_authz_v3.ExtAuthzPerRoute{
|
||||||
|
|
|
@ -325,8 +325,13 @@ func (b *Builder) buildRouteForPolicyAndMatch(
|
||||||
PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
|
PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzDisabled(),
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
extAuthzOpts := MakeExtAuthzContextExtensions(false, routeID, routeChecksum)
|
||||||
|
extAuthzCfg := PerFilterConfigExtAuthzContextExtensions(extAuthzOpts)
|
||||||
|
if policy.IsMCPServer() {
|
||||||
|
extAuthzCfg = PerFilterConfigExtAuthzContextExtensionsWithBody(policy.MCP.GetMaxRequestBytes(), extAuthzOpts)
|
||||||
|
}
|
||||||
route.TypedPerFilterConfig = map[string]*anypb.Any{
|
route.TypedPerFilterConfig = map[string]*anypb.Any{
|
||||||
PerFilterConfigExtAuthzName: PerFilterConfigExtAuthzContextExtensions(MakeExtAuthzContextExtensions(false, routeID, routeChecksum)),
|
PerFilterConfigExtAuthzName: extAuthzCfg,
|
||||||
}
|
}
|
||||||
luaMetadata["remove_pomerium_cookie"] = &structpb.Value{
|
luaMetadata["remove_pomerium_cookie"] = &structpb.Value{
|
||||||
Kind: &structpb.Value_StringValue{
|
Kind: &structpb.Value_StringValue{
|
||||||
|
|
|
@ -216,6 +216,15 @@ type MCP struct {
|
||||||
UpstreamOAuth2 *UpstreamOAuth2 `mapstructure:"upstream_oauth2" yaml:"upstream_oauth2,omitempty" json:"upstream_oauth2,omitempty"`
|
UpstreamOAuth2 *UpstreamOAuth2 `mapstructure:"upstream_oauth2" yaml:"upstream_oauth2,omitempty" json:"upstream_oauth2,omitempty"`
|
||||||
// PassUpstreamAccessToken indicates whether to pass the upstream access token in the `Authorization: Bearer` header that is suitable for calling the MCP routes
|
// PassUpstreamAccessToken indicates whether to pass the upstream access token in the `Authorization: Bearer` header that is suitable for calling the MCP routes
|
||||||
PassUpstreamAccessToken bool `mapstructure:"pass_upstream_access_token" yaml:"pass_upstream_access_token,omitempty" json:"pass_upstream_access_token,omitempty"`
|
PassUpstreamAccessToken bool `mapstructure:"pass_upstream_access_token" yaml:"pass_upstream_access_token,omitempty" json:"pass_upstream_access_token,omitempty"`
|
||||||
|
// MaxRequestBytes is the maximum request body size in bytes that can be sent to the MCP server
|
||||||
|
MaxRequestBytes *uint32 `mapstructure:"max_request_bytes" yaml:"max_request_bytes,omitempty" json:"max_request_bytes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *MCP) GetMaxRequestBytes() uint32 {
|
||||||
|
if p == nil || p.MaxRequestBytes == nil {
|
||||||
|
return 4 * 1024
|
||||||
|
}
|
||||||
|
return *p.MaxRequestBytes
|
||||||
}
|
}
|
||||||
|
|
||||||
// HasUpstreamOAuth2 checks if the route is for the MCP Server and if it has an upstream OAuth2 configuration
|
// HasUpstreamOAuth2 checks if the route is for the MCP Server and if it has an upstream OAuth2 configuration
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue