Score:0

HAProxy transparent TCP proxy fails when source 0.0.0.0 usesrc clientip is set

fo flag

I have a setup where I want a transparent TCP proxy in front of some HTTPS services. The services themselves deal with certificates so I'm trying to avoid a HTTP proxy here. I also need the services to get the client's original IP.

The setup is like this:

  1. 192.168.86.33: HAProxy
  2. 192.168.86.31: Service1
  3. 192.168.86.36: Service2 All three run Raspberry Pi OS.

Based on docs it seems like HAProxy is a good tool for the job but I can't get it to work. It works until I add source 0.0.0.0 usesrc clientip but the service sees the IP of the proxy instead of the client.

When I add source 0.0.0.0 usesrc clientip I get this in the client: Error: Client network socket disconnected before secure TLS connection was established And this in the HAProxy log when run with strace:

[{EPOLLIN, {u32=15, u64=15}}], 200, 60000, NULL, 8) = 1
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=304312352}) = 0
read(15, "c", 1024)                     = 1
read(15, 0x7fe9410d50, 1024)            = -1 EAGAIN (Resource temporarily unavailable)
setsockopt(17, SOL_TCP, TCP_NODELAY, [1], 4) = 0
recvfrom(17, 0x55a7d68c80, 15360, 0, NULL, NULL) = -1 EAGAIN (Resource temporarily unavailable)
socket(AF_INET, SOCK_STREAM, IPPROTO_IP) = 18
fcntl(18, F_SETFL, O_RDONLY|O_NONBLOCK) = 0
setsockopt(18, SOL_TCP, TCP_NODELAY, [1], 4) = 0
setsockopt(18, SOL_IP, IP_BIND_ADDRESS_NO_PORT, [1], 4) = 0
setsockopt(18, SOL_IP, IP_TRANSPARENT, [1], 4) = 0
setsockopt(18, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
bind(18, {sa_family=AF_INET, sin_port=htons(0), sin_addr=inet_addr("192.168.86.161")}, 16) = 0
connect(18, {sa_family=AF_INET, sin_port=htons(30989), sin_addr=inet_addr("192.168.86.36")}, 16) = -1 EINPROGRESS (Operation now in progress)
epoll_ctl(4, EPOLL_CTL_ADD, 17, {EPOLLIN|EPOLLRDHUP, {u32=17, u64=17}}) = 0
epoll_ctl(4, EPOLL_CTL_ADD, 18, {EPOLLIN|EPOLLOUT|EPOLLRDHUP, {u32=18, u64=18}}) = 0
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=307519852}) = 0
epoll_pwait(4, [{EPOLLIN, {u32=17, u64=17}}], 200, 5001, NULL, 8) = 1
clock_gettime(CLOCK_THREAD_CPUTIME_ID, {tv_sec=0, tv_nsec=307766259}) = 0
recvfrom(17, "\26\3\1\2\0\1\0\1\374\3\3\277\357D\241\236\245Gw\361\16\6\273\234\212D\245\305\241.\10o"..., 15360, 0, NULL, NULL) = 517

My full haproxy.cfg below. I have temporarily removed out one of the backends:

defaults
mode tcp
timeout connect 5000ms
timeout client 50000ms
timeout server 50000ms

frontend db
   bind 192.168.86.33:30989 transparent 
   default_backend databases

 backend databases
   source 0.0.0.0 usesrc clientip
   server db2 192.168.86.36:30989

Connecting directly to 192.168.86.36 works. Connecting via the proxy works if I remove the "source" line but my service sees the wrong IP.

I believe I have the right kernel modules loaded:

lsmod | grep proxy
nft_tproxy             16384  0
nf_tproxy_ipv6         16384  1 nft_tproxy
nf_tproxy_ipv4         16384  1 nft_tproxy
nf_defrag_ipv6         20480  3 nf_conntrack,xt_socket,nft_tproxy
nf_defrag_ipv4         16384  3 nf_conntrack,xt_socket,nft_tproxy
nf_tables             237568  212 nft_compat,nft_tproxy,nft_chain_nat,nft_socket
ipv6                  557056  50 nf_tproxy_ipv6,bridge,br_netfilter,nf_socket_ipv6

These are the iptables and routes set on the proxy:

#!/bin/bash
iptables -t mangle -N DIVERT
iptables -t mangle -A PREROUTING -p tcp -m socket -j DIVERT
iptables -t mangle -A DIVERT -j MARK --set-mark 111
iptables -t mangle -A DIVERT -j ACCEPT
ip rule add fwmark 111 lookup 100
ip route add local 0.0.0.0/0 dev lo table 100

Based on the response here I have also set the proxy as the default gateway on the service machine:

ip route show
default via 192.168.86.33 dev wlan0 src 192.168.86.36 metric 303 

Final things I've tried:

  • Recompiled haproxy with USE_LINUX_TPROXY=1: OPTIONS = USE_LINUX_TPROXY=1
  • I have set net.ipv4.ip_forward & net.ipv4.ipnonlocalbind to 1

Any help as to where I am going wrong would be much appreciated!

djdomi avatar
za flag
don't you wanna have a reverse proxy instead?
Rablet avatar
fo flag
Interesting! Is there a good alternative tool you can think of which would do the same? Passthrough of TCP requests in a load balanced way whilst preserving the original source IP.
djdomi avatar
za flag
Product Suggestion is offtopic. however, HAProxy and NGINX can do reverse proxy - you need only to manage the certificate on the Proxy, due (for nginx) nginx does not care on the backend on ssl errors by default
I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.