Skip to content

Commit

Permalink
docs: update security and reference docs for L7 intentions bypass pre…
Browse files Browse the repository at this point in the history
…vention

- Update security docs with best practices for service intentions
  configuration
- Update configuration entry references for mesh and intentions to
  reflect new values and add guidance on usage
  • Loading branch information
zalimeni committed Oct 14, 2024
1 parent d7e7492 commit 7f5b4db
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 12 deletions.
103 changes: 103 additions & 0 deletions website/content/docs/connect/config-entries/mesh.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,58 @@ spec:

Note that the Kubernetes example does not include a `partition` field. Configuration entries are applied on Kubernetes using [custom resource definitions (CRD)](/consul/docs/k8s/crds), which can only be scoped to their own partition.

### Request Normalization

Enable options under `HTTP.Incoming.RequestNormalization` to apply normalization to all inbound traffic to mesh proxies.

<CodeTabs tabs={[ "HCL", "Kubernetes YAML", "JSON" ]}>

```hcl
Kind = "mesh"
HTTP {
Incoming {
RequestNormalization {
InsecureDisablePathNormalization = false // default false, shown for completeness
MergeSlashes = true
PathWithEscapedSlashesAction = "UNESCAPE_AND_FORWARD"
HeadersWithUnderscoresAction = "REJECT_REQUEST"
}
}
}
```

```yaml
apiVersion: consul.hashicorp.com/v1alpha1
kind: Mesh
metadata:
name: mesh
spec:
http:
incoming:
requestNormalization:
insecureDisablePathNormalization: false # default false, shown for completeness
mergeSlashes: true
pathWithEscapedSlashesAction: UNESCAPE_AND_FORWARD
headersWithUnderscoresAction: REJECT_REQUEST
```
```json
{
"Kind": "mesh",
"HTTP": {
"Incoming": {
"RequestNormalization": {
"InsecureDisablePathNormalization": false,
"MergeSlashes": true,
"PathWithEscapedSlashesAction": "UNESCAPE_AND_FORWARD",
"HeadersWithUnderscoresAction": "REJECT_REQUEST"
}
}
}
}
```

</CodeTabs>

## Available Fields

Expand Down Expand Up @@ -452,6 +504,57 @@ Note that the Kubernetes example does not include a `partition` field. Configura
for all Envoy proxies. As a result, Consul will not include the \`x-forwarded-client-cert\` header in the next hop.
If set to \`false\` (default), the XFCC header is propagated to upstream applications.`,
},
{
name: 'Incoming',
type: 'DirectionalHTTPConfig: <optional>',
description: `HTTP configuration for inbound traffic to mesh proxies.`,
children: [
{
name: 'RequestNormalization',
type: 'RequestNormalizationConfig: <optional>',
description: `Request normalization configuration for inbound traffic to mesh proxies.`,
children: [
{
name: 'InsecureDisablePathNormalization',
type: 'bool: false',
description: `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.`,
},
{
name: 'MergeSlashes',
type: 'bool: false',
description: `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.`,
},
{
name: 'PathWithEscapedSlashesAction',
type: 'string: "UNESCAPE_AND_FORWARD"',
description: `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.`,
},
{
name: 'HeadersWithUnderscoresAction',
type: 'string: "REJECT_REQUEST"',
description: `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.`,
},
],
},
],
}
],
},
{
Expand Down
67 changes: 56 additions & 11 deletions website/content/docs/connect/config-entries/service-intentions.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ The following outline shows how to format the service intentions configuration e
- [`exact`](#spec-sources-permissions-http-header): string | no default
- [`prefix`](#spec-sources-permissions-http-header): string | no default
- [`suffix`](#spec-sources-permissions-http-header): string | no default
- [`contains`](#spec-sources-permissions-http-header): string | no default
- [`regex`](#spec-sources-permissions-http-header): string | no default
- [`ignoreCase`](#spec-sources-permissions-http-header): boolean | `false`
- [`invert`](#spec-sources-permissions-http-header): boolean | `false`
- [`description`](#spec-sources-description): string

Expand Down Expand Up @@ -156,18 +158,31 @@ Sources = [
{
Name = "<http header name>" # string
Present = <true or false> # boolean
Invert = <true or false> # boolean
},
{
Name = "<http header name>" # string
Exact = "<header-value>" # boolean
IgnoreCase = <true or false> # boolean
Invert = <true or false> # boolean
},
{
Name = "<http header name>" # string
Prefix = "<source header value prefix>" # string
IgnoreCase = <true or false> # boolean
Invert = <true or false> # boolean
},
{
Name = "<http header name>" # string
Suffix = "<source header value suffix>" # string
IgnoreCase = <true or false> # boolean
Invert = <true or false> # boolean
},
{
Name = "<http header name>" # string
Contains = "<value to search for>" # string
IgnoreCase = <true or false> # boolean
Invert = <true or false> # boolean
},
{
Name = "<http header name>" # string
Expand Down Expand Up @@ -227,12 +242,23 @@ spec:
header:
- name: <http header name>
present: true
invert: false
- name: <http header name>
exact: false
exact: <header-value>
ignoreCase: false
invert: false
- name: <http header name>
prefix: <source header value prefix>
ignoreCase: false
invert: false
- name: <http header name>
suffix: <source header value suffix>
ignoreCase: false
invert: false
- name: <http header name>
contains: <value to search for>
ignoreCase: false
invert: false
- name: <http header name>
regex: <regex pattern to match>
invert: false
Expand Down Expand Up @@ -287,19 +313,32 @@ spec:
"Header":[
{
"Name":"<http header name>",
"Present":true
"Present":true,
"Invert":false
},
{
"Name":"<http header name>",
"Exact":"<header-value>",
"IgnoreCase":false,,
"Invert":false
},
{
"Name":"<http header name>",
"Exact":false
"Prefix":"<source header value prefix>",
"IgnoreCase":false,
"Invert":false
},
{
"Name":"<http header name>",
"Prefix":"<source header value prefix>"
"Suffix":"<source header value suffix>",
"IgnoreCase":false,
"Invert":false
},
{
"Name":"<http header name>",
"Suffix":"<source header value suffix>"
"Contains":"<value to search for>",
"IgnoreCase":false,
"Invert":false
},
{
"Name":"<http header name>",
Expand Down Expand Up @@ -923,16 +962,22 @@ Specifies a set of criteria for matching HTTP request headers. The request heade
- Default: None
- Data type: List of maps

Each member of the `header` list is a map that contains a `name` field and at least one match criterion. The following table describes the parameters that each member of the `header` list may contain:
Each member of the `header` list is a map that contains a `name` field and at least one match criterion.

~> **Warning**: If it is possible for a header to contain multiple values, we recommend using `contains` or `regex` rather than `exact`, `prefix`, or `suffix`. Envoy internally concatenates multiple header values into a single CSV value prior to applying match rules, which may result in match rules that depend on the beginning or end of a string vulnerable to circumvention. A more robust alternative is using `contains` or, if a stricter value match is required, configuring a regex pattern that is tolerant of comma-separated values.

The following table describes the parameters that each member of the `header` list may contain:

| Parameter | Description | Data type | Required |
| --- | --- | --- | --- |
| `name` | Specifies the name of the header to match. | string | required |
| `present` | Enables a match if the header configured in the `name` field appears in the request. Consul matches on any value as long as the header key appears in the request. Do not specify `present` if `exact`, `prefix`, `suffix`, or `regex` are configured in the same `header` configuration. | boolean | optional |
| `Exact` | Specifies a value for the header key set in the `Name` field. If the request header value matches the `exact` value, Consul applies the permission. Do not specify `exact` if `present`, `prefix`, `suffix`, or `regex` are configured in the same `header` configuration. | string | optional |
| `prefix` | Specifies a prefix value for the header key set in the `name` field. If the request header value starts with the `prefix` value, Consul applies the permission. Do not specify `prefix` if `present`, `exact`, `suffix`, or `regex` are configured in the same `header` configuration. | string | optional |
| `suffix` | Specifies a suffix value for the header key set in the `name` field. If the request header value ends with the `suffix` value, Consul applies the permission. Do not specify `suffix` if `present`, `exact`, `prefix`, or `regex` are configured in the same `header` configuration. | string | optional |
| `regex` | Specifies a regular expression pattern as the value for the header key set in the `name` field. If the request header value matches the regex, Consul applies the permission. Do not specify `regex` if `present`, `exact`, `prefix`, or `suffix` are configured in the same `header` configuration. The regex syntax is proxy-specific. If using Envoy, refer to the [re2 documentation](https://github.com/google/re2/wiki/Syntax) for details. | string | optional |
| `present` | Enables a match if the header configured in the `name` field appears in the request. Consul matches on any value as long as the header key appears in the request. Do not specify `present` if `exact`, `prefix`, `suffix`, `contains`, or `regex` are configured in the same `header` configuration. | boolean | optional |
| `Exact` | Specifies a value for the header key set in the `Name` field. If the request header value matches the `exact` value, Consul applies the permission. Do not specify `exact` if `present`, `prefix`, `suffix`, `contains`, or `regex` are configured in the same `header` configuration. | string | optional |
| `prefix` | Specifies a prefix value for the header key set in the `name` field. If the request header value starts with the `prefix` value, Consul applies the permission. Do not specify `prefix` if `present`, `exact`, `suffix`, `contains`, or `regex` are configured in the same `header` configuration. | string | optional |
| `suffix` | Specifies a suffix value for the header key set in the `name` field. If the request header value ends with the `suffix` value, Consul applies the permission. Do not specify `suffix` if `present`, `exact`, `prefix`, `contains`, or `regex` are configured in the same `header` configuration. | string | optional |
| `contains` | Specifies a contains value for the header key set in the `name` field. If the request header value includes the `contains` value, Consul applies the permission. Do not specify `contains` if `present`, `exact`, `prefix`, `suffix`, or `regex` are configured in the same `header` configuration. | string | optional |
| `regex` | Specifies a regular expression pattern as the value for the header key set in the `name` field. If the request header value matches the regex, Consul applies the permission. Do not specify `regex` if `present`, `exact`, `prefix`, `suffix`, or `contains` are configured in the same `header` configuration. The regex syntax is proxy-specific. If using Envoy, refer to the [re2 documentation](https://github.com/google/re2/wiki/Syntax) for details. | string | optional |
| `ignoreCase` | Ignores the case of the provided header value when matching with exact, prefix, suffix, or contains. Default is `false`. | boolean | optional |
| `invert` | Inverts the matching logic configured in the `header`. Default is `false`. | boolean | optional |

### `spec.sources[].type`
Expand Down
6 changes: 5 additions & 1 deletion website/content/docs/connect/intentions/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,11 @@ application](/consul/docs/connect/native) enforces intentions on inbound connect

L4 intentions mediate the ability to establish new connections. Modifying an intention does not have an effect on existing connections. As a result, changing a connection from `allow` to `deny` does not sever the connection.

L7 intentions mediate the ability to issue new requests. When an intention is modified, requests received after the modification use the latest intention rules to enforce access. Changing a connection from `allow` to `deny` does not sever the connection, but doing so blocks new requests from being processed.
L7 intentions mediate the ability to issue new requests. When an intention is modified, requests received after the modification use the latest intention rules to enforce access. Changing a connection from `allow` to `deny` does not sever the connection, but doing so blocks new requests from being processed.

When using L7 intentions, we recommend that you review and update the [Mesh request normalization configuration](/consul/docs/connect/security#request-normalization-and-configured) to avoid unintended match rule circumvention. More details are available in the [Mesh configuration entry reference](/consul/docs/connect/config-entries/mesh#request-normalization).

When you use L7 intentions with header matching and it is possible for a header to contain multiple values, we recommend using `contains` or `regex` instead of `exact`, `prefix`, or `suffix`. For more information, refer to the [service intentions configuration entry reference](/consul/docs/connect/config-entries/service-intentions#spec-sources-permissions-http-header).

### Caching

Expand Down
32 changes: 32 additions & 0 deletions website/content/docs/connect/security.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,38 @@ Consul should be configured with a default deny intention policy. This forces
all service-to-service communication to be explicitly
allowed via an allow [intention](/consul/docs/connect/intentions).

One advantage of using a default deny policy in combination with specific "allow" rules
is that a failure of intentions due to misconfiguration always results in
_denied_ traffic, rather than unwanted _allowed_ traffic.

In the absence of `default_intention_policy` Consul will fall back to the ACL
default policy when determining whether to allow or deny communications without
an explicit intention.

### Request Normalization Configured for L7 Intentions

Atypical traffic patterns may interfere with the enforcement of L7 intentions. For
example, if a service makes request to a non-normalized URI path and Consul is not
configured to force path normalization, it becomes possible to circumvent path match rules. While a
default deny policy can limit the impact of this issue, we still recommend
that you review your current request normalization configuration. Normalization is critical to avoid unwanted
traffic, especially when using unrecommended security options such as a default allow intentions policy.

Consul adopts a default normalization mode that adheres to [RFC 3986](
https://tools.ietf.org/html/rfc3986#section-6), but additional options to enable stricter
normalization are available in the cluster-wide [Mesh configuration entry](
/consul/docs/connect/config-entries/mesh). We recommend reviewing these options and
enabling the strictest set that does not interfere with application traffic.

We also recommend that you review L7 intention header match rules for potential
issues with multiple header values. Refer to the [service intentions
configuration entry reference](/consul/docs/connect/config-entries/service-intentions#spec-sources-permissions-http-header)
for more information.

You do not need to enable request normalization if you are not using L7 intentions.
However, normalization may also benefit the use of other service mesh features that
rely on L7 attribute matching, such as [service routers](/consul/docs/connect/manage-traffic#routing).

### ACLs Enabled with Default Deny

Consul must be configured to use ACLs with a default deny policy. This forces
Expand All @@ -51,6 +79,10 @@ this. **If ACLs are not enabled**, deny intentions will still be enforced, but a
may edit intentions. This renders the security of the created intentions
effectively useless.

The advantage of a default deny policy in combination with specific "allow" rules
is that at worst, a failure of intentions due to misconfiguration will result in
_denied_ traffic, rather than unwanted _allowed_ traffic.

### TCP and UDP Encryption Enabled

TCP and UDP encryption must be enabled to prevent plaintext communication
Expand Down
Loading

0 comments on commit 7f5b4db

Please sign in to comment.