Skip to content
21 changes: 21 additions & 0 deletions internal/gatewayapi/backendtrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -933,19 +933,37 @@
}, nil
}

func checkResponseBodySize(b *string) error {
// Make this configurable in the future
// https://www.envoyproxy.io/docs/envoy/latest/api-v3/config/route/v3/route.proto.html#max_direct_response_body_size_bytes
maxDirectResponseSize := 4096
lenB := len(*b)
if lenB > maxDirectResponseSize {
return fmt.Errorf("response.body size %d greater than the max size %d", lenB, maxDirectResponseSize)
}

return nil
}

func getCustomResponseBody(body *egv1a1.CustomResponseBody, resources *resource.Resources, policyNs string) (*string, error) {
if body != nil && body.Type != nil && *body.Type == egv1a1.ResponseValueTypeValueRef {
cm := resources.GetConfigMap(policyNs, string(body.ValueRef.Name))
if cm != nil {
b, dataOk := cm.Data["response.body"]
switch {
case dataOk:
if err := checkResponseBodySize(&b); err != nil {
return nil, err
}

Check warning on line 957 in internal/gatewayapi/backendtrafficpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/backendtrafficpolicy.go#L956-L957

Added lines #L956 - L957 were not covered by tests
return &b, nil
case len(cm.Data) > 0: // Fallback to the first key if response.body is not found
for _, value := range cm.Data {
b = value
break
}
if err := checkResponseBodySize(&b); err != nil {
return nil, err
}

Check warning on line 966 in internal/gatewayapi/backendtrafficpolicy.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/backendtrafficpolicy.go#L964-L966

Added lines #L964 - L966 were not covered by tests
return &b, nil
default:
return nil, fmt.Errorf("can't find the key response.body in the referenced configmap %s", body.ValueRef.Name)
Expand All @@ -955,6 +973,9 @@
return nil, fmt.Errorf("can't find the referenced configmap %s", body.ValueRef.Name)
}
} else if body != nil && body.Inline != nil {
if err := checkResponseBodySize(body.Inline); err != nil {
return nil, err
}
return body.Inline, nil
}

Expand Down
14 changes: 12 additions & 2 deletions internal/gatewayapi/clienttrafficpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -948,7 +948,12 @@ func translateEarlyRequestHeaders(headerModifier *gwapiv1.HTTPHeaderFilter) ([]i
}
// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
if strings.ContainsAny(string(addHeader.Name), "/:") {
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders Filter cannot set headers with a '/' or ':' character in them. Header: %q", string(addHeader.Name)))
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot add a header with a '/' or ':' character in them. Header: '%q'", string(addHeader.Name)))
continue
}
// Gateway API specification allows only valid value as defined by RFC 7230
if !HeaderValueRegexp.MatchString(addHeader.Value) {
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot add a header with an invalid value. Header: '%q'", string(addHeader.Name)))
continue
}
// Check if the header is a duplicate
Expand Down Expand Up @@ -988,7 +993,12 @@ func translateEarlyRequestHeaders(headerModifier *gwapiv1.HTTPHeaderFilter) ([]i
}
// Per Gateway API specification on HTTPHeaderName, : and / are invalid characters in header names
if strings.ContainsAny(string(setHeader.Name), "/:") {
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set headers with a '/' or ':' character in them. Header: '%s'", string(setHeader.Name)))
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set a header with a '/' or ':' character in them. Header: '%q'", string(setHeader.Name)))
continue
}
// Gateway API specification allows only valid value as defined by RFC 7230
if !HeaderValueRegexp.MatchString(setHeader.Value) {
errs = errors.Join(errs, fmt.Errorf("EarlyRequestHeaders cannot set a header with an invalid value. Header: '%q'", string(setHeader.Name)))
continue
}

Expand Down
46 changes: 45 additions & 1 deletion internal/gatewayapi/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ type HTTPFilterIR struct {
ExtensionRefs []*ir.UnstructuredRef
}

// Header value pattern according to RFC 7230
var HeaderValueRegexp = regexp.MustCompile(`^[!-~]+([\t ]?[!-~]+)*$`)

// ProcessHTTPFilters translates gateway api http filters to IRs.
func (t *Translator) ProcessHTTPFilters(parentRef *RouteParentContext,
route RouteContext,
Expand Down Expand Up @@ -370,6 +373,7 @@ func (t *Translator) processRequestHeaderModifierFilter(
emptyFilterConfig = false
}
for _, addHeader := range headersToAdd {

emptyFilterConfig = false
if addHeader.Name == "" {
updateRouteStatusForFilter(
Expand All @@ -378,16 +382,27 @@ func (t *Translator) processRequestHeaderModifierFilter(
// try to process the rest of the headers and produce a valid config.
continue
}

if !isModifiableHeader(string(addHeader.Name)) {
updateRouteStatusForFilter(
filterContext,
fmt.Sprintf(
"Header: %q. The RequestHeaderModifier filter cannot set the Host header or headers with a '/' "+
"Header: %q. The RequestHeaderModifier filter cannot add the Host header or headers with a '/' "+
"or ':' character in them. To modify the Host header use the URLRewrite or the HTTPRouteFilter filter.",
string(addHeader.Name)),
)
continue
}

if !HeaderValueRegexp.MatchString(addHeader.Value) {
updateRouteStatusForFilter(
filterContext,
fmt.Sprintf(
"Header: %q. RequestHeaderModifier Filter cannot add a header with an invalid value.",
string(addHeader.Name)))
continue
}

// Check if the header is a duplicate
headerKey := string(addHeader.Name)
canAddHeader := true
Expand Down Expand Up @@ -437,6 +452,15 @@ func (t *Translator) processRequestHeaderModifierFilter(
continue
}

if !HeaderValueRegexp.MatchString(setHeader.Value) {
updateRouteStatusForFilter(
filterContext,
fmt.Sprintf(
"Header: %q. RequestHeaderModifier Filter cannot set a header with an invalid value.",
string(setHeader.Name)))
continue
}

// Check if the header to be set has already been configured
headerKey := string(setHeader.Name)
canAddHeader := true
Expand Down Expand Up @@ -550,6 +574,7 @@ func (t *Translator) processResponseHeaderModifierFilter(
// try to process the rest of the headers and produce a valid config.
continue
}

if !isModifiableHeader(string(addHeader.Name)) {
updateRouteStatusForFilter(
filterContext,
Expand All @@ -559,6 +584,16 @@ func (t *Translator) processResponseHeaderModifierFilter(
string(addHeader.Name)))
continue
}

if !HeaderValueRegexp.MatchString(addHeader.Value) {
updateRouteStatusForFilter(
filterContext,
fmt.Sprintf(
"Header: %q. ResponseHeaderModifier Filter cannot add a header with an invalid value.",
string(addHeader.Name)))
continue
}

// Check if the header is a duplicate
headerKey := string(addHeader.Name)
canAddHeader := true
Expand Down Expand Up @@ -607,6 +642,15 @@ func (t *Translator) processResponseHeaderModifierFilter(
continue
}

if !HeaderValueRegexp.MatchString(setHeader.Value) {
updateRouteStatusForFilter(
filterContext,
fmt.Sprintf(
"Header: %q. ResponseHeaderModifier Filter cannot set a header with an invalid value.",
string(setHeader.Name)))
continue
}

// Check if the header to be set has already been configured
headerKey := string(setHeader.Name)
canAddHeader := true
Expand Down
32 changes: 31 additions & 1 deletion internal/gatewayapi/sort.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
// Equal case

// 3. Sort based on the number of Header matches.
// When the number is same, sort based on number of Exact Header matches.
hCountI := len(x[i].HeaderMatches)
hCountJ := len(x[j].HeaderMatches)
if hCountI < hCountJ {
Expand All @@ -71,12 +72,31 @@
if hCountI > hCountJ {
return false
}

hExtNumberI := numberOfExactMatches(x[i].HeaderMatches)
hExtNumberJ := numberOfExactMatches(x[j].HeaderMatches)
if hExtNumberI < hExtNumberJ {
return true
}
if hExtNumberI > hExtNumberJ {
return false
}

Check warning on line 83 in internal/gatewayapi/sort.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/sort.go#L82-L83

Added lines #L82 - L83 were not covered by tests
// Equal case

// 4. Sort based on the number of Query param matches.
// When the number is same, sort based on number of Exact Query param matches.
qCountI := len(x[i].QueryParamMatches)
qCountJ := len(x[j].QueryParamMatches)
return qCountI < qCountJ
if qCountI < qCountJ {
return true
}

Check warning on line 92 in internal/gatewayapi/sort.go

View check run for this annotation

Codecov / codecov/patch

internal/gatewayapi/sort.go#L91-L92

Added lines #L91 - L92 were not covered by tests
if qCountI > qCountJ {
return false
}

qExtNumberI := numberOfExactMatches(x[i].QueryParamMatches)
qExtNumberJ := numberOfExactMatches(x[j].QueryParamMatches)
return qExtNumberI < qExtNumberJ
}

// sortXdsIR sorts the xdsIR based on the match precedence
Expand Down Expand Up @@ -107,3 +127,13 @@
}
return 0
}

func numberOfExactMatches(stringMatches []*ir.StringMatch) int {
var cnt int
for _, stringMatch := range stringMatches {
if stringMatch != nil && stringMatch.Exact != nil {
cnt++
}
}
return cnt
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ clientTrafficPolicies:
add:
- name: ""
value: "empty"
- name: "invalid"
- name: "Example/Header/1"
value: ":/"
- name: "example-header-2"
value: |
multi-line-header-value
set:
- name: ""
value: "empty"
- name: "invalid"
- name: "Example:Header:3"
value: ":/"
- name: "example-header-4"
value: " invalid"
remove:
- ""
targetRef:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,20 @@ clientTrafficPolicies:
add:
- name: ""
value: empty
- name: invalid
- name: Example/Header/1
value: :/
- name: example-header-2
value: |
multi-line-header-value
remove:
- ""
set:
- name: ""
value: empty
- name: invalid
- name: Example:Header:3
value: :/
- name: example-header-4
value: ' invalid'
enableEnvoyHeaders: true
preserveXRequestID: true
withUnderscoresAction: Allow
Expand All @@ -38,8 +43,13 @@ clientTrafficPolicies:
- lastTransitionTime: null
message: |-
Headers: EarlyRequestHeaders cannot add a header with an empty name
EarlyRequestHeaders cannot add a header with a '/' or ':' character in them. Header: '"Example/Header/1"'
EarlyRequestHeaders cannot add a header with an invalid value. Header: '"example-header-2"'
EarlyRequestHeaders cannot set a header with an empty name
EarlyRequestHeaders cannot remove a header with an empty name.
EarlyRequestHeaders cannot set a header with a '/' or ':' character in them. Header: '"Example:Header:3"'
EarlyRequestHeaders cannot set a header with an invalid value. Header: '"example-header-4"'
EarlyRequestHeaders cannot remove a header with an empty name
EarlyRequestHeaders did not provide valid configuration to add/set/remove any headers.
reason: Invalid
status: "False"
type: Accepted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ httpRoutes:
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: direct-response-with-errors
name: direct-response-with-value-not-found
namespace: default
spec:
parentRefs:
Expand All @@ -67,6 +67,27 @@ httpRoutes:
group: gateway.envoyproxy.io
kind: HTTPRouteFilter
name: direct-response-value-ref-not-found
- apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: direct-response-too-long
namespace: default
spec:
parentRefs:
- name: gateway-1
namespace: envoy-gateway
sectionName: http
rules:
- matches:
- path:
type: PathPrefix
value: /too-long
filters:
- type: ExtensionRef
extensionRef:
group: gateway.envoyproxy.io
kind: HTTPRouteFilter
name: direct-response-too-long
configMaps:
- apiVersion: v1
kind: ConfigMap
Expand Down Expand Up @@ -117,3 +138,14 @@ httpFilters:
group: ""
kind: ConfigMap
name: value-ref-response
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: HTTPRouteFilter
metadata:
name: direct-response-too-long
namespace: default
spec:
directResponse:
contentType: text/plain
body:
type: Inline
inline: "-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------"
Loading
Loading