Skip to content

HAProxy ssl_sni_matches/host_matches frontend ACLs are stored via the API but never rendered into haproxy.cfg, breaking SNI/Host-based routing #948

Description

@antoniodargenio

Is your feature request related to existing pfSense functionality that is missing from the API? Please describe.
Yes — Services > HAProxy > Settings > Frontend > <edit> > Access Control Lists, expression types "SSL SNI matches" / "Host matches" (and presumably the sibling ssl_sni_contains/ssl_sni_starts_with/ssl_sni_ends_with/ssl_sni_regex/host_* variants, only the two above were directly tested). The REST API exposes the identical field (ha_acls[].expression on /api/v2/services/haproxy/frontend, and the dedicated /api/v2/services/haproxy/frontend/acl sub-resource) and accepts/stores these values without error — but this isn't a missing field, it's a field that's accepted and echoed back correctly by GET yet doesn't actually work.

Is your feature request related to a problem? Please describe.
I'm always frustrated when I configure a TCP-mode SNI-passthrough frontend (the standard pfSense-HAProxy recipe for routing multiple HTTPS backends off one WAN IP without terminating TLS on pfSense) entirely through the REST API, and it silently fails to actually work. Repro:

  1. POST /api/v2/services/haproxy/frontend — TCP-mode frontend with a default backend.
  2. POST /api/v2/services/haproxy/frontend/acl — {"parent_id": <id>, "name": "my_acl", "expression": "ssl_sni_matches", "value": "example.com"}.
  3. POST /api/v2/services/haproxy/frontend/action — {"parent_id": <id>, "action": "use_backend", "acl": "my_acl", "backend": "other_backend"}.
  4. POST /api/v2/services/haproxy/apply — returns 200/{"applied": false}, no error surfaced to the caller.
  5. HAProxy actually fails to reload. The only place the failure shows up is /var/log/system.log:
    [ALERT] : config : parsing [.../haproxy.cfg:N] : error detected while parsing switching rule : no such ACL : 'my_acl'.
    [ALERT] : config : Fatal errors found in configuration file
    
    The generated config contains the use_backend ... if my_acl switching line but never the acl my_acl ... declaration it depends on — so the ACL is silently dropped somewhere between stored config and rendered haproxy.cfg, only for these expression types. A source_ip-expression ACL added the identical way (same frontend, same nested/sub-resource creation paths tried both ways) rendered and reloaded correctly, so this isn't a general "ACLs aren't wired up" issue.

Describe the solution you'd like
Fix whatever translates a stored ACL's expression enum value into the corresponding acl <name> <fetch> line in haproxy.cfg so it covers ssl_sni_matches/ssl_sni_contains/ssl_sni_starts_with/ssl_sni_ends_with/ssl_sni_regex and the host_* family the same way it already correctly covers source_ip and (presumably) other working expression types.

Describe alternatives you've considered

  • expression: "custom" with the raw HAProxy fetch syntax as value (e.g. "req.ssl_sni -i example.com" for SNI, "hdr(host) -i example.com" for Host) — this renders correctly and is the workaround I'm using, but it means the friendlier typed expression options are unusable for the single most common HAProxy-on-pfSense use case (SNI/Host-based backend routing).

Additional context

  • Versions: pfSense 2.9.0-RELEASE (FreeBSD 16.0-CURRENT), pfSense-pkg-RESTAPI 2.10_2, pfSense-pkg-haproxy 0.65.7 (haproxy 3.2.19).
  • Reproduced on a stock install with only pfSense-pkg-haproxy installed via POST /api/v2/system/package {"name": "pfSense-pkg-haproxy"}, no other HAProxy config present beforehand.
  • Tried both ACL-creation paths (inline ha_acls/a_actionitems arrays in the frontend's own POST/PATCH body, and the dedicated /frontend/acl + /frontend/action sub-resource endpoints) — both produce the same broken render, and GET on the frontend correctly echoes the ACL back with the right parent_id either way, confirming it's stored correctly and the bug is specifically in the config-file renderer.
  • Happy to provide full request/response payloads and system.log excerpts on request.

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