Skip to content

HTTPS frontend name _front_https__local breaks backend pathID matching when any ingress uses ssl-passthrough #1432

@abh

Description

@abh

Thank you for all the work on haproxy-ingress. We've been running it in production for years and it's been great. We hit an issue after upgrading from v0.15 to v0.17.0-alpha.1 that took a while to pin down.

Description of the problem

When any ingress in the cluster has ssl-passthrough enabled, the HTTPS frontend is named _front_https__local instead of _front_https. This is expected -- the TLS frontend does SNI inspection and passes through to the local HTTPS frontend.

But the generated backend configs still check for _front_https when setting txn.pathID:

http-request set-var-fmt(req.fe) "%f"
http-request set-var(txn.pathID) var(req.base),map_dir(...) if { var(req.fe) -m str _front_https }

Since %f returns _front_https__local (the real frontend name), the -m str _front_https exact match never succeeds. txn.pathID is never set, and every conditional rule that depends on it is silently skipped.

This affects all HTTPS backends in the cluster, not just the one with ssl-passthrough. Features like rewrite-target and per-path HSTS stop working for every ingress.

Expected behavior

The backend pathID condition should match the actual frontend name. Either the condition should check for _front_https__local when ssl-passthrough is active, or the %f variable should resolve to whatever name the backend expects.

With the pathID set correctly, rewrite-target and other path-dependent features should work as they did in v0.15.

Steps to reproduce the problem

  1. Have at least one ingress with ingress.kubernetes.io/ssl-passthrough: "true" (this triggers the __local suffix on the HTTPS frontend name)
  2. Have a separate, unrelated ingress with haproxy-ingress.github.io/rewrite-target set
  3. Send an HTTPS request that should be rewritten
  4. The request arrives at the backend with the original path -- the replace-path rule in the generated config never fires because txn.pathID is empty

In our case, requests to www.ntppool.org/api/data/dns/counts should be rewritten to /api/dns/counts before reaching the backend. Instead they arrive as /api/data/dns/counts and the backend returns 404.

Environment information

HAProxy Ingress version: v0.17.0-alpha.1

Command-line options:

--default-backend-service=$(POD_NAMESPACE)/default-http-backend
--default-ssl-certificate=$(POD_NAMESPACE)/ewrlb-tls
--configmap=$(POD_NAMESPACE)/haproxy-ingress
--tcp-services-configmap=$(POD_NAMESPACE)/haproxy-tcp
--watch-gateway
--publish-service=haproxy-ingress/haproxy-ingress

Global options:

access-log: "true"
backend-server-naming: pod
forwardfor: update
max-connections: "4000"
slots-min-free: "2"
timeout-client: 110s

Ingress objects:

The ssl-passthrough ingress (triggers the __local frontend rename):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    ingress.kubernetes.io/ssl-passthrough: "true"
  name: monitor-api
  namespace: ntppool
spec:
  ingressClassName: haproxy
  rules:
  - host: api.mon.ntppool.dev
    http:
      paths:
      - backend:
          service:
            name: monitor-api
            port:
              number: 443
        path: /
        pathType: Prefix

The rewrite-target ingress (broken by the frontend name mismatch):

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    haproxy-ingress.github.io/rewrite-target: /api
  name: ntppool-data-api
  namespace: ntppool
spec:
  ingressClassName: haproxy
  rules:
  - host: www.ntppool.org
    http:
      paths:
      - backend:
          service:
            name: data-api
            port:
              number: 80
        path: /api/data
        pathType: Prefix
  tls:
  - hosts:
    - www.ntppool.org
    secretName: ntppool-data-api-tls

The generated haproxy.cfg for the data-api backend shows the mismatch:

frontend _front_https__local
    ...

backend ntppool_data-api_8030
    ...
    http-request set-var-fmt(req.fe) "%f"
    # req.fe is now "_front_https__local"

    # This never matches because of the __local suffix:
    http-request set-var(txn.pathID) var(req.base),map_dir(...) if { var(req.fe) -m str _front_https }

    # So pathID is empty, and this replace-path is skipped:
    http-request replace-path '^/api/data(.*)$' '/api\1' if { var(txn.pathID) -m str path12 }

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions