ACL filters when using mode tcp?

Hi, I have a setup I’ve been struggling with for a while.

I want to use tcp mode to pass-through SSL.
I want it so when I enter abc.com I get passed through to the abc.com backend, but if any other domain than abc.com is used to access haproxy with it will be sent to the fallback backend.

Is that possible?

Here is what I’ve tried so far:

global
        log /dev/log local0
        log /dev/log local1 notice
        chroot /var/lib/haproxy
        stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
        stats timeout 30s
        user haproxy
        group haproxy
        daemon
        maxconn 10240
        nbproc 4
        nbthread 1
        cpu-map auto:1/1-4 0-3

defaults
        log global
        mode tcp
        option tcplog
        option dontlognull
        maxconn 2048
        retries 3
        timeout connect 10s
        timeout client  30s
        timeout server  30s
        timeout http-request 10s
        timeout http-keep-alive 2s
        timeout queue 5s
        timeout tunnel 2m
        timeout client-fin 2s
        timeout server-fin 2s

frontend abc.com
         bind XXX.YYY.ZZZ.WWW:80
         bind XXX.YYY.ZZZ.WWW:443
         use_backend abc.com if { hdr(host) -i www.abc.com }
         use_backend abc.com if { hdr_dom(host) -i abc.com }
         default_backend fallback

backend abc.com
         balance static-rr
         server default AAA.BBB.CCC.DDD:443 check verify none fall 3 rise 2

backend fallback
         balance static-rr
         server fallback reverse-proxy.fallback.com:443

Any suggestions?

Those ACL would access HTTP headers. This is possible when a) the content is not encrypted or it is decrypted by haproxy and b) when the frontend is in http mode (this implies decryption).

In this case though the entire HTTP transaction is encrypted and you cannot access it.

What you can do is parse the SNI value in the SSL client_hello.

tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend abc.com if { req.ssl_sni -i abc.com }

Two important notes:

  • you need to wait for the complete SSL client_hello to be in the buffers (first to lines)
  • this will not work for overlapping certificates (one certificate that covers both acl’ed as well as fallback domains, because the SNI decision will be made once per SSL session (during handshake based on the client_hello packet) and the browser will reuse an existing SSL session for different domains, when the certificate allows that
2 Likes

Those 3 lines work wonders for the actual abc.com backend, thanks!

But is there any way to have it fallback to another backend if none of those rules fire? Like, if I change it to:

tcp-request inspect-delay 5s
tcp-request content accept if { req.ssl_hello_type 1 }
use_backend abc.com if { req.ssl_sni -i abc2.com }
use_backend abc.com if { req.ssl_sni -i www.abc2.com }

I have then added “default_backend fallback” after those 4 lines but it doesn’t seem to register?
I have a web server running on the same machine as haproxy which listens on 127.0.0.1:8080 and it works fine using “curl 127.0.0.1:8080” so it is running - but the backend is never used?

My fallback backend simply looks like this:

backend fallback
         balance static-rr
         server fallback 127.0.0.1:8080

8080 is probably not an HTTPS/SSL port. You can’t connect incoming HTTPS on port 443 to a plain HTTP server.

works fine using “curl 127.0.0.1:8080”

Exactly, using HTTP. Not HTTPS.
(You also need to remove binding to port 80 on your frontend, this will do the wrong thing)

When this is fixed, post the output of

curl -v https://XXX.YYY.ZZZ.WWW:443/

from the haproxy box, to see what happens.

1 Like