Skip to content

OCPBUGS-87205: Strip X-SSL-Client-* headers for plain HTTP#787

Open
bentito wants to merge 5 commits into
openshift:masterfrom
bentito:OCPBUGS-86718
Open

OCPBUGS-87205: Strip X-SSL-Client-* headers for plain HTTP#787
bentito wants to merge 5 commits into
openshift:masterfrom
bentito:OCPBUGS-86718

Conversation

@bentito

@bentito bentito commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

This prevents unauthenticated spoofing of mutual TLS client identities by deleting X-SSL-Client headers in the fe_http frontend before they can reach the backend.

It introduces a new environment variable ROUTER_MUTUAL_TLS_AUTH_FILTER
which defaults to true. Setting this to false allows external load balancers
to inject these headers.

Joint effort of the author and gemini-pro-3.1-preview

Summary by CodeRabbit

  • Bug Fixes
    • Strips mutual-TLS client identity headers from requests forwarded over plain HTTP.
    • Applies this header filtering consistently across edge-terminated, SNI, and non-TLS request flows.
    • Adds an environment toggle to enable/disable the header filtering (enabled by default).

@openshift-ci-robot openshift-ci-robot added jira/severity-important Referenced Jira bug's severity is important for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jun 4, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@bentito: This pull request references Jira Issue OCPBUGS-86718, which is invalid:

  • expected the vulnerability to target either version "5.0." or "openshift-5.0.", but it targets "4.21.z" instead

Comment /jira refresh to re-evaluate validity if changes to the Jira bug are made, or edit the title of this pull request to link to a different bug.

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

This prevents unauthenticated spoofing of mutual TLS client identities by deleting X-SSL-Client headers in the fe_http frontend before they can reach the backend.

It introduces a new environment variable ROUTER_MUTUAL_TLS_AUTH_FILTER
which defaults to true. Setting this to false allows external load balancers
to inject these headers.

Joint effort of the author and gemini-pro-3.1-preview

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 72e88061-68dc-46f3-9e8d-586afc4cc890

📥 Commits

Reviewing files that changed from the base of the PR and between cfb360d and d180c82.

📒 Files selected for processing (1)
  • images/router/haproxy/conf/haproxy-config.template
🚧 Files skipped from review as they are similar to previous changes (1)
  • images/router/haproxy/conf/haproxy-config.template

Walkthrough

The HAProxy config template now conditionally strips X-SSL and X-SSL-Client-* headers from forwarded plain-HTTP requests in three frontends (public, fe_sni, fe_no_sni) using a ROUTER_MUTUAL_TLS_HEADER_FILTER guard (default: true).

Changes

mTLS Header Spoofing Prevention

Layer / File(s) Summary
Strip mTLS headers from non-TLS frontends
images/router/haproxy/conf/haproxy-config.template
Adds conditional header-deletion rules in frontend public, frontend fe_sni, and frontend fe_no_sni to remove X-SSL and X-SSL-Client-* headers before forwarding, controlled by ROUTER_MUTUAL_TLS_HEADER_FILTER (default true).

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 15
✅ Passed checks (15 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title specifically references stripping X-SSL-Client-* headers for plain HTTP, which directly aligns with the main change—conditionally removing mutual-TLS identity headers from HAProxy frontends via the ROUTER_MUTUAL_TLS_HEADER_FILTER variable.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Stable And Deterministic Test Names ✅ Passed PR contains no Ginkgo tests. Changes are limited to haproxy configuration template and standard Go test files using func Test* patterns, which are not subject to this Ginkgo-specific check.
Test Structure And Quality ✅ Passed The PR only modifies haproxy-config.template, not test code. The custom check for Ginkgo test structure/quality is not applicable as the PR contains zero test file changes.
Microshift Test Compatibility ✅ Passed No new Ginkgo e2e tests are added in this PR. The repository uses standard Go testing framework, and this PR only modifies HAProxy template configuration files.
Single Node Openshift (Sno) Test Compatibility ✅ Passed No new Ginkgo e2e tests are added in this PR. Changes are limited to HAProxy config template modifications (+53/-0). SNO test compatibility check is not applicable.
Topology-Aware Scheduling Compatibility ✅ Passed PR modifies only HAProxy configuration template, not deployment manifests, operator code, or controllers. Custom check is inapplicable.
Ote Binary Stdout Contract ✅ Passed PR changes only the HAProxy configuration template file, not Go code. No process-level stdout writes were introduced that could corrupt OTE binary JSON communication.
Ipv6 And Disconnected Network Test Compatibility ✅ Passed No Ginkgo e2e tests were added in this PR. All 32 new test files use standard Go testing with TestXxx(t *testing.T) functions, not Ginkgo patterns (It, Describe, Context, When).
No-Weak-Crypto ✅ Passed PR changes are HAProxy config template header deletion rules to prevent mTLS spoofing. No weak crypto algorithms, custom implementations, or non-constant-time comparisons are introduced.
Container-Privileges ✅ Passed PR modifies HAProxy template for TLS header filtering. No new privileged settings introduced; hostNetwork:true is pre-existing and justified.
No-Sensitive-Data-In-Logs ✅ Passed PR deletes X-SSL-Client headers to prevent spoofing; no logging of sensitive data introduced. Header deletion only, no log statements or capture directives added.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@openshift-ci openshift-ci Bot requested review from candita and frobware June 4, 2026 16:57

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@images/router/haproxy/conf/haproxy-config.template`:
- Around line 276-285: The template is conflating ROUTER_MUTUAL_TLS_AUTH_FILTER
(used as a DN regex in frontends fe_sni and fe_no_sni) with a boolean
header-strip switch; create a new boolean env var
ROUTER_MUTUAL_TLS_STRIP_HEADERS (or similar) and use it in the header-stripping
block (replace isTrue (env "ROUTER_MUTUAL_TLS_AUTH_FILTER" "true") with isTrue
(env "ROUTER_MUTUAL_TLS_STRIP_HEADERS" "true")), leave
ROUTER_MUTUAL_TLS_AUTH_FILTER exclusively as the DN regex in frontends fe_sni
and fe_no_sni, and add backward-compatibility logic: if
ROUTER_MUTUAL_TLS_STRIP_HEADERS is unset, treat stripping as enabled only when
ROUTER_MUTUAL_TLS_AUTH_FILTER exactly equals "true" (case-insensitive) so
existing boolean deployments keep behavior but regex values won’t mistakenly
enable/disable stripping; apply the same rename/compatibility change in the
other two header-strip blocks mentioned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b4ee8516-a792-44bc-8a83-55453bc0488d

📥 Commits

Reviewing files that changed from the base of the PR and between 6761134 and f2e92ec.

📒 Files selected for processing (1)
  • images/router/haproxy/conf/haproxy-config.template

Comment thread images/router/haproxy/conf/haproxy-config.template Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
images/router/haproxy/conf/haproxy-config.template (1)

278-285: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Critical: mTLS identity headers are not fully stripped on plain HTTP, enabling spoofing

With ROUTER_MUTUAL_TLS_HEADER_FILTER=true, the plain-HTTP strip blocks in images/router/haproxy/conf/haproxy-config.template (frontend public lines 278-285, plus the same pattern in the fe_sni and fe_no_sni blocks) delete only X-SSL-Client-{DN,DER,NotAfter,NotBefore,SHA1,Subject}. They do not delete X-SSL, X-SSL-Client-{Verify,Serial,Version,CN}, or X-SSL-Issuer, even though these are populated from TLS state in fe_sni/fe_no_sni. Since plain HTTP never sets them, attacker-supplied values can reach the backend and may bypass mTLS auth/identity checks if the backend trusts these headers.

🔒 Proposed fix: strip all mTLS identity headers
  {{- if isTrue (env "ROUTER_MUTUAL_TLS_HEADER_FILTER" "true") }}
+ http-request del-header X-SSL
+ http-request del-header X-SSL-Client-CN
  http-request del-header X-SSL-Client-DN
  http-request del-header X-SSL-Client-DER
+ http-request del-header X-SSL-Client-Serial
  http-request del-header X-SSL-Client-NotAfter
  http-request del-header X-SSL-Client-NotBefore
  http-request del-header X-SSL-Client-SHA1
  http-request del-header X-SSL-Client-Subject
+ http-request del-header X-SSL-Client-Verify
+ http-request del-header X-SSL-Client-Version
+ http-request del-header X-SSL-Issuer
  {{- end }}

Apply the same deletion additions to the corresponding strip blocks in fe_sni (lines ~406-413) and fe_no_sni (lines ~532-539).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@images/router/haproxy/conf/haproxy-config.template` around lines 278 - 285,
The plain-HTTP header-strip blocks gated by ROUTER_MUTUAL_TLS_HEADER_FILTER
currently only remove X-SSL-Client-{DN,DER,NotAfter,NotBefore,SHA1,Subject},
leaving other mTLS-derived headers that can be spoofed; update the strip
sections in the frontend public block and the fe_sni and fe_no_sni blocks to
also remove X-SSL, X-SSL-Issuer and all X-SSL-Client-{Verify,Serial,Version,CN}
headers so no mTLS identity headers reach backends over plain HTTP when the
ROUTER_MUTUAL_TLS_HEADER_FILTER is enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@images/router/haproxy/conf/haproxy-config.template`:
- Around line 278-285: The plain-HTTP header-strip blocks gated by
ROUTER_MUTUAL_TLS_HEADER_FILTER currently only remove
X-SSL-Client-{DN,DER,NotAfter,NotBefore,SHA1,Subject}, leaving other
mTLS-derived headers that can be spoofed; update the strip sections in the
frontend public block and the fe_sni and fe_no_sni blocks to also remove X-SSL,
X-SSL-Issuer and all X-SSL-Client-{Verify,Serial,Version,CN} headers so no mTLS
identity headers reach backends over plain HTTP when the
ROUTER_MUTUAL_TLS_HEADER_FILTER is enabled.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: openshift/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: d99a5d53-4fe2-4fbf-90d0-002924f87918

📥 Commits

Reviewing files that changed from the base of the PR and between f2e92ec and bc50db1.

📒 Files selected for processing (1)
  • images/router/haproxy/conf/haproxy-config.template

@bentito

bentito commented Jun 4, 2026

Copy link
Copy Markdown
Contributor Author

@Miciah in #787 (review) coderabbit seems to be correct in having found more mTLS header with the same problem, like X-SSL, X-SSL-Client-Verify, X-SSL-Client-Serial, X-SSL-Client-Version, X-SSL-Client-CN, and X-SSL-Issuer

what do you think, fix these in this PR too?

@Miciah

Miciah commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

coderabbit seems to be correct in having found more mTLS header with the same problem, like X-SSL, X-SSL-Client-Verify, X-SSL-Client-Serial, X-SSL-Client-Version, X-SSL-Client-CN, and X-SSL-Issuer

what do you think, fix these in this PR too?

Yes, we should delete them all.


# Strip off X-SSL-Client-* headers for plain HTTP if not explicitly disabled.
# This prevents unauthenticated spoofing of mutual TLS client identities.
{{- if isTrue (env "ROUTER_MUTUAL_TLS_AUTH_FILTER" "true") }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you planning to add an API option and logic in cluster-ingress-operator to set this environment variable, or are you adding the environment variable just in case we later need to add an API field or unsupported override?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idea was in case a user has got a need for this, there will be a way. Is that sufficient for now?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the flag will also need to come with a documentation that says "if you enable this, be sure you add a network policy on your router to trust HTTP traffic just from your loadbalancer"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rikatz would you want to create a DOCS tracker story please

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread images/router/haproxy/conf/haproxy-config.template Outdated
@bentito

bentito commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

/retest

@bentito bentito changed the title OCPBUGS-86718: Strip X-SSL-Client-* headers for plain HTTP OCPBUGS-87205: Strip X-SSL-Client-* headers for plain HTTP Jun 5, 2026
@bentito

bentito commented Jun 5, 2026

Copy link
Copy Markdown
Contributor Author

/jira refresh

1 similar comment
@MrSanketkumar

Copy link
Copy Markdown

/jira refresh

@openshift-ci-robot openshift-ci-robot added jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. and removed jira/invalid-bug Indicates that a referenced Jira bug is invalid for the branch this PR is targeting. labels Jun 8, 2026
@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@MrSanketkumar: This pull request references Jira Issue OCPBUGS-87205, which is valid. The bug has been moved to the POST state.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state ASSIGNED, which is one of the valid states (NEW, ASSIGNED, POST)

The bug has been updated to refer to the pull request using the external bug tracker.

Details

In response to this:

/jira refresh

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

bentito and others added 4 commits June 8, 2026 11:47
This prevents unauthenticated spoofing of mutual TLS client identities by deleting X-SSL-Client headers in the fe_http frontend before they can reach the backend.

It introduces a new environment variable ROUTER_MUTUAL_TLS_AUTH_FILTER
which defaults to true. Setting this to false allows external load balancers
to inject these headers.
This avoids a collision with the existing ROUTER_MUTUAL_TLS_AUTH_FILTER regex.
Expands the list of mTLS headers stripped from plain HTTP requests to include all headers set natively by the router (X-SSL, X-SSL-Client-Verify, X-SSL-Client-Serial, X-SSL-Client-Version, X-SSL-Client-CN, X-SSL-Issuer). This fully mitigates the risk of spoofing any of these headers.
Co-authored-by: Miciah Dashiel Butler Masters <mmasters@redhat.com>

# Strip off X-SSL-Client-* headers for plain HTTP if not explicitly disabled.
# This prevents unauthenticated spoofing of mutual TLS client identities.
{{- if isTrue (env "ROUTER_MUTUAL_TLS_HEADER_FILTER" "true") }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation of these Go template stanzas is inconsistent with surrounding Go template stanzas, but the template already has similar inconsistencies elsewhere; we can fix them all up in a follow-up.

http-request del-header X-SSL-Client-NotBefore
http-request del-header X-SSL-Client-SHA1
http-request del-header X-SSL-Client-Serial
http-request del-header X-SSL-Client-Subject

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is X-SSL-Client-Subject added? Why does it need to be deleted?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While we don't set them on router, I think it is a valid deletion. This header can also be used for authentication, in case some day we decide to add it to router passing to backend by a customer request we can run on a problem of forgetting to remove it

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems arbitrary. Why not also add http-request del-header X-SSL-Client-Chain-DER or http-request del-header X-SSL-Client-Used or other arbitrary things that an application could theoretically use or HAProxy could theoretically set?

# Strip off Proxy headers to prevent HTTpoxy (https://httpoxy.org/)
http-request del-header Proxy

# Strip off X-SSL-Client-* headers for plain HTTP if not explicitly disabled.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the latest changes, X-SSL-Client-* should be changed to X-SSL* here and below.

Suggested change
# Strip off X-SSL-Client-* headers for plain HTTP if not explicitly disabled.
# Strip off X-SSL* headers for plain HTTP if not explicitly disabled.

Comment on lines +543 to +544
# Strip off X-SSL-Client-* headers for plain HTTP if not explicitly disabled.
# This prevents unauthenticated spoofing of mutual TLS client identities.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment in fe_no_sni should match the one in fe_sni, not the one in public.

@openshift-ci-robot

Copy link
Copy Markdown
Contributor

@bentito: This pull request references Jira Issue OCPBUGS-87205, which is valid.

3 validation(s) were run on this bug
  • bug is open, matching expected state (open)
  • bug target version (5.0.0) matches configured target version for branch (5.0.0)
  • bug is in the state POST, which is one of the valid states (NEW, ASSIGNED, POST)
Details

In response to this:

This prevents unauthenticated spoofing of mutual TLS client identities by deleting X-SSL-Client headers in the fe_http frontend before they can reach the backend.

It introduces a new environment variable ROUTER_MUTUAL_TLS_AUTH_FILTER
which defaults to true. Setting this to false allows external load balancers
to inject these headers.

Joint effort of the author and gemini-pro-3.1-preview

Summary by CodeRabbit

  • Bug Fixes
  • Strips mutual-TLS client identity headers from requests forwarded over plain HTTP.
  • Applies this header filtering consistently across edge-terminated, SNI, and non-TLS request flows.
  • Adds an environment toggle to enable/disable the header filtering (enabled by default).

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci

openshift-ci Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

@bentito: all tests passed!

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

@Miciah

Miciah commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Thanks!

/approve
/lgtm

By the way, do you want to squash the commits?

@openshift-ci openshift-ci Bot added the lgtm Indicates that a PR is ready to be merged. label Jun 9, 2026
@openshift-ci

openshift-ci Bot commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Miciah

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci openshift-ci Bot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Jun 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. jira/severity-important Referenced Jira bug's severity is important for the branch this PR is targeting. jira/valid-bug Indicates that a referenced Jira bug is valid for the branch this PR is targeting. jira/valid-reference Indicates that this PR references a valid Jira ticket of any type. lgtm Indicates that a PR is ready to be merged.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants