mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-29 18:36:30 +02:00
mcp: add to route config, 401 when unauthenticated
This commit is contained in:
parent
a10b505386
commit
be8149c8e6
6 changed files with 889 additions and 775 deletions
|
@ -98,6 +98,9 @@ func (a *Authorize) handleResultDenied(
|
||||||
case invalidClientCertReason(reasons):
|
case invalidClientCertReason(reasons):
|
||||||
denyStatusCode = httputil.StatusInvalidClientCertificate
|
denyStatusCode = httputil.StatusInvalidClientCertificate
|
||||||
denyStatusText = httputil.DetailsText(httputil.StatusInvalidClientCertificate)
|
denyStatusText = httputil.DetailsText(httputil.StatusInvalidClientCertificate)
|
||||||
|
case request.Policy.IsMCP():
|
||||||
|
denyStatusCode = http.StatusUnauthorized
|
||||||
|
denyStatusText = httputil.DetailsText(http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
return a.deniedResponse(ctx, in, denyStatusCode, denyStatusText, nil)
|
return a.deniedResponse(ctx, in, denyStatusCode, denyStatusText, nil)
|
||||||
|
@ -216,7 +219,7 @@ func (a *Authorize) requireLoginResponse(
|
||||||
options := a.currentConfig.Load().Options
|
options := a.currentConfig.Load().Options
|
||||||
state := a.state.Load()
|
state := a.state.Load()
|
||||||
|
|
||||||
if !a.shouldRedirect(in) {
|
if !a.shouldRedirect(in, request) {
|
||||||
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -268,7 +271,7 @@ func (a *Authorize) requireWebAuthnResponse(
|
||||||
return a.okResponse(result.Headers), nil
|
return a.okResponse(result.Headers), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !a.shouldRedirect(in) {
|
if !a.shouldRedirect(in, request) {
|
||||||
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
return a.deniedResponse(ctx, in, http.StatusUnauthorized, "Unauthenticated", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -353,7 +356,11 @@ func (a *Authorize) userInfoEndpointURL(in *envoy_service_auth_v3.CheckRequest)
|
||||||
return urlutil.NewSignedURL(a.state.Load().sharedKey, debugEndpoint).Sign(), nil
|
return urlutil.NewSignedURL(a.state.Load().sharedKey, debugEndpoint).Sign(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Authorize) shouldRedirect(in *envoy_service_auth_v3.CheckRequest) bool {
|
func (a *Authorize) shouldRedirect(in *envoy_service_auth_v3.CheckRequest, request *evaluator.Request) bool {
|
||||||
|
if request.Policy.IsMCP() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
requestHeaders := in.GetAttributes().GetRequest().GetHttp().GetHeaders()
|
requestHeaders := in.GetAttributes().GetRequest().GetHttp().GetHeaders()
|
||||||
if requestHeaders == nil {
|
if requestHeaders == nil {
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -113,6 +113,18 @@ func TestAuthorize_handleResult(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
assert.Equal(t, 495, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
assert.Equal(t, 495, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||||
})
|
})
|
||||||
|
t.Run("mcp-route-unauthenticated", func(t *testing.T) {
|
||||||
|
res, err := a.handleResult(context.Background(),
|
||||||
|
&envoy_service_auth_v3.CheckRequest{},
|
||||||
|
&evaluator.Request{
|
||||||
|
Policy: &config.Policy{MCP: &config.MCP{}},
|
||||||
|
},
|
||||||
|
&evaluator.Result{
|
||||||
|
Allow: evaluator.NewRuleResult(false, criteria.ReasonUserUnauthenticated),
|
||||||
|
})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, 401, int(res.GetDeniedResponse().GetStatus().GetCode()))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthorize_okResponse(t *testing.T) {
|
func TestAuthorize_okResponse(t *testing.T) {
|
||||||
|
|
|
@ -665,3 +665,17 @@ func (f JWTIssuerFormat) Valid() bool {
|
||||||
_, ok := knownJWTIssuerFormats[f]
|
_, ok := knownJWTIssuerFormats[f]
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MCPFromPB(src *configpb.MCP) *MCP {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &MCP{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func MCPToPB(src *MCP) *configpb.MCP {
|
||||||
|
if src == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return &configpb.MCP{}
|
||||||
|
}
|
||||||
|
|
|
@ -202,8 +202,14 @@ type Policy struct {
|
||||||
Policy *PPLPolicy `mapstructure:"policy" yaml:"policy,omitempty" json:"policy,omitempty"`
|
Policy *PPLPolicy `mapstructure:"policy" yaml:"policy,omitempty" json:"policy,omitempty"`
|
||||||
|
|
||||||
DependsOn []string `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
|
DependsOn []string `mapstructure:"depends_on" yaml:"depends_on,omitempty" json:"depends_on,omitempty"`
|
||||||
|
|
||||||
|
// MCP is an experimental support for Model Context Protocol upstreams
|
||||||
|
MCP *MCP `mapstructure:"mcp" yaml:"mcp,omitempty" json:"mcp,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MCP is an experimental support for Model Context Protocol upstreams configuration
|
||||||
|
type MCP struct{}
|
||||||
|
|
||||||
// RewriteHeader is a policy configuration option to rewrite an HTTP header.
|
// RewriteHeader is a policy configuration option to rewrite an HTTP header.
|
||||||
type RewriteHeader struct {
|
type RewriteHeader struct {
|
||||||
Header string `mapstructure:"header" yaml:"header" json:"header"`
|
Header string `mapstructure:"header" yaml:"header" json:"header"`
|
||||||
|
@ -316,6 +322,7 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
||||||
KubernetesServiceAccountToken: pb.GetKubernetesServiceAccountToken(),
|
KubernetesServiceAccountToken: pb.GetKubernetesServiceAccountToken(),
|
||||||
KubernetesServiceAccountTokenFile: pb.GetKubernetesServiceAccountTokenFile(),
|
KubernetesServiceAccountTokenFile: pb.GetKubernetesServiceAccountTokenFile(),
|
||||||
LogoURL: pb.GetLogoUrl(),
|
LogoURL: pb.GetLogoUrl(),
|
||||||
|
MCP: MCPFromPB(pb.GetMcp()),
|
||||||
Name: pb.GetName(),
|
Name: pb.GetName(),
|
||||||
PassIdentityHeaders: pb.PassIdentityHeaders,
|
PassIdentityHeaders: pb.PassIdentityHeaders,
|
||||||
Path: pb.GetPath(),
|
Path: pb.GetPath(),
|
||||||
|
@ -470,6 +477,7 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
||||||
KubernetesServiceAccountToken: p.KubernetesServiceAccountToken,
|
KubernetesServiceAccountToken: p.KubernetesServiceAccountToken,
|
||||||
KubernetesServiceAccountTokenFile: p.KubernetesServiceAccountTokenFile,
|
KubernetesServiceAccountTokenFile: p.KubernetesServiceAccountTokenFile,
|
||||||
LogoUrl: p.LogoURL,
|
LogoUrl: p.LogoURL,
|
||||||
|
Mcp: MCPToPB(p.MCP),
|
||||||
Name: p.Name,
|
Name: p.Name,
|
||||||
PassIdentityHeaders: p.PassIdentityHeaders,
|
PassIdentityHeaders: p.PassIdentityHeaders,
|
||||||
Path: p.Path,
|
Path: p.Path,
|
||||||
|
@ -824,6 +832,11 @@ func (p *Policy) IsForKubernetes() bool {
|
||||||
return p.KubernetesServiceAccountTokenFile != "" || p.KubernetesServiceAccountToken != ""
|
return p.KubernetesServiceAccountTokenFile != "" || p.KubernetesServiceAccountToken != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsMCP returns true if the route is for the Model Context Protocol upstream server.
|
||||||
|
func (p *Policy) IsMCP() bool {
|
||||||
|
return p != nil && p.MCP != nil
|
||||||
|
}
|
||||||
|
|
||||||
// IsTCP returns true if the route is for TCP.
|
// IsTCP returns true if the route is for TCP.
|
||||||
func (p *Policy) IsTCP() bool {
|
func (p *Policy) IsTCP() bool {
|
||||||
return strings.HasPrefix(p.From, "tcp")
|
return strings.HasPrefix(p.From, "tcp")
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -52,7 +52,7 @@ enum BearerTokenFormat {
|
||||||
BEARER_TOKEN_FORMAT_IDP_IDENTITY_TOKEN = 3;
|
BEARER_TOKEN_FORMAT_IDP_IDENTITY_TOKEN = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next ID: 72.
|
// Next ID: 73.
|
||||||
message Route {
|
message Route {
|
||||||
message StringList { repeated string values = 1; }
|
message StringList { repeated string values = 1; }
|
||||||
|
|
||||||
|
@ -143,8 +143,12 @@ message Route {
|
||||||
optional string idp_client_secret = 56;
|
optional string idp_client_secret = 56;
|
||||||
optional StringList idp_access_token_allowed_audiences = 69;
|
optional StringList idp_access_token_allowed_audiences = 69;
|
||||||
bool show_error_details = 59;
|
bool show_error_details = 59;
|
||||||
|
|
||||||
|
optional MCP mcp = 72;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message MCP {}
|
||||||
|
|
||||||
message PPLPolicy { bytes raw = 1; }
|
message PPLPolicy { bytes raw = 1; }
|
||||||
|
|
||||||
message Policy {
|
message Policy {
|
||||||
|
|
Loading…
Add table
Reference in a new issue