Set Up HAProxy for TLS Passthrough with SNI Routing on OPNsense

In this guide, we’ll configure HAProxy on OPNsense to support TLS passthrough with SNI-based routing. This allows secure HTTPS traffic to be routed based on domain names without decrypting the traffic on the firewall.

  1. Install HAProxy Plugin
  • Go to System > Firmware > Plugins
  • Search for: os-haproxy
  • Click Install
  • After installation, go to Services > HAProxy (you may need to reboot)
  1. Enable HAProxy
  • Go to Services > HAProxy > Settings > Service
  • Check Enable HAProxy
  • Click Apply
  1. Add Real Servers
  • Go to Services > HAProxy > Settings > Real Servers
  • Click Add
Nameappk8s-controller
Typestaticstatic
FQDN or IP10.0.128.510.0.128.240
Port443443
SSLUncheckedUnchecked
Verify SSL CertificateUncheckedUnchecked
  • Click Save
  • Click Apply
  1. Create Backend Pools
  • Go to Services > HAProxy > Settings > Virtual Services > Backend Pools
  • Click Add
Nameappk8s-controller
ModeTCP (Layer 4)TCP (Layer 4)
Serversappk8s-controller
Enable Health CheckingUncheckedUnchecked
  • Click Save
  • Click Apply
  1. Create TCP Frontend with SNI Routing
  • Go to Services > HAProxy > Settings > Virtual Services > Public Services
  • Click Add
    Name: frontend-https
    Listen Address: 0.0.0.0:443
    Type: SSL/HTTPS(TCP mode)
    Default Backend Pool: None
  • Option pass-through
    Enable advanced mode and scroll down to Advanced settings > Option pass-through
    Paste this:
# Required to extract SNI from TLS handshake in TCP mode
tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
# ACL: is_app_tls
acl is_app_tls req.ssl_sni -i maksonlee.com
acl is_app_tls req.ssl_sni -i www.maksonlee.com
acl is_app_tls req.ssl_sni -i zabbix.maksonlee.com
acl is_app_tls req.ssl_sni -i jenkins-test.maksonlee.com
# ACL: is_k8s_tls
acl is_k8s_tls req.ssl_sni -i whoami.maksonlee.com

# ACTION: app
use_backend app if is_app_tls
# ACTION: k8s
use_backend k8s-controller if is_k8s_tls

This enables SNI-based routing in the correct order, which the UI doesn’t handle automatically.

  • Click Save
    Click Apply

Done, now it’s time to test.

HTTP Pass-through (Optional)

If you want to forward port 80 traffic as plain HTTP (not TLS passthrough), here is the configuration:

# Frontend: frontend-http ()
frontend frontend-http
    bind 0.0.0.0:80 name 0.0.0.0:80 
    mode http
    option http-keep-alive

    # logging options
    # WARNING: pass through options below this line
    # ACL: is_app_http
    acl is_app_http hdr(host) -i maksonlee.com
    acl is_app_http hdr(host) -i www.maksonlee.com
    acl is_app_http hdr(host) -i zabbix.maksonlee.com
    acl is_app_http hdr(host) -i jenkins-test.maksonlee.com
    # ACL: is_k8s_http
    acl is_k8s_http hdr(host) -i whoami.maksonlee.com
    
    # ACTION: app
    use_backend app-http if is_app_http
    # ACTION: k8s
    use_backend k8s-controller-http if is_k8s_http

# Backend: app-http ()
backend app-http
    # health checking is DISABLED
    mode http
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m  
    stick on src
    http-reuse safe
    server app 10.0.128.5 

# Backend: k8s-controller-http ()
backend k8s-controller-http
    # health checking is DISABLED
    mode http
    balance source
    # stickiness
    stick-table type ip size 50k expire 30m  
    stick on src
    http-reuse safe
    server k8s-controller 10.0.128.240 

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top