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