Skip to content

HAProxy

SNI proxy

frontend https
    bind :443
    mode tcp 

    acl clienthello req.ssl_hello_type 1

    acl site1 req.ssl_sni -i domain1.com
    acl site1 req.ssl_sni -i -m end .domain1.com

    acl site2 req.ssl_sni -i domain2.org
    acl site2 req.ssl_sni -i -m end .domain2.org

    tcp-request inspect-delay 2s
    tcp-request content accept if clienthello

    use_backend web-https-site1 if site1
    use_backend web-https-site2 if site1
    default_backend default-site

Pass client IP to backend server

Client ---> HAProxy --(connects with client IP)--> Backend
       <---         <---(responds to client IP)---

The idea is that HAProxy connects to the backend server on a loopback address with the IP address of the client, and catches response with the help of iptables and policy routing.

Note

Preferably, the backend server should bind to an address that no one else can reach. Loopback addresses, namely addresses in 127/8, is avoided, because to use them for routing purposes one needs to turn on sysctl route_localnet, which has security implications. We can still create a dummy interface and assign a reserved address to it to avoid route_localnet.

In the following example, the address of the dummy interface is 203.0.113.1.

/etc/haproxy/haproxy.cfg:

frontend http
    bind :80 transparent
    mode tcp 
    option tcplog

    default_backend web-http

backend web-http
    mode tcp 
    source 0.0.0.0 usesrc clientip
    server nginx 203.0.113.1:81

iptables:

-t mangle -N TRANSPROXY
-t mangle -A OUTPUT -p tcp --sport 81 -j TRANSPROXY
-t mangle -A TRANSPROXY -j CONNMARK --restore-mark
-t mangle -A TRANSPROXY -m mark ! --mark 0 -j ACCEPT
-t mangle -A TRANSPROXY -j MARK --set-mark 1
-t mangle -A TRANSPROXY -j CONNMARK --save-mark

ip rule:

ip rule add fwmark 0x1 lookup 100
ip route add local default dev lo