Please describe the problem you have
Contour hardcodes HttpConnectionManager.normalize_path: true and never sets
path_with_escaped_slashes_action, leaving it at Envoy's implementation-specific
default (KEEP_UNCHANGED). Neither is exposed in ContourConfiguration, so
operators cannot change either one.
That leaves two gaps:
-
normalize_path cannot be turned off. RFC 3986 path normalization is the
right default, but some backends treat the request path as an opaque
identifier rather than a hierarchical path: S3-compatible / object storage
gateways, artifact registries, and APIs whose request signature covers the
raw path. For those, collapsing ./.. segments before proxying makes the
request unroutable or breaks signature verification, and there is no escape
hatch short of forking Contour.
-
path_with_escaped_slashes_action cannot be turned on. Encoded slashes
(%2F, %5C) are forwarded unchanged today. That is a legitimate choice for
some backends, but it is also a well-known authorization-bypass vector: a
route or external-authz policy that matches on a path prefix can be evaded by
encoding the separator. Operators who want Envoy to reject such requests, or
to unescape and re-route them, currently cannot express that in Contour at
all — even though Envoy has supported the knob for years and other proxies
expose it.
Both fields are per-HttpConnectionManager settings that Contour already builds,
so exposing them is mostly plumbing.
Please describe the solution you'd like
Add two fields to ContourConfiguration.spec.envoy.listener, following the
disableMergeSlashes precedent from #4339 (which sits next to them in the same
struct and solves the same class of problem):
envoy:
listener:
# existing
disableMergeSlashes: false
# proposed
disableNormalizePath: false
pathWithEscapedSlashesAction: keep_unchanged
disableNormalizePath (*bool, Contour default false) — when true, sets
normalize_path: false on the HTTP connection manager. Naming and doc comment
mirror disableMergeSlashes / disableAllowChunkedLength, so the default
preserves today's behavior and the field reads as an opt-out.
pathWithEscapedSlashesAction (string enum, Contour default
keep_unchanged) — maps 1:1 onto Envoy's PathWithEscapedSlashesAction:
keep_unchanged, reject_request, unescape_and_redirect,
unescape_and_forward. Invalid values rejected during config validation, the
same way serverHeaderTransformation is handled.
Scope notes:
- Global listener-level only, matching where
disableMergeSlashes lives. No
per-HTTPProxy / per-HTTPRoute scope is proposed here — path transformations
happen before routing, so a per-route knob would be misleading.
- Defaults reproduce current behavior exactly; this is a no-op for existing
installations.
- The stats/metrics listener also hardcodes
normalize_path: true; that should
stay as is and not follow the user-facing setting.
- Docs should describe how the three path-transformation options
(merge_slashes, normalize_path, path_with_escaped_slashes_action)
interact with route matching, and explicitly warn that disabling
normalize_path can expose backends to path-traversal-style matching bypasses.
- If the deprecated ConfigMap-based configuration is still receiving new fields,
add the equivalent keys there too; otherwise ContourConfiguration only.
Please describe alternatives you've considered
- Envoy runtime keys (
http_connection_manager.normalize_path,
http_connection_manager.path_with_escaped_slashes_action) — Contour sets
normalize_path explicitly, which overrides the runtime value, so this does
not work for case 1 at all. For case 2 it means managing Envoy runtime layers
out of band, which is not something Contour's bootstrap exposes.
- Patching the generated xDS / bootstrap — not available to users of the
Gateway provisioner or of managed Contour distributions, and fragile across
upgrades.
- Doing nothing — users hitting either case today have to fork Contour or
drop the affected workloads behind a second proxy.
Additional context
Please describe the problem you have
Contour hardcodes
HttpConnectionManager.normalize_path: trueand never setspath_with_escaped_slashes_action, leaving it at Envoy's implementation-specificdefault (
KEEP_UNCHANGED). Neither is exposed inContourConfiguration, sooperators cannot change either one.
That leaves two gaps:
normalize_pathcannot be turned off. RFC 3986 path normalization is theright default, but some backends treat the request path as an opaque
identifier rather than a hierarchical path: S3-compatible / object storage
gateways, artifact registries, and APIs whose request signature covers the
raw path. For those, collapsing
./..segments before proxying makes therequest unroutable or breaks signature verification, and there is no escape
hatch short of forking Contour.
path_with_escaped_slashes_actioncannot be turned on. Encoded slashes(
%2F,%5C) are forwarded unchanged today. That is a legitimate choice forsome backends, but it is also a well-known authorization-bypass vector: a
route or external-authz policy that matches on a path prefix can be evaded by
encoding the separator. Operators who want Envoy to reject such requests, or
to unescape and re-route them, currently cannot express that in Contour at
all — even though Envoy has supported the knob for years and other proxies
expose it.
Both fields are per-
HttpConnectionManagersettings that Contour already builds,so exposing them is mostly plumbing.
Please describe the solution you'd like
Add two fields to
ContourConfiguration.spec.envoy.listener, following thedisableMergeSlashesprecedent from #4339 (which sits next to them in the samestruct and solves the same class of problem):
disableNormalizePath(*bool, Contour defaultfalse) — whentrue, setsnormalize_path: falseon the HTTP connection manager. Naming and doc commentmirror
disableMergeSlashes/disableAllowChunkedLength, so the defaultpreserves today's behavior and the field reads as an opt-out.
pathWithEscapedSlashesAction(string enum, Contour defaultkeep_unchanged) — maps 1:1 onto Envoy'sPathWithEscapedSlashesAction:keep_unchanged,reject_request,unescape_and_redirect,unescape_and_forward. Invalid values rejected during config validation, thesame way
serverHeaderTransformationis handled.Scope notes:
disableMergeSlasheslives. Noper-HTTPProxy / per-HTTPRoute scope is proposed here — path transformations
happen before routing, so a per-route knob would be misleading.
installations.
normalize_path: true; that shouldstay as is and not follow the user-facing setting.
(
merge_slashes,normalize_path,path_with_escaped_slashes_action)interact with route matching, and explicitly warn that disabling
normalize_pathcan expose backends to path-traversal-style matching bypasses.add the equivalent keys there too; otherwise
ContourConfigurationonly.Please describe alternatives you've considered
http_connection_manager.normalize_path,http_connection_manager.path_with_escaped_slashes_action) — Contour setsnormalize_pathexplicitly, which overrides the runtime value, so this doesnot work for case 1 at all. For case 2 it means managing Envoy runtime layers
out of band, which is not something Contour's bootstrap exposes.
Gateway provisioner or of managed Contour distributions, and fragile across
upgrades.
drop the affected workloads behind a second proxy.
Additional context
normalize_path: trueis hardcoded ininternal/envoy/v3/listener.go(
httpConnectionManagerBuilder.Get()) and ininternal/envoy/v3/stats.go.path_with_escaped_slashes_actiondoes not appear anywhere in the repo, so theeffective behavior is Envoy's default of
KEEP_UNCHANGED.https://www.envoyproxy.io/docs/envoy/latest/api-v3/extensions/filters/network/http_connection_manager/v3/http_connection_manager.proto#envoy-v3-api-enum-extensions-filters-network-http-connection-manager-v3-httpconnectionmanager-pathwithescapedslashesaction
disableMergeSlashes), which established the pattern thisproposal follows.
main(go-control-plane/envoy v1.39.0).question is naming:
disableNormalizePath(consistent with the neighboringfields) vs. a positive
normalizePath *bool.