Openbsd pf squid transparent proxy using a bridge

bridgeopenbsdpfsquidtransparent-proxy

I want to use OpenBSD 5.7 as a filtering bridge in front of my router, and as part of that run a transparent proxy (squid 3.5.1) to manage web access. I have set up the openbsd machine as a bridge, and this works fine. However, I cannot get pf to divert web traffic to squid.

This is how my network looks

[Internet]
      |
      |
[OpenBSD]
      |
      |
  [       (Router)     ]
     |                 |
     |                 |
 [Wired Workstations]   [Wi-Fi]

Squid works correctly if I connect the OpenBSD machine to my network and manually configure a workstation to use it as a proxy. I have the following in my squid configuration to enable transparent interception

http_port 127.0.0.1:3129 tproxy

This is my pf configuration:

# internet facing
ext_if = "em1"

# internal facing
int_if = "em0"

#scrub provides a measure of protection against certain kinds of attacks based on incorrect handling of packet fragments
match in all scrub (no-df max-mss 1440)

# Prevent spoofing
antispoof log for $ext_if label "antispoof"

# Block anything coming form source we have no back routes for
block in log from no-route to any label "no back route"

# Proxy http
pass in quick on $int_if inet proto tcp to port www divert-to 127.0.0.1 port 3129
pass out quick from 127.0.0.1 divert-reply

pass in on $int_if
pass out on $int_if

pass in on $ext_if
pass out on $ext_if

Does anyone know why my divert-to rule doesn't work? I've looked in the logs

pass in quick on em0 inet proto tcp from any to any port = 80 flags S/SA divert-to 127.0.0.1 port 3129
  [ Evaluations: 36248     Packets: 544       Bytes: 355122      States: 29    ]
  [ Inserted: uid 0 pid 15666 State Creations: 29    ]

pass out quick inet from 127.0.0.1 to any flags S/SA divert-reply
[ Evaluations: 72399 Packets: 0 Bytes: 0 States: 0 ]
[ Inserted: uid 0 pid 15666 State Creations: 0 ]

I can see that the rule is 'working', however nothing shows up in the squid logs, and I can still access websites. This is basically the rules recommended by the squid how to. I've tried using this rule instead

pass in log quick on $int_if inet proto tcp from any to any port www rdr-to 127.0.0.1 port 3129

When I use this I see that the rule redirection occurs, however nothing shows up in the squid logs. Also requests to websites time-out.

pass in log quick on em0 inet proto tcp from any to any port = 80 flags     S/SA rdr-to 127.0.0.1 port 3129
  [ Evaluations: 25213     Packets: 8         Bytes: 496         States: 1     ]

This is the output from ifconfig

ifconfig

lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 32768
    priority: 0
    groups: lo
    inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4
    inet6 ::1 prefixlen 128
    inet 127.0.0.1 netmask 0xff000000
em0: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
    lladdr 00:e8:4c:68:1a:f1
    priority: 0
    groups: egress
    media: Ethernet autoselect (1000baseT full-duplex,rxpause,txpause)
    status: active
em1: flags=8b43<UP,BROADCAST,RUNNING,PROMISC,ALLMULTI,SIMPLEX,MULTICAST> mtu 1500
    lladdr 00:e8:4c:68:1a:f2
    priority: 0
    media: Ethernet autoselect (100baseTX full-duplex,rxpause,txpause)
    status: active
enc0: flags=0<>
    priority: 0
    groups: enc
    status: active
bridge0: flags=41<UP,RUNNING>
    groups: bridge
    priority 32768 hellotime 2 fwddelay 15 maxage 20 holdcnt 6 proto rstp
    em0 flags=7<LEARNING,DISCOVER,BLOCKNONIP>
            port 1 ifpriority 0 ifcost 0
    em1 flags=7<LEARNING,DISCOVER,BLOCKNONIP>
            port 2 ifpriority 0 ifcost 0
pflog0: flags=141<UP,RUNNING,PROMISC> mtu 33144
    priority: 0
    groups: pflog

How can I get this to work? What is wrong with my pf rules?

Update

This is getting a bit weird

If I use

pass in quick log on $int_if inet proto tcp to port 80 divert-to 127.0.0.1 port 3129
pass out quick log from 127.0.0.1 divert-reply

I get the following in the log:

rule 3/(match) pass in on em0: 233.297.39.227.32880 > 153.94.152.119.87: S 2656450467:2656450467(0) win 65535 <mss 1460,nop,wscale 5,nop,nop,timestamp 605332037 0,[|tcp]> (DF)

If I change it to

pass in quick log on $int_if inet proto tcp to port 80 rdr-to 127.0.0.1 port 3129
pass out quick log from 127.0.0.1 divert-reply

I get the following in the log, however nothing shows up in the squid logs, and unsurprisingly requests to web sites time out

rule 3/(match) pass in on em0: 233.297.39.227.39091 > 127.0.0.1.3129: S 409217949:409217949(0) win 65535 <mss 1460,nop,wscale 5,nop,nop,timestamp 605394683 0,[|tcp]> (DF)

Best Answer

If you have OpenBSD 4.1 or 4.3 , You have to do :

rdr pass inet proto tcp from 192.168.231.0/24 to any port 80 -> 192.168.231.1 port 3129

block in
pass in quick on $int_if
pass in quick on $wi_if
pass out keep state

And if you have OpenBSD 4.4 or higher You have to do :

set skip on $int_if
set skip on $wi_if
IPv6:

pass in quick inet6 proto tcp from 2001:DB8::/32 to port www divert-to ::1 port 3129
pass out quick inet6 from 2001:DB8::/32 divert-reply
IPv4:
pass in quick on inet proto tcp from 192.0.2.0/24 to port www divert-to 127.0.0.1 port 3129
pass out quick inet from 192.0.2.0/24 divert-reply

UPDATE:

OF course, You need some changes according to your interfaces.