PF Poking a VPN-Sized Hole Through 'pf'

Status
Not open for further replies.
I'm running a server that is using 'pf' as it's firewall. This server also runs a VPN (as a vpn server that client machines on the internet can connect to). I'm trying to open up all the ports I need so I can connect, but it's just not working. The VPN is L2TP over IPSec. Here's what I have currently:

Code:
block in all
pass in proto esp from any to any keep state
pass in proto udp from any to any port { 500,1701,4500 } keep state

Any thoughts? I was googling around and saw mention of adding 'ha' protocol too. I have never head of that, but I added it anyway, and it had no effect.
 
Code:
block in all
pass in proto esp from any to any keep state
pass in proto udp from any to any port { 500,1701,4500 } keep state

Any thoughts?

You don't use "keep state" with UDP, it is stateless. I don't know what proto esp is.

I would use a macro for my ports and assign it a designation.
Code:
### Macro name for external interface
ext_if = *Your external interface designation goes here*
udp_macro = "{ 500, 1701, 4500 }"


pass in on $ext_if proto udp from any to any port $udp_macro

My ruleset is set to block so I'm really guessing at that pass syntax, but it should be right. I'm assuming you have outbound rules in place.
 
You don't use "keep state" with UDP, it is stateless.

Sorry but this is as wrong as it gets...

UDP is stateless on the sending and receiving socket level but on a stateful inspection packet filter you can still keep a state by using timeout counters for tracking when the last packet belonging to that "state" was last seen and that's exactly what every modern packet filter including PF does. The timeouts are tunable and it's even recommended to tune them with certain applications like VOIP.
 
I've never seen a pf ruleset here or on an OpenBSD machine that specified keep state for UDP, though pfctl does show it is keeping state for my outbound rule:


Code:
### Keep and modulate state of outbound tcp, udp and icmp traffic
pass out on $ext_if proto { tcp, udp, icmp } from any to any modulate state

# pfctl -s all
pass out on em0 proto tcp all flags S/SA modulate state
pass out on em0 proto udp all keep state
pass out on em0 proto icmp all keep state
 
It says the same "keep state" for all those protocols but the underlying state keeping mechanism varies by protocol.
 
I'm finally getting back to this project. First things first, there really seems to be some part of pf config that i am not understanding. Lets ignore what my rules are trying to accomplish for a moment.

Why does this ruleset load just fine:
Code:
table <VPN> { 192.168.1.1/24 } persist
table <badhosts> persist

block in quick from <badhosts> to any
block in all

pass in quick from <VPN> to any flags S/SA keep state
pass in quick proto tcp from any to any port { 25 80 443 587 993 } flags S/SA keep state
pass in quick proto { esp icmp ah ipencap } from any to any keep state
pass in quick proto udp from any to any port { 500 1701 4500 } keep state

And yet this ruleset fails with a syntax error on the last line. All I did was change the order of the last four lines. Why would that cause a syntax error?
Code:
table <VPN> { 192.168.1.1/24 } persist
table <badhosts> persist

block in quick from <badhosts> to any
block in all

pass in quick proto tcp from any to any port { 25 80 443 587 993 } flags S/SA keep state
pass in quick proto { esp icmp ah ipencap } from any to any keep state
pass in quick proto udp from any to any port { 500 1701 4500 } keep state
pass in quick from <VPN> to any flags S/SA keep state
 
Read here:

https://www.freebsd.org/doc/handbook/ipsec.html

Also you need to exclude the vpn tunnel from your nat.

Based on that article, I added the protocol ipencap. So I have the following rules:
Code:
pass in quick proto { esp icmp ah ipencap } from any to any keep state
pass in quick proto udp from any to any port { 500 1701 4500 } keep state

Yes I am still unable to connect to my VPN. If I turn the VPN off, I can connect right away. So I know the problem is in the firewall settings somewhere.
 
Do you have NAT?

Where is your VPN interface in the pf configuration?

You don't need keep states for those protocols.

Is your VPN working without firewall?
 
Do you have NAT?
No I am on the same LAN as the server currently, the only firewall is on the server. This is a sever I'm configuring to live in a data center.

Where is your VPN interface in the pf configuration?
I allow the VPN traffic by IP rather than interface

You don't need keep states for those protocols.
*shrug* some say you do, some say you don't

Is your VPN working without firewall?
Perfectly.


BUT it seems to be fixed! Someone suggested I add set skip on lo0 and just like that, I seem to be able to connect perfectly. Now that I made some progress, I need to play around with the rules and do some tweaking and see where I stand (in a day or two).
 
It's easy to allow the entire vpn interface instead of specify all IPs that will goes through it.
pass in quick on gif0 from any to any
 
If you want pf to ignore a certain interface for filtering then it might be better to also just skip it:
Code:
set skip on gif0
This will prevent pf from spending any effort at all on the interface whereas with a dedicated rule it would always need to process that rule (and others) every time data goes in or out.
 
It looks like my VPN doesn't have a unique interface. It looks like it's piggy-backed on en0 along with a real public ethernet address.... But that said, having it setup the way it is isn't really a problem (is it?) so I'm fine with it for now.

BUT I found one more bug in my ruleset. Once the firewall is turned on, I am unable to browse the web on the machine. There must be a rule to allow inbound responses to allow normal internet use traffic that starts on the machine itself?
 
Ok this is my finished product:
Code:
set skip on lo0

table <VPN> const { 192.168.0.1/24 }
table <badhosts> persist

block in quick from <badhosts> to any
block in all

pass in quick proto tcp from any to any port { 25 80 443 587 993 } flags S/SA keep state
pass in quick proto { esp icmp } from any to any keep state
pass in quick from <VPN> to any flags S/SA keep state
pass in quick proto udp from any to any port { 500 1701 4500 } keep state
pass out proto { tcp, udp, icmp } from any to any keep state

That last line, pass out proto { tcp, udp, icmp } from any to any keep state, is what I came up with to allow normal internet use traffic emanating from the machine running the firewall. Some port scan results show me that it is working correctly but I'm hoping you guys can verify this with some first hand knowledge? Note that I intentionally don't use egress here because I do have some outgoing traffic on both of my ethernet ports.
 
If you want pf to ignore a certain interface for filtering then it might be better to also just skip it:
Code:
set skip on gif0
This will prevent pf from spending any effort at all on the interface whereas with a dedicated rule it would always need to process that rule (and others) every time data goes in or out.

Now that everything else seems settled, i've been thinking about this tip. If i run ifconfig, I do have a gif0 interface, but it has no IP address bound to it. My VPN's IP is actually bound to en0 as a second IP in addition to the "main" IP. Is this normal, and does VPN traffic still come in through gif0 as far as pf is concerned? Or is my setup different?

.... spoiler alert, I'm actually running OS X 10.11, not true BSD.
 
Status
Not open for further replies.
Back
Top