PF Redirecting incoming responses to transparent proxy

I run multiple jails on a physical FreeBSD 13.1 server. For simplicity this is what it looks like:

Code:
Physical machine:
  em0: 10.0.0.10/24

Jail lb01:
  epair1b: 10.0.0.20/24
  epair2b: 1.2.3.4/24 (public)

Jail app01:
  epair3b: 10.0.0.30/24

lb01 runs nginx that I've been using as a TCP/UDP proxy as a frontend to services running in other jails that I'd like to be available from outside of my network.
app01 runs such a service on port 33333/tcp.

Currently, in non-transparent proxy mode, the traffic flow looks as follows:
Code:
5.5.5.5:<src_port1>      ->   1.2.3.4:33333 (request from external client to nginx proxy)
10.0.0.20:<src_port2>    ->   10.0.0.30:33333 (request from nginx on lb01 to app01)
10.0.0.20:<src_port2>    <-   10.0.0.30:33333 (response from app01 to nginx on lb01)
5.5.5.5:<src_port1>      <-   1.2.3.4:33333 (respose from nginx proxy on lb01 to external client)
This works without issues, however I'd like to enable nginx transparent mode so that service on app01:33333/tcp is aware of the real client IP.
This changes (or rather *should change*) the traffic flow as follows:

Code:
5.5.5.5:<src_port1>      ->   1.2.3.4:33333 (request from external client to nginx proxy)
5.5.5.5:<src_port2>      ->   10.0.0.30:33333 (request from nginx on lb01 to app01, with a spoofed client IP)
5.5.5.5:<src_port2>      <-   10.0.0.30:33333 (response from app01 to the client IP)
5.5.5.5:<src_port1>      <-   1.2.3.4:33333 (respose from nginx proxy on lb01 to external client)

Jail app01 uses lb01 as its default gateway, so in the latter case the replies from app01 to the spoofed client IP address (5.5.5.5), arrive at lb01 and then without any pf rules, obviously get sent on to 5.5.5.5.
Because app01 is not to the original request from the client sent from src_port1 but to the proxied request with a src_port2, the client IP drops the packet.

That obviously doesn't work as intended, I'm not expecting magic here.


What I've been failing to do is to use PF to redirect the incoming response from app01 on epair1b to the nginx running locally, instead of being normally routed through the ext_if.
The original instructions published by nginx for Linux are rather simple but I can't work out an equivalent on FreeBSD:

Code:
# ip rule add fwmark 1 lookup 100
# ip route add local 0.0.0.0/0 dev lo table 100
# iptables -t mangle -A PREROUTING -p tcp -s 172.16.0.0/28 --sport 80 -j MARK --set-xmark 0x1/0xffffffff

I created a separate routing table used in one of the attempts described later:
Code:
#  setfib 1 netstat -rnW
Routing tables (fib: 1)

Internet:
Destination        Gateway            Flags   Nhop#    Mtu      Netif Expire
default            link#1             US          1  16384        lo0
127.0.0.1          link#1             UHS         2  16384        lo0

The rules below (tested separately, of course) stop the response from 10.0.0.20:<src_port2> from trying to leave epair2b but still don't cause the response to be intercepted by nginx, which complains about the lack of response from app01:

Code:
int_if="epair1b"
ext_if="epair2b"

pass in quick on $int_if proto tcp from $int_if:network port 33333 to any flags any rtable 1
rdr pass on $int_if inet proto tcp from $int_if:network port 33333 to any -> 127.0.0.1
pass in quick on $int_if route-to (lo0 127.0.0.1) proto tcp from $int_if:network port 33333 to any

I've tested various other rules, to no avail but I'd like to omit them here as this post is already quite long.
I've been reading man pages of pf, pf.conf and docs section on firewall and advanced networking. So far that hasn't helped me find what I'm missing.
Could someone please point me in the right direction?

Thank you.
 
Shorter version - how to redirect response packet, starting from line 6 to the transparent nginx proxy running on the host that's capturing these packets?

Code:
reading from file dump, link-type PFLOG (OpenBSD pflog file), snapshot length 262144
     1  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [S], seq 2289730441, win 64240, options [mss 1452,sackOK,TS val 845180587 ecr 0,nop,wscale 7], length 0
     2  rule 1/0(match): pass out on epair2b: 1.2.3.4.33333 > 5.5.5.5.19326: Flags [S.], seq 4186374178, ack 2289730442, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 209974190 ecr 845180587], length 0
     3  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [.], ack 1, win 502, options [nop,nop,TS val 845180590 ecr 209974190], length 0
     4  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [P.], seq 1:87, ack 1, win 502, options [nop,nop,TS val 845180590 ecr 209974190], length 86
     5  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158583517 ecr 0], length 0
     6  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129197840 ecr 3158583517,nop,wscale 7], length 0
     7  rule 1/0(match): pass out on epair2b: 1.2.3.4.33333 > 5.5.5.5.19326: Flags [.], ack 87, win 1044, options [nop,nop,TS val 209974232 ecr 845180590], length 0
     8  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129198850 ecr 3158583517,nop,wscale 7], length 0
     9  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158584573 ecr 0], length 0
    10  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129198896 ecr 3158583517,nop,wscale 7], length 0
    11  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129200928 ecr 3158583517,nop,wscale 7], length 0
    12  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158586783 ecr 0], length 0
    13  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129201106 ecr 3158583517,nop,wscale 7], length 0
    14  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129205280 ecr 3158583517,nop,wscale 7], length 0
    15  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158590985 ecr 0], length 0
    16  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129205308 ecr 3158583517,nop,wscale 7], length 0
    17  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129213473 ecr 3158583517,nop,wscale 7], length 0
    18  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158599219 ecr 0], length 0
    19  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129213542 ecr 3158583517,nop,wscale 7], length 0
    20  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129229602 ecr 3158583517,nop,wscale 7], length 0
    21  rule 1/0(match): pass out on epair1b: 5.5.5.5.30573 > 10.0.0.20.33333: Flags [S], seq 2194501990, win 65535, options [mss 1460,nop,wscale 6,sackOK,TS val 3158615431 ecr 0], length 0
    22  rule 1/0(match): pass in on epair1b: 10.0.0.20.33333 > 5.5.5.5.30573: Flags [S.], seq 1027893281, ack 2194501991, win 65160, options [mss 1460,sackOK,TS val 2129229754 ecr 3158583517,nop,wscale 7], length 0
    23  rule 1/0(match): pass out on epair2b: 1.2.3.4.33333 > 5.5.5.5.19326: Flags [P.], seq 1:330, ack 87, win 1044, options [nop,nop,TS val 210034209 ecr 845180590], length 329
    24  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [.], ack 330, win 501, options [nop,nop,TS val 845240610 ecr 210034209], length 0
    25  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [F.], seq 87, ack 330, win 501, options [nop,nop,TS val 845240611 ecr 210034209], length 0
    26  rule 1/0(match): pass out on epair2b: 1.2.3.4.33333 > 5.5.5.5.19326: Flags [.], ack 88, win 1044, options [nop,nop,TS val 210034213 ecr 845240611], length 0
    27  rule 1/0(match): pass out on epair2b: 1.2.3.4.33333 > 5.5.5.5.19326: Flags [F.], seq 330, ack 88, win 1044, options [nop,nop,TS val 210034213 ecr 845240611], length 0
    28  rule 1/0(match): pass in on epair2b: 5.5.5.5.19326 > 1.2.3.4.33333: Flags [.], ack 331, win 501, options [nop,nop,TS val 845240613 ecr 210034213], length 0
 
Back
Top