Shared Frontends - how to do SSL of loading and SNI forwarding

Hi,
I’m using haproxy through PfSense and as I’m not able to have my conf working, I was wondering if what I need is possible or not, hence my question here.

here is a recap of my need :

I have 1 single public IP address,
I need the following at the same time :

I have a domain , smalldragoon.com , where

A1 - A.smalldragoon.com, B.smalldragoon.com, C.smalldragoon.com need to be forwarded to an internal which is managing the SSL connection ( equivalent to port forwarding of the 443 as ex )
A2 - D.smalldragoon.com need to have its SSL communication terminated on PFSense and redirected to an internal host which is running on port 80 ( so not in https , it is a basic website).
Ex : https://D.smalldragoon.com redirect to http://192.168.1.1:80

So maybe first question : is this possible ?
I get some warnings with telling me I need to use shared frontends but everything I have done so far did not work.
Of course, each config alone works perflectly

Thanks for your insights !

well…up ? just at least to know if iti is posisble or not ?

It’s possible, you need a TCP frontend that SNI routes the traffic as necessary.

The SSL session that you want to terminate you router to SSL terminating frontend on another port of the same haproxy instance, so you are able to have both.

This all works only if the certificates do not overlap.

Hi @lukastribus , thanks for your answer.
let me rephrase to be sure I understand correctly :
I have 2 domain A and B
I want to terminate B and fw A as is to internal
So based on SNI, single frontend, I
for A domain, FW to my internal host
for B : redirect to port 12345
then create another frontend on port 12345 , which will do SSL termination
am I correct stating that ?
Thanks

Exactly.

Thanks a lot !
I will play in the next couple of days !
Thanks

Hi,
As I still can’t get it working , I decided to proceed step by step.
1 - re-started from a blank complete config.
2 - created a front end with SNI on port 443, with each Server Name Indication TLS extension matches X1.smalldragoon.com to an action ( X1 to x1, X2 to x2 …).
Action beiing : x1 - > use backend “general”; General is a backend with forward to ip + port and has his own certificate public working .
3 - apply config , it works ( if I try to reach X1.smalldragoon.com" )
4 - Now, I Change this frontend to use port 445
5 - I create a backend " Forwarder-to-smalldragoon.com", where action is " redirected to frontend “my main frontend which now listeing on port 445 )
6 - create a new front end, using port 443, with a rule " Server Name Indication TLS extension contains: smalldragoon.com”, forward to backend “Forwarder-to-smalldragoon.com port 445”
it just do not do anything …
Am doing right ?missing a step or even maybe it is where I’m wrong since the begining … is it the proper method ?
Thanks
[ EDIT] I forgot to say that of course, if I try to reach site on port 445 ( https://X1.smalldragoon.com:445 , it works )

Post the full configuration, otherwise it’s difficult to get a complete picture of the situation.

sure, here we go then


global
        maxconn                 1500
        log                     /var/run/log    local0  debug
        stats socket /tmp/haproxy.socket level admin  expose-fd listeners
        uid                     80
        gid                     80
        nbproc                  1
        nbthread                        1
        hard-stop-after         15m
        chroot                          /tmp/haproxy_chroot
        daemon
        tune.ssl.default-dh-param       2048
        log-send-hostname               BASTION
        server-state-file /tmp/haproxy_server_state

listen HAProxyLocalStats
        bind 127.0.0.1:2200 name localstats
        mode http
        stats enable
        stats refresh 10
        stats admin if TRUE
        stats show-legends
        stats uri /haproxy/haproxy_stats.php?haproxystats=1
        timeout client 5000
        timeout connect 5000
        timeout server 5000

frontend Main-Frontend-SNI-redirect-443
        bind                    [MY PUBLIC IP]:443 name [MY PUBLIC IP]:443
        mode                    tcp
        log                     global
        timeout client          30000
        tcp-request inspect-delay       5s
        acl                     dragoon req.ssl_sni -m sub -i smalldragoon.com
        tcp-request content accept if { req.ssl_hello_type 1 }
        use_backend Forwarder-to-smalldragoon.com-port-445_ipvANY  if  dragoon

frontend Frontend-smalldragoon.com-SNI
        bind                    [MY PUBLIC IP]:445 name [MY PUBLIC IP]:445
        bind /tmp/haproxy_chroot/Frontend-smalldragoon.com-SNI.socket name unixsocket uid 80 accept-proxy
        mode                    tcp
        log                     global
        timeout client          30000
        tcp-request inspect-delay       5s
        acl                     admin   req.ssl_sni -i admin-poc.smalldragoon.com
         acl                     ssp     req.ssl_sni -i ssp-poc.smalldragoon.com
        acl                     dmz     req.ssl_sni -i dmz-poc.smalldragoon.com
        tcp-request content accept if { req.ssl_hello_type 1 }
        use_backend smalldragoon.com-Server_ipvANY  if  admin
        use_backend smalldragoon.com-Server_ipvANY  if  ssp
        use_backend smalldragoon.com-Server_ipvANY  if  dmz
		
		
backend Forwarder-to-smalldragoon.com-port-445_ipvANY
        mode                    tcp
        id                      106
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        option                  httpchk OPTIONS /
        server                  forwarder /Frontend-smalldragoon.com-SNI.socket send-proxy-v2-ssl-cn id 107 check inter 1000

backend smalldragoon.com-Server_ipvANY
        mode                    tcp
        id                      104
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        server                  smalldragoon.com 192.168.1.1:443 id 105 check inter 1000

A few suggestions:

  • you only need to match SNI in one frontend (the one listening on port 443)
  • start using normal IP sockets for the backend → frontend re-circulation, thats less complex
  • don’t start adding features when your basic configuration doesn’t work. Don’t use proxy-protocol on the first try
  • I don’t see any SSL terminating configuration in there, so it’s unclear how that would work

Here’s a (untested) configuration that would work, based on your initial description (a, b, c.smalldragoon.com should be SSL passthrough, d.smalldragoon.com should be SSL terminated):

frontend Main-Frontend-SNI-redirect-443
        bind                    [MY PUBLIC IP]:443 name [MY PUBLIC IP]:443
        mode                    tcp
        log                     global
        timeout client          30000
        tcp-request inspect-delay       5s
        tcp-request content accept if { req.ssl_hello_type 1 }
        
        acl                     passthroughdom req.ssl_sni -i a.smalldragoon.com
        acl                     passthroughdom req.ssl_sni -i b.smalldragoon.com
        acl                     passthroughdom req.ssl_sni -i c.smalldragoon.com
        use_backend passthrough if passthroughdom
        
        acl                     ssltermdom req.ssl_sni -i d.smalldragoon.com
        use_backend sslterm if ssltermdom
        
        # or use a default_backend directive

frontend frontsslterm
        mode http
        bind 127.0.0.1:445 ssl crt /var/ssl/private/
        default_backend httpbackend

backend httpbackend
        mode http
        server server1 192.168.1.2:80

backend passthrough
        mode tcp
        server server1 192.168.1.1:443

backend sslterm
        mode tcp
        server local 127.0.0.1:445

hi @lukastribus , thanks a lot for all your help . So I understand indeed your suggestions on SNI matching and normal IP sockets.
regarding now the “other features”, I’m using PfSense and the GUI is adding these extra’s.
I checked all the options, checkboxes trying to be as close as possible updating the conf to get it matched with your sugested one :


listen HAProxyLocalStats
        bind 127.0.0.1:2200 name localstats
        mode http
        stats enable
        stats refresh 10
        stats admin if TRUE
        stats show-legends
        stats uri /haproxy/haproxy_stats.php?haproxystats=1
        timeout client 5000
        timeout connect 5000
        timeout server 5000

frontend Main-Frontend-SNI-and-redirect
        bind                    [MY PUBLIC IP]:443 name [MY PUBLIC IP]:443
        mode                    tcp
        log                     global
        timeout client          30000
        tcp-request inspect-delay       5s
        acl                     passthrough     req.ssl_sni -i admin-poc.smalldragoon.com
        acl                     passthrough     req.ssl_sni -i ssp-poc.smalldragoon.com
        acl                     joomla  req.ssl_sni -i joomla.smalldragoon.com
        tcp-request content accept if { req.ssl_hello_type 1 }
		
        use_backend passtrough-smalldragoon.com-Server_ipvANY  if  passthrough
        use_backend SSLterm_ipvANY  if  joomla

frontend frontend-ssl-term
        bind                    127.0.0.1:445 name 127.0.0.1:445
        mode                    tcp
        log                     global
        timeout client          30000
        default_backend httpbackend-portal-joomla_ipvANY

backend passtrough-smalldragoon.com-Server_ipvANY
        mode                    tcp
        id                      104
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        server                  smalldragoon-com 192.168.1.69:443 id 105 check inter 1000

backend SSLterm_ipvANY
        mode                    tcp
        id                      106
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        option                  httpchk OPTIONS /
        server                  sslterm 127.0.0.1:445 id 107 check inter 1000

backend httpbackend-portal-joomla_ipvANY
        mode                    tcp
        id                      108
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        server                  portal 192.168.1.26:80 id 109 check inter 1000

The differences I see are the mode for http backend ( do not manage to switch it to http instead of tcp)
When I try to access to https://joomla.smalldragoon.com, it is still redirected to the passtrhough site ( like he is not making a difference on the SNI )

frontend frontend-ssl-term is missing all the SSL configuration. It needs ssl and crt keywords with certificate.

And importantly:

I tried to anonymize as less as possible for the certificate not overlaping.
I have the SSL config in place now I think

frontend Main-Frontend-SNI-and-redirect
        bind                    [My Public IP]:443 name [My Public IP]:443
        mode                    tcp
        log                     global
        timeout client          30000
        tcp-request inspect-delay       5s
        acl                     passthrough     req.ssl_sni -i admin-poc.smalldragoon.com
        acl                     passthrough     req.ssl_sni -i ssp-poc.smalldragoon.com
        acl                     joomla  req.ssl_sni -i joomla.smalldragoon.xyz
        tcp-request content accept if { req.ssl_hello_type 1 }
        use_backend passtrough-smalldragoon.com-Server_ipvANY  if  passthrough
        use_backend SSLterm_ipvANY  if  joomla

frontend frontend-ssl-term
        bind                    127.0.0.1:445 name 127.0.0.1:445   ssl crt-list /var/etc/haproxy/frontend-ssl-term.crt_list
        mode                    http
        log                     global
        option                  http-keep-alive
        timeout client          30000
        acl                     aclcrt_frontend-ssl-term        var(txn.txnhost) -m reg -i ^([^\.]*)\.smalldragoon\.xyz(:([0-9]){1,5})?$
        http-request set-var(txn.txnhost) hdr(host)
        http-request  deny if { req.hdr_cnt(content-length) gt 1 }
        http-response deny if { res.hdr_cnt(content-length) gt 1 }
        use_backend httpbackend-portal-joomla_ipvANY  if   aclcrt_frontend-ssl-term

backend passtrough-smalldragoon.com-Server_ipvANY
        mode                    tcp
        id                      104
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        server                  smalldragoon.com 192.168.1.69:443 id 105 check inter 1000

backend SSLterm_ipvANY
        mode                    tcp
        id                      106
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        option                  httpchk OPTIONS /
        server                  sslterm 127.0.0.1:445 id 107 check inter 1000

backend httpbackend-portal-joomla_ipvANY
        mode                    http
        id                      108
        log                     global
        timeout connect         30000
        timeout server          30000
        retries                 3
        server                  portal 192.168.1.26:80 id 109 check inter 1000

but the SSL still says it is presenting the smalldragoon.com instead of offloading for domain smalldragoon.xyz ( and therefore saying the cert domain is not matching )

I can’t really help you further at this point. Check haproxy logs and confirm that the correct routing decision are made. Check that the correct SSL certificate used for SSL termination.

Go step by step.

OK, thanks you. will keep you posted.

HI, just an update. I’m having trouble to get logs from haproxy with PFSense. I only get the stop /restart but no info on the routing.
Will keep you posted once this solved and hopefully the main problem thanks to the logs

Check this for syslog configuration: