Skip to content

Commit

Permalink
chore(MeshHTTPRoute): add precise hostname validation (#8548)
Browse files Browse the repository at this point in the history
Added validation for PreciseHostname used in RequestRedirect
and URLRewrite filters

Signed-off-by: Bart Smykla <[email protected]>
  • Loading branch information
bartsmykla authored Dec 6, 2023
1 parent ccaca5b commit f8f5b40
Show file tree
Hide file tree
Showing 2 changed files with 185 additions and 0 deletions.
30 changes: 30 additions & 0 deletions pkg/plugins/policies/meshhttproute/api/v1alpha1/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"strings"

k8s_validation "k8s.io/apimachinery/pkg/util/validation"

common_api "github.com/kumahq/kuma/api/common/v1alpha1"
"github.com/kumahq/kuma/pkg/core/resources/apis/mesh"
"github.com/kumahq/kuma/pkg/core/validators"
Expand Down Expand Up @@ -201,6 +203,7 @@ func validateFilters(filters *[]Filter, matches []Match) validators.ValidationEr
hasAnyMatchesWithoutPrefix(matches) {
errs.AddViolationAt(path.Field("requestRedirect").Field("path").Field("replacePrefixMatch"), "can only appear if all matches match a path prefix")
}
errs.AddErrorAt(path.Field("requestRedirect").Field("hostname"), validatePreciseHostname(filter.RequestRedirect.Hostname))
case URLRewriteType:
if filter.URLRewrite == nil {
errs.AddViolationAt(path.Field("urlRewrite"), validators.MustBeDefined)
Expand All @@ -211,6 +214,7 @@ func validateFilters(filters *[]Filter, matches []Match) validators.ValidationEr
hasAnyMatchesWithoutPrefix(matches) {
errs.AddViolationAt(path.Field("urlRewrite").Field("path").Field("replacePrefixMatch"), "can only appear if all matches match a path prefix")
}
errs.AddErrorAt(path.Field("urlRewrite").Field("hostname"), validatePreciseHostname(filter.URLRewrite.Hostname))
case RequestMirrorType:
if filter.RequestMirror == nil {
errs.AddViolationAt(path.Field("requestMirror"), validators.MustBeDefined)
Expand All @@ -231,6 +235,32 @@ func validateFilters(filters *[]Filter, matches []Match) validators.ValidationEr
return errs
}

// PreciseHostname is the fully qualified domain name of a network host. This
// matches the RFC 1123 definition of a hostname with 1 notable exception that
// numeric IP addresses are not allowed.
//
// Note that as per RFC1035 and RFC1123, a *label* must consist of lower case
// alphanumeric characters or '-', and must start and end with an alphanumeric
// character. No other punctuation is allowed.
func validatePreciseHostname(hostname *PreciseHostname) validators.ValidationError {
var errs validators.ValidationError

if hostname == nil {
return errs
}

if len(k8s_validation.IsValidIP(string(*hostname))) == 0 {
errs.AddViolationAt(validators.Root(), "cannot be an IP address")
return errs
}

for _, violation := range k8s_validation.IsDNS1123Subdomain(string(*hostname)) {
errs.AddViolationAt(validators.Root(), violation)
}

return errs
}

func validateHeaderModifier(modifier *HeaderModifier) validators.ValidationError {
var errs validators.ValidationError

Expand Down
155 changes: 155 additions & 0 deletions pkg/plugins/policies/meshhttproute/api/v1alpha1/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,161 @@ to:
kind: MeshServiceSubset
tags:
version: v1
`),
ErrorCases("invalid hostnames",
[]validators.Violation{
{
Field: "spec.to[0].rules[0].default.filters[0].requestRedirect.hostname",
Message: "cannot be an IP address",
},
{
Field: "spec.to[0].rules[0].default.filters[1].requestRedirect.hostname",
Message: "cannot be an IP address",
},
{
Field: "spec.to[0].rules[0].default.filters[2].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[3].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[4].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[5].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[6].requestRedirect.hostname",
Message: "must be no more than 253 characters",
},
{
Field: "spec.to[0].rules[0].default.filters[7].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[8].requestRedirect.hostname",
Message: "must be no more than 253 characters",
},
{
Field: "spec.to[0].rules[0].default.filters[8].requestRedirect.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[9].urlRewrite.hostname",
Message: "cannot be an IP address",
},
{
Field: "spec.to[0].rules[0].default.filters[10].urlRewrite.hostname",
Message: "cannot be an IP address",
},
{
Field: "spec.to[0].rules[0].default.filters[11].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[12].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[13].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[14].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[15].urlRewrite.hostname",
Message: "must be no more than 253 characters",
},
{
Field: "spec.to[0].rules[0].default.filters[16].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
{
Field: "spec.to[0].rules[0].default.filters[17].urlRewrite.hostname",
Message: "must be no more than 253 characters",
},
{
Field: "spec.to[0].rules[0].default.filters[17].urlRewrite.hostname",
Message: "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')",
},
}, `
type: MeshHTTPRoute
mesh: mesh-1
name: route-1
targetRef:
kind: MeshService
name: frontend
to:
- targetRef:
kind: MeshService
name: frontend
rules:
- matches:
- path:
type: PathPrefix
value: /
default:
filters:
- type: RequestRedirect
requestRedirect:
hostname: 127.0.0.1
- type: RequestRedirect
requestRedirect:
hostname: 2001:db8:3333:4444:5555:6666:7777:8888
- type: RequestRedirect
requestRedirect:
hostname: a..bc
- type: RequestRedirect
requestRedirect:
hostname: ec2-35-160-210-253.us-west-2-.compute.amazonaws.com
- type: RequestRedirect
requestRedirect:
hostname: -ec2_35$160%210-253.us-west-2-.compute.amazonaws.com
- type: RequestRedirect
requestRedirect:
hostname: ec2-35-160-210-253.us-west-2-.compute.amazonaws.com.
- type: RequestRedirect
requestRedirect:
hostname: a23456789-a23456789-a234567890.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.com
- type: RequestRedirect
requestRedirect:
hostname: mx.foo.com.
- type: RequestRedirect
requestRedirect:
hostname: a23456789-a23456789-a234567890.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a2345678.com.
- type: URLRewrite
urlRewrite:
hostname: 127.0.0.1
- type: URLRewrite
urlRewrite:
hostname: 2001:db8:3333:4444:5555:6666:7777:8888
- type: URLRewrite
urlRewrite:
hostname: a..bc
- type: URLRewrite
urlRewrite:
hostname: ec2-35-160-210-253.us-west-2-.compute.amazonaws.com
- type: URLRewrite
urlRewrite:
hostname: -ec2_35$160%210-253.us-west-2-.compute.amazonaws.com
- type: URLRewrite
urlRewrite:
hostname: ec2-35-160-210-253.us-west-2-.compute.amazonaws.com.
- type: URLRewrite
urlRewrite:
hostname: a23456789-a23456789-a234567890.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.com
- type: URLRewrite
urlRewrite:
hostname: mx.foo.com.
- type: URLRewrite
urlRewrite:
hostname: a23456789-a23456789-a234567890.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a23456789.a2345678.com.
`),
)
DescribeValidCases(
Expand Down

0 comments on commit f8f5b40

Please sign in to comment.