mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-06 04:42:56 +02:00
core/config: implement direct response (#4960)
* implement direct response * proto * fix tests * update
This commit is contained in:
parent
2db2bd09a1
commit
513d8bf615
7 changed files with 967 additions and 843 deletions
|
@ -14,13 +14,13 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errKeysMustBeStrings = errors.New("cannot convert nested map: all keys must be strings")
|
errKeysMustBeStrings = errors.New("cannot convert nested map: all keys must be strings")
|
||||||
errZeroWeight = errors.New("zero load balancing weight not permitted")
|
errZeroWeight = errors.New("zero load balancing weight not permitted")
|
||||||
errEndpointWeightsSpec = errors.New("either no weights should be provided, or all endpoints must have non-zero weight specified")
|
errEndpointWeightsSpec = errors.New("either no weights should be provided, or all endpoints must have non-zero weight specified")
|
||||||
errHostnameMustBeSpecified = errors.New("endpoint hostname must be specified")
|
errHostnameMustBeSpecified = errors.New("endpoint hostname must be specified")
|
||||||
errSchemeMustBeSpecified = errors.New("url scheme must be provided")
|
errSchemeMustBeSpecified = errors.New("url scheme must be provided")
|
||||||
errEmptyUrls = errors.New("url list is empty")
|
errEmptyUrls = errors.New("url list is empty")
|
||||||
errEitherToOrRedirectRequired = errors.New("policy should have either `to` or `redirect` defined")
|
errEitherToOrRedirectOrResponseRequired = errors.New("policy should have either `to` or `redirect` or `response` defined")
|
||||||
)
|
)
|
||||||
|
|
||||||
var protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
|
var protoPartial = protojson.UnmarshalOptions{AllowPartial: true, DiscardUnknown: true}
|
||||||
|
|
|
@ -77,7 +77,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
||||||
],
|
],
|
||||||
"route": {
|
"route": {
|
||||||
"autoHostRewrite": true,
|
"autoHostRewrite": true,
|
||||||
"cluster": "route-5feb9fe8bd89aa97",
|
"cluster": "route-5d678ee30d16332b",
|
||||||
"hashPolicy": [
|
"hashPolicy": [
|
||||||
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
||||||
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
||||||
|
@ -94,7 +94,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "6911793875091303063"
|
"route_id": "6730505273956774699"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
||||||
],
|
],
|
||||||
"route": {
|
"route": {
|
||||||
"autoHostRewrite": true,
|
"autoHostRewrite": true,
|
||||||
"cluster": "route-5feb9fe8bd89aa97",
|
"cluster": "route-5d678ee30d16332b",
|
||||||
"hashPolicy": [
|
"hashPolicy": [
|
||||||
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
{ "header": { "headerName": "x-pomerium-routing-key" }, "terminal": true },
|
||||||
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
{ "connectionProperties": { "sourceIp": true }, "terminal": true }
|
||||||
|
@ -147,7 +147,7 @@ func TestBuilder_buildMainRouteConfiguration(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "6911793875091303063"
|
"route_id": "6730505273956774699"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -282,6 +282,9 @@ func (b *Builder) buildRouteForPolicyAndMatch(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
route.Action = &envoy_config_route_v3.Route_Redirect{Redirect: action}
|
route.Action = &envoy_config_route_v3.Route_Redirect{Redirect: action}
|
||||||
|
} else if policy.Response != nil {
|
||||||
|
action := b.buildPolicyRouteDirectResponseAction(policy.Response)
|
||||||
|
route.Action = &envoy_config_route_v3.Route_DirectResponse{DirectResponse: action}
|
||||||
} else {
|
} else {
|
||||||
action, err := b.buildPolicyRouteRouteAction(cfg.Options, policy)
|
action, err := b.buildPolicyRouteRouteAction(cfg.Options, policy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -343,6 +346,17 @@ func (b *Builder) buildRouteForPolicyAndMatch(
|
||||||
return route, nil
|
return route, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *Builder) buildPolicyRouteDirectResponseAction(r *config.DirectResponse) *envoy_config_route_v3.DirectResponseAction {
|
||||||
|
return &envoy_config_route_v3.DirectResponseAction{
|
||||||
|
Status: uint32(r.Status),
|
||||||
|
Body: &envoy_config_core_v3.DataSource{
|
||||||
|
Specifier: &envoy_config_core_v3.DataSource_InlineString{
|
||||||
|
InlineString: r.Body,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (b *Builder) buildPolicyRouteRedirectAction(r *config.PolicyRedirect) (*envoy_config_route_v3.RedirectAction, error) {
|
func (b *Builder) buildPolicyRouteRedirectAction(r *config.PolicyRedirect) (*envoy_config_route_v3.RedirectAction, error) {
|
||||||
action := &envoy_config_route_v3.RedirectAction{}
|
action := &envoy_config_route_v3.RedirectAction{}
|
||||||
switch {
|
switch {
|
||||||
|
|
|
@ -444,7 +444,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "11444765232398592404"
|
"route_id": "16913502743845432363"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,7 +515,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2990091139764155677"
|
"route_id": "911713133804109577"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -585,7 +585,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2544588842279234006"
|
"route_id": "6407864870815560799"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -657,7 +657,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "10244970664102670752"
|
"route_id": "1103677309004574500"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -728,7 +728,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2544588842279234006"
|
"route_id": "6407864870815560799"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -798,7 +798,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2990091139764155677"
|
"route_id": "911713133804109577"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -869,7 +869,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2990091139764155677"
|
"route_id": "911713133804109577"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -940,7 +940,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "1052418080698022187"
|
"route_id": "17831746838845374842"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1123,7 +1123,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2226589900561460978"
|
"route_id": "15730681265277585877"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1195,7 +1195,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "2226589900561460978"
|
"route_id": "15730681265277585877"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1293,7 +1293,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "15508081512033148378"
|
"route_id": "16598125949405432745"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1423,7 +1423,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1494,7 +1494,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1570,7 +1570,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1641,7 +1641,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1712,7 +1712,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1788,7 +1788,7 @@ func Test_buildPolicyRoutesRewrite(t *testing.T) {
|
||||||
"checkSettings": {
|
"checkSettings": {
|
||||||
"contextExtensions": {
|
"contextExtensions": {
|
||||||
"internal": "false",
|
"internal": "false",
|
||||||
"route_id": "285016060542193864"
|
"route_id": "13828028232508831592"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,14 +30,14 @@ type Policy struct {
|
||||||
|
|
||||||
From string `mapstructure:"from" yaml:"from"`
|
From string `mapstructure:"from" yaml:"from"`
|
||||||
To WeightedURLs `mapstructure:"to" yaml:"to"`
|
To WeightedURLs `mapstructure:"to" yaml:"to"`
|
||||||
|
// Redirect is used for a redirect action instead of `To`
|
||||||
|
Redirect *PolicyRedirect `mapstructure:"redirect" yaml:"redirect"`
|
||||||
|
Response *DirectResponse `mapstructure:"response" yaml:"response,omitempty" json:"response,omitempty"`
|
||||||
|
|
||||||
// LbWeights are optional load balancing weights applied to endpoints specified in To
|
// LbWeights are optional load balancing weights applied to endpoints specified in To
|
||||||
// this field exists for compatibility with mapstructure
|
// this field exists for compatibility with mapstructure
|
||||||
LbWeights []uint32 `mapstructure:"_to_weights,omitempty" json:"-" yaml:"-"`
|
LbWeights []uint32 `mapstructure:"_to_weights,omitempty" json:"-" yaml:"-"`
|
||||||
|
|
||||||
// Redirect is used for a redirect action instead of `To`
|
|
||||||
Redirect *PolicyRedirect `mapstructure:"redirect" yaml:"redirect"`
|
|
||||||
|
|
||||||
// Identity related policy
|
// Identity related policy
|
||||||
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
|
AllowedUsers []string `mapstructure:"allowed_users" yaml:"allowed_users,omitempty" json:"allowed_users,omitempty"`
|
||||||
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
|
AllowedDomains []string `mapstructure:"allowed_domains" yaml:"allowed_domains,omitempty" json:"allowed_domains,omitempty"`
|
||||||
|
@ -213,6 +213,12 @@ type PolicyRedirect struct {
|
||||||
StripQuery *bool `mapstructure:"strip_query" yaml:"strip_query,omitempty" json:"strip_query,omitempty"`
|
StripQuery *bool `mapstructure:"strip_query" yaml:"strip_query,omitempty" json:"strip_query,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A DirectResponse is the response to an HTTP request.
|
||||||
|
type DirectResponse struct {
|
||||||
|
Status int `mapstructure:"status" yaml:"status,omitempty" json:"status,omitempty"`
|
||||||
|
Body string `mapstructure:"body" yaml:"body,omitempty" json:"body,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// NewPolicyFromProto creates a new Policy from a protobuf policy config route.
|
// NewPolicyFromProto creates a new Policy from a protobuf policy config route.
|
||||||
func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
||||||
var timeout *time.Duration
|
var timeout *time.Duration
|
||||||
|
@ -284,6 +290,11 @@ func NewPolicyFromProto(pb *configpb.Route) (*Policy, error) {
|
||||||
ResponseCode: pb.Redirect.ResponseCode,
|
ResponseCode: pb.Redirect.ResponseCode,
|
||||||
StripQuery: pb.Redirect.StripQuery,
|
StripQuery: pb.Redirect.StripQuery,
|
||||||
}
|
}
|
||||||
|
} else if pb.Response != nil {
|
||||||
|
p.Response = &DirectResponse{
|
||||||
|
Status: int(pb.Response.GetStatus()),
|
||||||
|
Body: pb.Response.GetBody(),
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
to, err := ParseWeightedUrls(pb.GetTo()...)
|
to, err := ParseWeightedUrls(pb.GetTo()...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -405,6 +416,11 @@ func (p *Policy) ToProto() (*configpb.Route, error) {
|
||||||
ResponseCode: p.Redirect.ResponseCode,
|
ResponseCode: p.Redirect.ResponseCode,
|
||||||
StripQuery: p.Redirect.StripQuery,
|
StripQuery: p.Redirect.StripQuery,
|
||||||
}
|
}
|
||||||
|
} else if p.Response != nil {
|
||||||
|
pb.Response = &configpb.RouteDirectResponse{
|
||||||
|
Status: uint32(p.Response.Status),
|
||||||
|
Body: p.Response.Body,
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
to, weights, err := p.To.Flatten()
|
to, weights, err := p.To.Flatten()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -446,8 +462,8 @@ func (p *Policy) Validate() error {
|
||||||
source.String())
|
source.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(p.To) == 0 && p.Redirect == nil {
|
if len(p.To) == 0 && p.Redirect == nil && p.Response == nil {
|
||||||
return errEitherToOrRedirectRequired
|
return errEitherToOrRedirectOrResponseRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range p.To {
|
for _, u := range p.To {
|
||||||
|
@ -567,8 +583,10 @@ func (p *Policy) RouteID() (uint64, error) {
|
||||||
id.To = dst
|
id.To = dst
|
||||||
} else if p.Redirect != nil {
|
} else if p.Redirect != nil {
|
||||||
id.Redirect = p.Redirect
|
id.Redirect = p.Redirect
|
||||||
|
} else if p.Response != nil {
|
||||||
|
id.Response = p.Response
|
||||||
} else {
|
} else {
|
||||||
return 0, errEitherToOrRedirectRequired
|
return 0, errEitherToOrRedirectOrResponseRequired
|
||||||
}
|
}
|
||||||
|
|
||||||
return hashutil.Hash(id)
|
return hashutil.Hash(id)
|
||||||
|
@ -685,6 +703,7 @@ type routeID struct {
|
||||||
Path string
|
Path string
|
||||||
Regex string
|
Regex string
|
||||||
Redirect *PolicyRedirect
|
Redirect *PolicyRedirect
|
||||||
|
Response *DirectResponse
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -33,12 +33,19 @@ message RouteRedirect {
|
||||||
optional bool strip_query = 8;
|
optional bool strip_query = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next ID: 62.
|
message RouteDirectResponse {
|
||||||
|
uint32 status = 1;
|
||||||
|
string body = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next ID: 63.
|
||||||
message Route {
|
message Route {
|
||||||
string name = 1;
|
string name = 1;
|
||||||
|
|
||||||
string from = 2;
|
string from = 2;
|
||||||
repeated string to = 3;
|
repeated string to = 3;
|
||||||
|
RouteRedirect redirect = 34;
|
||||||
|
RouteDirectResponse response = 62;
|
||||||
|
|
||||||
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/endpoint/v3/endpoint_components.proto#envoy-v3-api-msg-config-endpoint-v3-lbendpoint
|
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/endpoint/v3/endpoint_components.proto#envoy-v3-api-msg-config-endpoint-v3-lbendpoint
|
||||||
// optional load balancing weights assigned to upstream servers defined in TO
|
// optional load balancing weights assigned to upstream servers defined in TO
|
||||||
|
@ -47,8 +54,6 @@ message Route {
|
||||||
// len(load_balancing_weights)
|
// len(load_balancing_weights)
|
||||||
repeated uint32 load_balancing_weights = 37;
|
repeated uint32 load_balancing_weights = 37;
|
||||||
|
|
||||||
RouteRedirect redirect = 34;
|
|
||||||
|
|
||||||
repeated string allowed_users = 4 [ deprecated = true ];
|
repeated string allowed_users = 4 [ deprecated = true ];
|
||||||
// repeated string allowed_groups = 5 [ deprecated = true ];
|
// repeated string allowed_groups = 5 [ deprecated = true ];
|
||||||
repeated string allowed_domains = 6 [ deprecated = true ];
|
repeated string allowed_domains = 6 [ deprecated = true ];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue