mirror of https://github.com/hashicorp/consul
[NET-1151 NET-11228] api: Add fields for HTTP request normalization and L7 intentions header additions (1.15) (#21842)
api: Add fields for HTTP request normalization and L7 intentions header additions This feature is available in Consul CE 1.20.1 and Consul Enterprise 1.19.3, 1.18.4, and 1.15.15.pull/21858/head
parent
619c288023
commit
400c6a8bdc
|
@ -60,11 +60,13 @@ type IntentionHTTPPermission struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type IntentionHTTPHeaderPermission struct {
|
type IntentionHTTPHeaderPermission struct {
|
||||||
Name string
|
Name string
|
||||||
Present bool `json:",omitempty"`
|
Present bool `json:",omitempty"`
|
||||||
Exact string `json:",omitempty"`
|
Exact string `json:",omitempty"`
|
||||||
Prefix string `json:",omitempty"`
|
Prefix string `json:",omitempty"`
|
||||||
Suffix string `json:",omitempty"`
|
Suffix string `json:",omitempty"`
|
||||||
Regex string `json:",omitempty"`
|
Contains string `json:",omitempty"`
|
||||||
Invert bool `json:",omitempty"`
|
Regex string `json:",omitempty"`
|
||||||
|
Invert bool `json:",omitempty"`
|
||||||
|
IgnoreCase bool `json:",omitempty" alias:"ignore_case"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,12 +65,53 @@ type MeshDirectionalTLSConfig struct {
|
||||||
|
|
||||||
type MeshHTTPConfig struct {
|
type MeshHTTPConfig struct {
|
||||||
SanitizeXForwardedClientCert bool `alias:"sanitize_x_forwarded_client_cert"`
|
SanitizeXForwardedClientCert bool `alias:"sanitize_x_forwarded_client_cert"`
|
||||||
|
// Incoming configures settings for incoming HTTP traffic to mesh proxies.
|
||||||
|
Incoming *MeshDirectionalHTTPConfig `json:",omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MeshDirectionalHTTPConfig holds mesh configuration specific to HTTP
|
||||||
|
// requests for a given traffic direction.
|
||||||
|
type MeshDirectionalHTTPConfig struct {
|
||||||
|
RequestNormalization *RequestNormalizationMeshConfig `json:",omitempty" alias:"request_normalization"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type PeeringMeshConfig struct {
|
type PeeringMeshConfig struct {
|
||||||
PeerThroughMeshGateways bool `json:",omitempty" alias:"peer_through_mesh_gateways"`
|
PeerThroughMeshGateways bool `json:",omitempty" alias:"peer_through_mesh_gateways"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RequestNormalizationMeshConfig contains options pertaining to the
|
||||||
|
// normalization of HTTP requests processed by mesh proxies.
|
||||||
|
type RequestNormalizationMeshConfig struct {
|
||||||
|
// InsecureDisablePathNormalization sets the value of the \`normalize_path\` option in the Envoy listener's
|
||||||
|
// `HttpConnectionManager`. The default value is \`false\`. When set to \`true\` in Consul, \`normalize_path\` is
|
||||||
|
// set to \`false\` for the Envoy proxy. This parameter disables the normalization of request URL paths according to
|
||||||
|
// RFC 3986, conversion of \`\\\` to \`/\`, and decoding non-reserved %-encoded characters. When using L7 intentions
|
||||||
|
// with path match rules, we recommend enabling path normalization in order to avoid match rule circumvention with
|
||||||
|
// non-normalized path values.
|
||||||
|
InsecureDisablePathNormalization bool `json:",omitempty" alias:"insecure_disable_path_normalization"`
|
||||||
|
// MergeSlashes sets the value of the \`merge_slashes\` option in the Envoy listener's \`HttpConnectionManager\`.
|
||||||
|
// The default value is \`false\`. This option controls the normalization of request URL paths by merging
|
||||||
|
// consecutive \`/\` characters. This normalization is not part of RFC 3986. When using L7 intentions with path
|
||||||
|
// match rules, we recommend enabling this setting to avoid match rule circumvention through non-normalized path
|
||||||
|
// values, unless legitimate service traffic depends on allowing for repeat \`/\` characters, or upstream services
|
||||||
|
// are configured to differentiate between single and multiple slashes.
|
||||||
|
MergeSlashes bool `json:",omitempty" alias:"merge_slashes"`
|
||||||
|
// PathWithEscapedSlashesAction sets the value of the \`path_with_escaped_slashes_action\` option in the Envoy
|
||||||
|
// listener's \`HttpConnectionManager\`. The default value of this option is empty, which is equivalent to
|
||||||
|
// \`IMPLEMENTATION_SPECIFIC_DEFAULT\`. This parameter controls the action taken in response to request URL paths
|
||||||
|
// with escaped slashes in the path. When using L7 intentions with path match rules, we recommend enabling this
|
||||||
|
// setting to avoid match rule circumvention through non-normalized path values, unless legitimate service traffic
|
||||||
|
// depends on allowing for escaped \`/\` or \`\\\` characters, or upstream services are configured to differentiate
|
||||||
|
// between escaped and unescaped slashes. Refer to the Envoy documentation for more information on available
|
||||||
|
// options.
|
||||||
|
PathWithEscapedSlashesAction string `json:",omitempty" alias:"path_with_escaped_slashes_action"`
|
||||||
|
// HeadersWithUnderscoresAction sets the value of the \`headers_with_underscores_action\` option in the Envoy
|
||||||
|
// listener's \`HttpConnectionManager\` under \`common_http_protocol_options\`. The default value of this option is
|
||||||
|
// empty, which is equivalent to \`ALLOW\`. Refer to the Envoy documentation for more information on available
|
||||||
|
// options.
|
||||||
|
HeadersWithUnderscoresAction string `json:",omitempty" alias:"headers_with_underscores_action"`
|
||||||
|
}
|
||||||
|
|
||||||
func (e *MeshConfigEntry) GetKind() string { return MeshConfig }
|
func (e *MeshConfigEntry) GetKind() string { return MeshConfig }
|
||||||
func (e *MeshConfigEntry) GetName() string { return MeshConfigMesh }
|
func (e *MeshConfigEntry) GetName() string { return MeshConfigMesh }
|
||||||
func (e *MeshConfigEntry) GetPartition() string { return e.Partition }
|
func (e *MeshConfigEntry) GetPartition() string { return e.Partition }
|
||||||
|
|
|
@ -2305,6 +2305,10 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
name = "hdr-suffix"
|
name = "hdr-suffix"
|
||||||
suffix = "suffix"
|
suffix = "suffix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name = "hdr-contains"
|
||||||
|
contains = "contains"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name = "hdr-regex"
|
name = "hdr-regex"
|
||||||
regex = "regex"
|
regex = "regex"
|
||||||
|
@ -2313,7 +2317,12 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
name = "hdr-absent"
|
name = "hdr-absent"
|
||||||
present = true
|
present = true
|
||||||
invert = true
|
invert = true
|
||||||
}
|
},
|
||||||
|
{
|
||||||
|
name = "hdr-ignore-case"
|
||||||
|
exact = "exact"
|
||||||
|
ignore_case = true
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -2382,6 +2391,10 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
Name = "hdr-suffix"
|
Name = "hdr-suffix"
|
||||||
Suffix = "suffix"
|
Suffix = "suffix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name = "hdr-contains"
|
||||||
|
Contains = "contains"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name = "hdr-regex"
|
Name = "hdr-regex"
|
||||||
Regex = "regex"
|
Regex = "regex"
|
||||||
|
@ -2390,6 +2403,11 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
Name = "hdr-absent"
|
Name = "hdr-absent"
|
||||||
Present = true
|
Present = true
|
||||||
Invert = true
|
Invert = true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Name = "hdr-ignore-case"
|
||||||
|
Exact = "exact"
|
||||||
|
IgnoreCase = true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2460,6 +2478,10 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"name": "hdr-suffix",
|
"name": "hdr-suffix",
|
||||||
"suffix": "suffix"
|
"suffix": "suffix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hdr-contains",
|
||||||
|
"contains": "contains"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hdr-regex",
|
"name": "hdr-regex",
|
||||||
"regex": "regex"
|
"regex": "regex"
|
||||||
|
@ -2468,6 +2490,11 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"name": "hdr-absent",
|
"name": "hdr-absent",
|
||||||
"present": true,
|
"present": true,
|
||||||
"invert": true
|
"invert": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "hdr-ignore-case",
|
||||||
|
"exact": "exact",
|
||||||
|
"ignore_case": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2542,6 +2569,10 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"Name": "hdr-suffix",
|
"Name": "hdr-suffix",
|
||||||
"Suffix": "suffix"
|
"Suffix": "suffix"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"Name": "hdr-contains",
|
||||||
|
"Contains": "contains"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"Name": "hdr-regex",
|
"Name": "hdr-regex",
|
||||||
"Regex": "regex"
|
"Regex": "regex"
|
||||||
|
@ -2550,6 +2581,11 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"Name": "hdr-absent",
|
"Name": "hdr-absent",
|
||||||
"Present": true,
|
"Present": true,
|
||||||
"Invert": true
|
"Invert": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Name": "hdr-ignore-case",
|
||||||
|
"Exact": "exact",
|
||||||
|
"IgnoreCase": true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -2623,6 +2659,10 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
Name: "hdr-suffix",
|
Name: "hdr-suffix",
|
||||||
Suffix: "suffix",
|
Suffix: "suffix",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "hdr-contains",
|
||||||
|
Contains: "contains",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Name: "hdr-regex",
|
Name: "hdr-regex",
|
||||||
Regex: "regex",
|
Regex: "regex",
|
||||||
|
@ -2632,6 +2672,11 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
Present: true,
|
Present: true,
|
||||||
Invert: true,
|
Invert: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
Name: "hdr-ignore-case",
|
||||||
|
Exact: "exact",
|
||||||
|
IgnoreCase: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -2719,7 +2764,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "mesh",
|
name: "mesh: kitchen sink",
|
||||||
snake: `
|
snake: `
|
||||||
kind = "mesh"
|
kind = "mesh"
|
||||||
meta {
|
meta {
|
||||||
|
@ -2729,6 +2774,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
transparent_proxy {
|
transparent_proxy {
|
||||||
mesh_destinations_only = true
|
mesh_destinations_only = true
|
||||||
}
|
}
|
||||||
|
validate_clusters = true
|
||||||
tls {
|
tls {
|
||||||
incoming {
|
incoming {
|
||||||
tls_min_version = "TLSv1_1"
|
tls_min_version = "TLSv1_1"
|
||||||
|
@ -2747,6 +2793,20 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
http {
|
||||||
|
sanitize_x_forwarded_client_cert = true
|
||||||
|
incoming {
|
||||||
|
request_normalization {
|
||||||
|
insecure_disable_path_normalization = true
|
||||||
|
merge_slashes = true
|
||||||
|
path_with_escaped_slashes_action = "UNESCAPE_AND_FORWARD"
|
||||||
|
headers_with_underscores_action = "DROP_HEADER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
peering {
|
||||||
|
peer_through_mesh_gateways = true
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
camel: `
|
camel: `
|
||||||
Kind = "mesh"
|
Kind = "mesh"
|
||||||
|
@ -2757,6 +2817,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
TransparentProxy {
|
TransparentProxy {
|
||||||
MeshDestinationsOnly = true
|
MeshDestinationsOnly = true
|
||||||
}
|
}
|
||||||
|
ValidateClusters = true
|
||||||
TLS {
|
TLS {
|
||||||
Incoming {
|
Incoming {
|
||||||
TLSMinVersion = "TLSv1_1"
|
TLSMinVersion = "TLSv1_1"
|
||||||
|
@ -2775,6 +2836,20 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
HTTP {
|
||||||
|
SanitizeXForwardedClientCert = true
|
||||||
|
Incoming {
|
||||||
|
RequestNormalization {
|
||||||
|
InsecureDisablePathNormalization = true
|
||||||
|
MergeSlashes = true
|
||||||
|
PathWithEscapedSlashesAction = "UNESCAPE_AND_FORWARD"
|
||||||
|
HeadersWithUnderscoresAction = "DROP_HEADER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Peering {
|
||||||
|
PeerThroughMeshGateways = true
|
||||||
|
}
|
||||||
`,
|
`,
|
||||||
snakeJSON: `
|
snakeJSON: `
|
||||||
{
|
{
|
||||||
|
@ -2786,6 +2861,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"transparent_proxy": {
|
"transparent_proxy": {
|
||||||
"mesh_destinations_only": true
|
"mesh_destinations_only": true
|
||||||
},
|
},
|
||||||
|
"validate_clusters": true,
|
||||||
"tls": {
|
"tls": {
|
||||||
"incoming": {
|
"incoming": {
|
||||||
"tls_min_version": "TLSv1_1",
|
"tls_min_version": "TLSv1_1",
|
||||||
|
@ -2803,6 +2879,20 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"http": {
|
||||||
|
"sanitize_x_forwarded_client_cert": true,
|
||||||
|
"incoming": {
|
||||||
|
"request_normalization": {
|
||||||
|
"insecure_disable_path_normalization": true,
|
||||||
|
"merge_slashes": true,
|
||||||
|
"path_with_escaped_slashes_action": "UNESCAPE_AND_FORWARD",
|
||||||
|
"headers_with_underscores_action": "DROP_HEADER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peering": {
|
||||||
|
"peer_through_mesh_gateways": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -2816,6 +2906,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"TransparentProxy": {
|
"TransparentProxy": {
|
||||||
"MeshDestinationsOnly": true
|
"MeshDestinationsOnly": true
|
||||||
},
|
},
|
||||||
|
"ValidateClusters": true,
|
||||||
"TLS": {
|
"TLS": {
|
||||||
"Incoming": {
|
"Incoming": {
|
||||||
"TLSMinVersion": "TLSv1_1",
|
"TLSMinVersion": "TLSv1_1",
|
||||||
|
@ -2833,6 +2924,20 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"HTTP": {
|
||||||
|
"SanitizeXForwardedClientCert": true,
|
||||||
|
"Incoming": {
|
||||||
|
"RequestNormalization": {
|
||||||
|
"InsecureDisablePathNormalization": true,
|
||||||
|
"MergeSlashes": true,
|
||||||
|
"PathWithEscapedSlashesAction": "UNESCAPE_AND_FORWARD",
|
||||||
|
"HeadersWithUnderscoresAction": "DROP_HEADER"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"Peering": {
|
||||||
|
"PeerThroughMeshGateways": true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`,
|
`,
|
||||||
|
@ -2844,6 +2949,7 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
TransparentProxy: api.TransparentProxyMeshConfig{
|
TransparentProxy: api.TransparentProxyMeshConfig{
|
||||||
MeshDestinationsOnly: true,
|
MeshDestinationsOnly: true,
|
||||||
},
|
},
|
||||||
|
ValidateClusters: true,
|
||||||
TLS: &api.MeshTLSConfig{
|
TLS: &api.MeshTLSConfig{
|
||||||
Incoming: &api.MeshDirectionalTLSConfig{
|
Incoming: &api.MeshDirectionalTLSConfig{
|
||||||
TLSMinVersion: "TLSv1_1",
|
TLSMinVersion: "TLSv1_1",
|
||||||
|
@ -2862,6 +2968,20 @@ func TestParseConfigEntry(t *testing.T) {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
HTTP: &api.MeshHTTPConfig{
|
||||||
|
SanitizeXForwardedClientCert: true,
|
||||||
|
Incoming: &api.MeshDirectionalHTTPConfig{
|
||||||
|
RequestNormalization: &api.RequestNormalizationMeshConfig{
|
||||||
|
InsecureDisablePathNormalization: true,
|
||||||
|
MergeSlashes: true,
|
||||||
|
PathWithEscapedSlashesAction: "UNESCAPE_AND_FORWARD",
|
||||||
|
HeadersWithUnderscoresAction: "DROP_HEADER",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Peering: &api.PeeringMeshConfig{
|
||||||
|
PeerThroughMeshGateways: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue