PF rdr rule does not take effect?

It's been a few years since I last used PF to manage my traffic, but my previous experience (2003-2005) was was very good so I thought I'd give it a go.

However, I'm having issues with a rdr rule. I'm trying to forward (rdr) HTTP/HTTPS to an internal machine but I can't quite make it work.

My setup:
  • Router (FreeBSD 11) with 4 interfaces, igb0-3, external net from DHCP
  • igb0, WAN
  • igb1, LAN, 172.31.0.0/24 (switched and with a wireless AP in the network)
  • igb2, DMZ, 172.31.1.0/24 (stupid name but here is where the web server lives)
  • igb3 is currently unused
I'm running DHCPD on the router which serves both igb1 & igb2, but with different subnet definitions. No issues here, computers gets their IPs without issues, even through the AP.

If try to connect to my public IP address on port 80 or 443, I simply get no reply. I can use SSH to login to my machine with the same IP from outside of my network so the IP works and at least one of the pass rules works.

If I listen to pflog0 with a log rule for the rdr, I get absolutely nothing (
Code:
tcpdump -i pflog0
)

This is my ruleset, everything from the current one.

Code:
wan="igb0"
lan="igb1"
dmz="igb2"
tcp = "{ ssh }"
fwd = "{ http, https }"
blf = "172.31.1.254"

# Options
set fingerprints "/etc/pf.os"
set loginterface $wan
set block-policy return
set optimization normal
set limit { states 100000, frags 100000, src-nodes 10000, table-entries 100000 }
set skip on lo0

## Normalisering
scrub in all

## Queuing

## NAT
nat on $wan from {$lan:network, $dmz:network} to any -> ($wan)
rdr pass log on $wan proto tcp from any to any port $fwd -> $blf

antispoof for { $wan, $lan, $dmz }

block all
pass quick on lo0
pass quick from $lan:network to any
pass quick from $dmz:network to any
pass in quick proto tcp from any to any port $tcp keep state
pass in quick proto tcp from any to ($wan) port $fwd
pass out quick on $wan keep state

I'm pretty sure I'm missing something obvious here, but I've tried to read up on how this should be done and have nothing to show for it.

Unrelated but maybe related - I sometimes have issues seeing a samba machine on one subnet from the other. The router is the WINS server which is sent with DHCP to all clients and the samba server on the different subnet is configured to use the router's WINS server as well. Have I missed something with my subnetting?
 
I suspect this might be due to the antispoof rule you have, but I can't remember exactly what that expands to as I never use it myself (I prefer to write my own antispoof rules). Could you please post the output of pfctl -sr? It also wouldn't hurt to post the output of pfctl -sn as well.
 
Current ruleset, slightly changed but still doesn't work:

Code:
wan="igb0"
# lan="igb1"
# dmz="igb2"
lan="{ igb1:network, igb2:network }"

fwd = "{ 32400 }"
tcp = "{ ssh, http, https }"
blf = "172.31.1.254"

# Options
set fingerprints "/etc/pf.os"
set loginterface $wan
set block-policy return
set optimization normal
set limit { states 100000, frags 100000, src-nodes 10000, table-entries 100000 \
}
set skip on lo0

scrub in all

nat on $wan from $lan to any -> ($wan)
rdr pass log on $wan proto tcp from any to any port $fwd -> $blf

block log all

pass quick from $lan to any
pass in quick proto tcp from any to any port $tcp keep state
pass out quick on $wan  keep state

Code:
# pfctl -sn
nat on igb0 inet from 172.31.0.0/24 to any -> (igb0) round-robin
nat on igb0 inet from 172.31.1.0/24 to any -> (igb0) round-robin
rdr pass log on igb0 inet proto tcp from any to any port = 32400 -> 172.31.1.254

# pfctl -sr
scrub in all fragment reassemble
block return log all
pass in quick proto tcp from any to any port = ssh flags S/SA keep state
pass in quick proto tcp from any to any port = http flags S/SA keep state
pass in quick proto tcp from any to any port = https flags S/SA keep state
pass quick inet from 172.31.0.0/24 to any flags S/SA keep state
pass quick inet from 172.31.1.0/24 to any flags S/SA keep state
pass out quick on igb0 all flags S/SA keep state

Whoa slight success!

21:23:49.174082 IP 54.171.49.114.54850 > 172.31.1.254.32400: Flags [S], seq 3520317425, win 17922, options [mss 1460,sackOK,TS val 345822647 ecr 0,nop,wscale 10], length 0

So I tried to telnet into my open port and this is what I got:

telnet: connect to address 85.xx.xx.xx: No route to host

Now that is interesting.
 
If try to connect to my public IP address on port 80 or 443, I simply get no reply. I can use SSH to login to my machine with the same IP from outside of my network so the IP works and at least one of the pass rules works.

You explicitly state you tested SSH from outside of the network. Is that also how you tested HTTP/HTTPS? The redirections won't work for traffic that isn't coming in on the external interface.
 
You'll need a rule to permit the ingress traffic. Also, does this machine (the router) have the address 172.31.1.254? If not, you'll also need an egress rule for your redirected traffic to your LAN. If it does, you shouldn't as it should pass the traffic over lo0 which you've set to skip. I also recommend the style of your original macros, and I'll show you why in the comments below.
Code:
wan="igb0"
lan="igb1"
dmz="igb2"
#lan="{ igb1:network, igb2:network }" # Don't do this.  If you really want multiple subnets, there are better ways to do this.
# In your paticular case you can optimize this by making one supernet of 172.31.0.0/23, but in cases where that is not applicable,
# use a table, which is designed to hold multiple IP address/networks.

lan_supernet = "172.31.0.0/23" # This is an optimization to include both of your networks in a single rule

fwd = "{ 32400 }"
tcp = "{ ssh, http, https }"
blf = "172.31.1.254"

# Options
set fingerprints "/etc/pf.os"
set loginterface $wan
set block-policy return
set optimization normal
set limit { states 100000, frags 100000, src-nodes 10000, table-entries 100000 \
}
set skip on lo0

scrub in all

nat on $wan from $lan_supernet to any -> ($wan) # This expands to only one nat rule, unlike your previous setup
rdr pass log on $wan proto tcp from any to any port $fwd -> $blf

block log all

# Many of these rules are extremely permissive, and should really be narrowed down to what you actually need.
# As they are now you might be better off with a default-accept and a few block rules instead, but it's better to just rewrite them to be more specific.
pass quick from $lan_supernet to any
pass in quick proto tcp from any to any port $tcp keep state # This does not permit the $fwd port!
pass out quick on $wan  keep state

# To permit the forwarded port I recommend something like this:
pass in quick on $wan inet proto tcp from any to $blf port $fwd synproxy state # use modulate state instead of synproxy if the $blf address is held by this machine
# You'll need something like this for egress traffic, assuming $blf is another machine in your dmz:
pass out quick on $dmz inet proto tcp from any to $blf port $fwd keep state
 
You explicitly state you tested SSH from outside of the network. Is that also how you tested HTTP/HTTPS? The redirections won't work for traffic that isn't coming in on the external interface.

Both. I've tested it both from a local subnet and using telnet from several remote hosts (on the internet). My current setup has changed since I never got the forwarding up and running (HTTP(S) on the router now) but I'd like to rdr a few other ports now.

You'll need a rule to permit the ingress traffic. Also, does this machine (the router) have the address 172.31.1.254? If not, you'll also need an egress rule for your redirected traffic to your LAN. If it does, you shouldn't as it should pass the traffic over lo0 which you've set to skip. I also recommend the style of your original macros, and I'll show you why in the comments below.
Code:
wan="igb0"
lan="igb1"
dmz="igb2"
#lan="{ igb1:network, igb2:network }" # Don't do this.  If you really want multiple subnets, there are better ways to do this.
# In your paticular case you can optimize this by making one supernet of 172.31.0.0/23, but in cases where that is not applicable,
# use a table, which is designed to hold multiple IP address/networks.

lan_supernet = "172.31.0.0/23" # This is an optimization to include both of your networks in a single rule

fwd = "{ 32400 }"
tcp = "{ ssh, http, https }"
blf = "172.31.1.254"

# Options
set fingerprints "/etc/pf.os"
set loginterface $wan
set block-policy return
set optimization normal
set limit { states 100000, frags 100000, src-nodes 10000, table-entries 100000 \
}
set skip on lo0

scrub in all

nat on $wan from $lan_supernet to any -> ($wan) # This expands to only one nat rule, unlike your previous setup
rdr pass log on $wan proto tcp from any to any port $fwd -> $blf

block log all

# Many of these rules are extremely permissive, and should really be narrowed down to what you actually need.
# As they are now you might be better off with a default-accept and a few block rules instead, but it's better to just rewrite them to be more specific.
pass quick from $lan_supernet to any
pass in quick proto tcp from any to any port $tcp keep state # This does not permit the $fwd port!
pass out quick on $wan  keep state

# To permit the forwarded port I recommend something like this:
pass in quick on $wan inet proto tcp from any to $blf port $fwd synproxy state # use modulate state instead of synproxy if the $blf address is held by this machine
# You'll need something like this for egress traffic, assuming $blf is another machine in your dmz:
pass out quick on $dmz inet proto tcp from any to $blf port $fwd keep state

Thank you very much, I will have a look at this later tonight.
 
You'll need a rule to permit the ingress traffic. Also, does this machine (the router) have the address 172.31.1.254? If not, you'll also need an egress rule for your redirected traffic to your LAN. If it does, you shouldn't as it should pass the traffic over lo0 which you've set to skip. I also recommend the style of your original macros, and I'll show you why in the comments below.
Code:
wan="igb0"
lan="igb1"
dmz="igb2"
#lan="{ igb1:network, igb2:network }" # Don't do this.  If you really want multiple subnets, there are better ways to do this.
# In your paticular case you can optimize this by making one supernet of 172.31.0.0/23, but in cases where that is not applicable,
# use a table, which is designed to hold multiple IP address/networks.

lan_supernet = "172.31.0.0/23" # This is an optimization to include both of your networks in a single rule

fwd = "{ 32400 }"
tcp = "{ ssh, http, https }"
blf = "172.31.1.254"

# Options
set fingerprints "/etc/pf.os"
set loginterface $wan
set block-policy return
set optimization normal
set limit { states 100000, frags 100000, src-nodes 10000, table-entries 100000 \
}
set skip on lo0

scrub in all

nat on $wan from $lan_supernet to any -> ($wan) # This expands to only one nat rule, unlike your previous setup
rdr pass log on $wan proto tcp from any to any port $fwd -> $blf

block log all

# Many of these rules are extremely permissive, and should really be narrowed down to what you actually need.
# As they are now you might be better off with a default-accept and a few block rules instead, but it's better to just rewrite them to be more specific.
pass quick from $lan_supernet to any
pass in quick proto tcp from any to any port $tcp keep state # This does not permit the $fwd port!
pass out quick on $wan  keep state

# To permit the forwarded port I recommend something like this:
pass in quick on $wan inet proto tcp from any to $blf port $fwd synproxy state # use modulate state instead of synproxy if the $blf address is held by this machine
# You'll need something like this for egress traffic, assuming $blf is another machine in your dmz:
pass out quick on $dmz inet proto tcp from any to $blf port $fwd keep state

This works much better, thank you very much!

I thought that rdr pass would automatically allow the port as well as forwarding it, but I seem to have misread the documentation.

As for subnetting or not - I've mostly used subnets since my network spans two NICs in my router. I tried to make it one network but couldn't figure it out (this is only a home network, so I'm a bit lazy) so I just went with two subnets. I think that I'll try to revisit this decision today.
 
Back
Top