mirror of
https://github.com/status-im/consul.git
synced 2025-01-10 13:55:55 +00:00
Header manip and validation added for ingress-gateway entries
This commit is contained in:
parent
6cac30aa22
commit
46e4041283
@ -1461,3 +1461,36 @@ func IsProtocolHTTPLike(protocol string) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHeaderModifiers is a set of rules for HTTP header modification that
|
||||
// should be performed by proxies as the request passes through them. It can
|
||||
// operate on either request or response headers depending on the context in
|
||||
// which it is used.
|
||||
type HTTPHeaderModifiers struct {
|
||||
// Add is a set of name -> value pairs that should be appended to the request
|
||||
// or response (i.e. allowing duplicates if the same header already exists).
|
||||
Add map[string]string `json:",omitempty"`
|
||||
|
||||
// Set is a set of name -> value pairs that should be added to the request or
|
||||
// response, overwriting any existing header values of the same name.
|
||||
Set map[string]string `json:",omitempty"`
|
||||
|
||||
// Remove is the set of header names that should be stripped from the request
|
||||
// or response.
|
||||
Remove []string `json:",omitempty"`
|
||||
}
|
||||
|
||||
func (m *HTTPHeaderModifiers) Validate(protocol string) error {
|
||||
if m == nil {
|
||||
// Empty is always valid
|
||||
return nil
|
||||
}
|
||||
if len(m.Add) == 0 && len(m.Set) == 0 && len(m.Remove) == 0 {
|
||||
return nil
|
||||
}
|
||||
if !IsProtocolHTTPLike(protocol) {
|
||||
// Non nil but context is not an httpish protocol
|
||||
return fmt.Errorf("only valid for http, http2 and grpc protocols")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -75,6 +75,10 @@ type IngressService struct {
|
||||
// using a "tcp" listener.
|
||||
Hosts []string
|
||||
|
||||
// Allow HTTP header manipulation to be configured.
|
||||
RequestHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"request_headers"`
|
||||
ResponseHeaders *HTTPHeaderModifiers `json:",omitempty" alias:"response_headers"`
|
||||
|
||||
Meta map[string]string `json:",omitempty"`
|
||||
EnterpriseMeta `hcl:",squash" mapstructure:",squash"`
|
||||
}
|
||||
@ -169,6 +173,13 @@ func (e *IngressGatewayConfigEntry) Validate() error {
|
||||
return fmt.Errorf("Services[%d].%v", i, err)
|
||||
}
|
||||
|
||||
if err := s.RequestHeaders.Validate(listener.Protocol); err != nil {
|
||||
return fmt.Errorf("request headers %s (service %q on listener on port %d)", err, s.Name, listener.Port)
|
||||
}
|
||||
if err := s.ResponseHeaders.Validate(listener.Protocol); err != nil {
|
||||
return fmt.Errorf("response headers %s (service %q on listener on port %d)", err, s.Name, listener.Port)
|
||||
}
|
||||
|
||||
if listener.Protocol == "tcp" {
|
||||
if s.Name == WildcardSpecifier {
|
||||
return fmt.Errorf("Wildcard service name is only valid for protocol = 'http' (listener on port %d)", listener.Port)
|
||||
|
@ -394,6 +394,135 @@ func TestIngressGatewayConfigEntry(t *testing.T) {
|
||||
},
|
||||
validateErr: `Host '*' is not allowed when TLS is enabled, all hosts must be valid DNS records to add as a DNSSAN`,
|
||||
},
|
||||
"request header manip allowed for http(ish) protocol": {
|
||||
entry: &IngressGatewayConfigEntry{
|
||||
Kind: "ingress-gateway",
|
||||
Name: "ingress-web",
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
Port: 1111,
|
||||
Protocol: "http",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "web",
|
||||
RequestHeaders: &HTTPHeaderModifiers{
|
||||
Set: map[string]string{"x-foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 2222,
|
||||
Protocol: "http2",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "web2",
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Set: map[string]string{"x-foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 3333,
|
||||
Protocol: "grpc",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "api",
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Remove: []string{"x-grpc-internal"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
// Unchanged
|
||||
expected: &IngressGatewayConfigEntry{
|
||||
Kind: "ingress-gateway",
|
||||
Name: "ingress-web",
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
Port: 1111,
|
||||
Protocol: "http",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "web",
|
||||
RequestHeaders: &HTTPHeaderModifiers{
|
||||
Set: map[string]string{"x-foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 2222,
|
||||
Protocol: "http2",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "web2",
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Set: map[string]string{"x-foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Port: 3333,
|
||||
Protocol: "grpc",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "api",
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Remove: []string{"x-grpc-internal"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
"request header manip not allowed for non-http protocol": {
|
||||
entry: &IngressGatewayConfigEntry{
|
||||
Kind: "ingress-gateway",
|
||||
Name: "ingress-web",
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
Port: 1111,
|
||||
Protocol: "tcp",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "db",
|
||||
RequestHeaders: &HTTPHeaderModifiers{
|
||||
Set: map[string]string{"x-foo": "bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "request headers only valid for http",
|
||||
},
|
||||
"response header manip not allowed for non-http protocol": {
|
||||
entry: &IngressGatewayConfigEntry{
|
||||
Kind: "ingress-gateway",
|
||||
Name: "ingress-web",
|
||||
Listeners: []IngressListener{
|
||||
{
|
||||
Port: 1111,
|
||||
Protocol: "tcp",
|
||||
Services: []IngressService{
|
||||
{
|
||||
Name: "db",
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Remove: []string{"x-foo"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
validateErr: "response headers only valid for http",
|
||||
},
|
||||
}
|
||||
|
||||
testConfigEntryNormalizeAndValidate(t, cases)
|
||||
|
@ -1037,6 +1037,24 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name = "db"
|
||||
request_headers {
|
||||
add {
|
||||
foo = "bar"
|
||||
}
|
||||
set {
|
||||
bar = "baz"
|
||||
}
|
||||
remove = ["qux"]
|
||||
}
|
||||
response_headers {
|
||||
add {
|
||||
foo = "bar"
|
||||
}
|
||||
set {
|
||||
bar = "baz"
|
||||
}
|
||||
remove = ["qux"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1081,6 +1099,24 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name = "db"
|
||||
RequestHeaders {
|
||||
Add {
|
||||
foo = "bar"
|
||||
}
|
||||
Set {
|
||||
bar = "baz"
|
||||
}
|
||||
Remove = ["qux"]
|
||||
}
|
||||
ResponseHeaders {
|
||||
Add {
|
||||
foo = "bar"
|
||||
}
|
||||
Set {
|
||||
bar = "baz"
|
||||
}
|
||||
Remove = ["qux"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@ -1125,6 +1161,16 @@ func TestDecodeConfigEntry(t *testing.T) {
|
||||
},
|
||||
{
|
||||
Name: "db",
|
||||
RequestHeaders: &HTTPHeaderModifiers{
|
||||
Add: map[string]string{"foo": "bar"},
|
||||
Set: map[string]string{"bar": "baz"},
|
||||
Remove: []string{"qux"},
|
||||
},
|
||||
ResponseHeaders: &HTTPHeaderModifiers{
|
||||
Add: map[string]string{"foo": "bar"},
|
||||
Set: map[string]string{"bar": "baz"},
|
||||
Remove: []string{"qux"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user