HAProxy That Refuses TLS 1.0

HAProxy terminating TLS 1.2 and 1.3 only with a modern cipher policy, HTTP redirected to HTTPS, HSTS on every response including HAProxy's own error pages (http-after-response, which the live test proved http-response does not cover), a self-signed certificate until yours arrives, stats kept local. Original role, live-tested on Rocky Linux 10.

ansibleWeb Servers

Verification

Live-tested

Really deployed to a container sandbox, proven idempotent (a second run changes nothing), verified against the role’s assertions, then torn down.

Conformance

  • Static validation (yamllint · ansible-lint)

Provenance

  • SHA-256 checksum
  • Signature (pending)

Functional

  • Live-tested - applied, verified, destroyed

Last verified 2026-09-19 · podman 4.9.3 · ansible 2.21.4 · how we verify

Documentation

haproxy-tls

HAProxy as a TLS-terminating reverse proxy: TLS 1.2 and 1.3 only with a modern cipher policy, HTTP redirected to HTTPS, HSTS and the usual response headers, and a self-signed certificate generated on first apply so the listener is encrypted before a real certificate arrives. Original role for EL 9 and 10, live-tested with podman on Rocky Linux 10.

bind :443 ssl on its own accepts TLS 1.0. HAProxy inherits whatever OpenSSL allows unless ssl-default-bind-options and the cipher lists say otherwise, and the package's sample configuration sets none of them. This role writes ssl-min-ver TLSv1.2, the Mozilla intermediate cipher list and the TLS 1.3 suites into global, so every ssl bind line in the file - including ones you add under conf.d/ - inherits the policy. The verify step offers a TLS 1.1 handshake and asserts it is refused, then negotiates TLS 1.3.

The headers are on every response, including HAProxy's own. The role sets HSTS and the other response headers with http-after-response, not http-response: the latter never runs on the 502 and 503 pages HAProxy generates when a backend is down, which the live test showed by getting a 503 with no HSTS header at all. A browser that sees the error page without HSTS has no reason to refuse plain HTTP next time.

No backend, no guessing. haproxy_tls_backend_servers is empty by default and HAProxy answers 503 until you name one, which is also how the live test proves TLS termination without an application behind it. Each entry is a plain HAProxy server line minus the keyword (web1 10.0.1.10:8080 weight 2), and check is appended unless you wrote it.

The certificate is yours. The role generates a self-signed PEM only where haproxy_tls_pem_path does not exist. Put a real key-and-chain PEM there and it is never touched again.

Stats stay local. The admin socket is root-only in /var/lib/haproxy, and the stats page is off; on, it binds to loopback.

License

Commercial - IaC Bazaar EULA. (c) IaC Bazaar.

Usage code & full reference need an account

The complete copy-paste usage, the full input/output reference, and operational notes are free with an account - shown here and bundled in the download. Sign in and this section fills in.

  • Variables
  • Test