Skip to content

Make Envoy's normalize_path and path_with_escaped_slashes_action configurable #7694

Description

@agentdanabol

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:

  1. 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.

  2. 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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions