The recent NTP reflection incident I was a victim of woke me up to the need for securing my FreeNAS boxes from outside connections. Luckily, FreeNAS 9.2.1.2 comes shipped with the kernel extension for pf, so getting it working is pretty easy.
DISCLAIMER – I AM NOT A SECURITY EXPERT, FOLLOW THIS GUIDE AT YOUR OWN RISK
I’ve been told that pf does not play nice with vimage jails. It tends to cause kernel panics.
Get the basics done
1. Mount the filesystem so we can make some changes.
su enter your root password mount -uw /
2. Figure out what interfaces you have active so we can add them to the firewall rules.
ifconfig
Your output should look something like this:
re0: flags=8943<up,broadcast,running,promisc,simplex,multicast> metric 0 mtu 1500
options=82099<RXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM,WOL_MAGIC,LINKSTATE>
ether f4:6d:04:db:21:ba
inet 192.168.0.10 netmask 0xffffff00 broadcast 192.168.0.255
inet6 xxxx::xxxx:xxxx:xxxx:xxxx%re0 prefixlen 64 scopeid 0x6
inet xxx.xxx.xxx.xxx netmask 0xfffffff8 broadcast 50.241.46.71
nd6 options=23<performnud,accept_rtadv,auto_linklocal>
media: Ethernet autoselect (1000baseT )
status: activeipfw0: flags=8801<up,simplex,multicast> metric 0 mtu 65536
nd6 options=9<performnud,ifdisabled>lo0: flags=8049<up,loopback,running,multicast> metric 0 mtu 16384
options=600003<RXCSUM,TXCSUM,RXCSUM_IPV6,TXCSUM_IPV6>
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0xa
inet 127.0.0.1 netmask 0xff000000
nd6 options=21<performnud,auto_linklocal>bridge0: flags=8843<up,broadcast,running,simplex,multicast> metric 0 mtu 1500
ether 02:df:7f:1c:ff:00
nd6 options=1
id 00:00:00:00:00:00 priority 32768 hellotime 2 fwddelay 15
maxage 20 holdcnt 6 proto rstp maxaddr 2000 timeout 1200
root id 00:00:00:00:00:00 priority 32768 ifcost 0 port 0
member: epair0a flags=143<learning,discover,autoedge,autoptp>
ifmaxaddr 0 port 12 priority 128 path cost 2000
member: re0 flags=143<learning,discover,autoedge,autoptp>
ifmaxaddr 0 port 6 priority 128 path cost 20000epair0a: flags=8943<up,broadcast,running,promisc,simplex,multicast> metric 0 mtu 1500
options=8<VLAN_MTU>
ether 02:09:09:00:0c:0a
nd6 options=1
media: Ethernet 10Gbase-T (10Gbase-T )
status: activetun0: flags=8051<up,pointopoint,running,multicast> metric 0 mtu 1500
options=80000<LINKSTATE>
inet 10.8.0.1 –> 10.8.0.2 netmask 0xffffffff nd6 options=1
Opened by PID 17528
What we want to note are all the interface names. Mine are re0, ipfw0, lo0, bridge0, epair0a and tun0
Create the firewall rules
3. Create the pf.conf file and edit it to your needs. We are going to put it on our data drive so future FreeNAS upgrades don’t wipe it out. Adjust the path to match your setup.
mkdir /mnt/Files/hacks vi /mnt/Files/hacks/pf.conf
Side note, vi has lots of commands, we just need to know a few:
x will delete the character your cursor is over, i will insert, esc will exit insert mode and to save and quit we use :wq
Here are the rules I am running.
#change this to match your primary ethernet interface, re0 or em0 are common, but there are others ext_if="re0" vpn_if="tun0" table <bruteforce> persist #These are all of the other interfaces we discovered in step 2 set skip on lo0 set skip on bridge0 set skip on ipfw0 set skip on epair0a set skip on tun0 set block-policy return scrub in all #change xxx.xxx.xxx.xxx to the external IP of your FreeNAS box nat on $ext_if from 10.8.0.0/24 to any -> xxx.xxx.xxx.xxx #Lock it down block in all block out all #Allow VPN traffic pass on tun0 keep state block quick from <bruteforce> #Allow traffic in for ssh pass in on $ext_if proto tcp from any to any port 22 flags S/SA keep state (max-src-conn 10, max-src-conn-rate 5/5, overload <bruteforce> flush global) #Allow traffic in for web - delete or comment out if you don't want web traffic pass in on $ext_if proto tcp from any to any port 80 flags S/SA keep state pass in on $ext_if proto tcp from any to any port 443 flags S/SA keep state #Allow traffic in for OpenVPN pass in on $ext_if proto udp from any to any port 1194 keep state label "openvpn" #Allow LAN traffic to connect to FreeNAS - change xxx.xxx.xxx.0 to match your network, ie 192.168.0.0 or 10.0.0.0 pass in on $ext_if from xxx.xxx.xxx.0/24 to any keep state #Allow traffic out from the LAN pass out on $ext_if from any to any keep state
Enable the Firewall
4. Edit /etc/rc.conf and add the following.
vi /etc/rc.conf
pf_enable="YES"
pf_rules="/mnt/Files/hacks/pf.conf"
gateway_enable="YES"
5. Start up the firewall and see if it works
service pf start
Your should get this as your output:
Enabling pf
No ALTQ support in kernel
ALTQ related functions disabled
Now check to make sure it is working:
service pf status
Your output should be something like this:
No ALTQ support in kernel
ALTQ related functions disabled
Status: Enabled for 0 days 00:04:55 Debug: UrgentState Table Total Rate
current entries 29
searches 1040038 3525.6/s
inserts 95 0.3/s
removals 109 0.4/s
Counters
match 1093 3.7/s
bad-offset 0 0.0/s
fragment 0 0.0/s
short 0 0.0/s
normalize 0 0.0/s
memory 0 0.0/s
bad-timestamp 0 0.0/s
congestion 0 0.0/s
ip-option 22 0.1/s
proto-cksum 0 0.0/s
state-mismatch 0 0.0/s
state-insert 0 0.0/s
state-limit 0 0.0/s
src-limit 0 0.0/s
synproxy 0 0.0/s
6. Start IP Forwarding without restarting your server
sysctl net.inet.ip.forwarding=1
Then make sure to go into the FreeNAS gui, click on System, Click on Sysctls. Then add that same value so it survives reboots.
Make your changes stick
7. Add your rc.conf changes to /conf/base/etc/rc.conf
vi /conf/base/etc/rc.conf
pf_enable="YES"
pf_rules="/mnt/Files/hacks/pf.conf"
gateway_enable="YES"
To really make these changes stick, follow my guide on the subject.
Clean Up
8. Make the filesystem read only again
mount -ur /
Final Thoughts
An added benefit of setting up a firewall this way is that it will let you route to other computers on your LAN over your VPN. Hope you all enjoy this and let me know how things work out for you.
Pingback: OpenVPN on FreeNas 9.1 | The Joe Paetzel Method
After activating the firewall I got a syntax error on my pf.conf file. The status says is running and I don’t see any problems in the file. Any idea why this happened?
That’s hard to say without seeing the pf.conf file you are using.
#change this to match your primary ethernet interface, re0 or em0 are common, but there are others
ext_if=”em0″
vpn_if=”tun0″
#These are all of the other interfaces we discovered in step 2
set skip on re0
set skip on lo0
set skip on bridge0
set skip on ipfw0
set skip on epair0a
set skip on epair1a
set skip on epair2a
set skip on epair3a
set skip on epair4a
set skip on epair5a
set skip on tun0
set block-policy return
scrub in all
#change xxx.xxx.xxx.xxx to the external IP of your FreeNAS box
nat on $ext_if from 10.8.0.0/24 to any -> 192.168.1.8
#Lock it down
block in all
block out all
#Allow VPN traffic
pass on tun0 keep state
block quick from
#Allow traffic in for ssh
pass in on $ext_if proto tcp from any to any port 22 flags S/SA keep state
#Allow traffic in for web – delete or comment out if you don’t want web traffic
pass in on $ext_if proto tcp from any to any port 80 flags S/SA keep state
pass in on $ext_if proto tcp from any to any port 443 flags S/SA keep state
#Allow traffic in for OpenVPN
pass in on $ext_if proto udp from any to any port 1194 keep state label “openvpn”
#Allow LAN traffic to connect to FreeNAS – change xxx.xxx.xxx.0 to match your network, ie 192.168.0.0 or 10.0.0.0
pass in on $ext_if from 192.168.1.0/24 to any keep state
#Allow traffic out from the LAN
pass out on $ext_if from any to any keep state
Looks like you dropped all the brute force stuff. After “pass on tun0 keep state”…delete “block quick from”.
I was troubleshooting that is why I took out the brute force things. I added it back in and deleted that line.
I just stopped and started it with the changes and there is still a syntax error.
Ok the syntax error was pointing at the line that has the brute force info. Once I deleted it, the error went away. Is there in an error above?
Yes, there was an error. WordPress decided not to “print” the parts. It should be this…I fixed it above:
table <bruteforce> persist
block quick from <bruteforce>
Thanks for catching this!!!
I think you are also missing a <bruteforce> in the port 22 line after overload also
Yes…darn wordpress. Fixed it above…should be this:
pass in on $ext_if proto tcp from any to any port 22 flags S/SA keep state (max-src-conn 10, max-src-conn-rate 5/5, overload <bruteforce> flush global)
Hi Joe!
First off, thank you for your OpenVPN tutorials. They have been extremely informative and well written, thank you for taking the time to make them. I couldn’t imagine setting up my system without them.
However, like most posters I imagine, I’m here because I encountered problems and I lack the technical expertise to fix them on my own. I’ve followed your guide as closely as possible but I’ve failed to successfully implement the packet filter. It runs and I have no issue with internet access from the VPN client device but I’m unable to reach devices on the local lan. When checking the pf status I see zero movement of packets! Is this because the lan is behind a router?
Thanks again! 🙂
Hi Tristan –
I really appreciate the kind words regarding the guides!
So help me understand. Is the FreeNAS box behind the same router? What type of port forwarding are you doing on the router? Could you email me your output from ifcong , and your pf.conf file? You can use the email form on the about page. Might as well include the openvpn server config file. Feel free to xxx.xxx.xxx.xxx out the ip’s.
Done! Thanks Joe, it’s really appreciated. I’m looking forward to hearing back from you!
Fabulous, what a weblog it is! This website presents helpful facts to us,
keep it up.
A superb guide! I had been struggling with getting this done for ages and it worked flawlessly.
A small tip in case you would like to route all your traffic over the VPN server once connected, including that to the internet is to add these 2 lines to your openvpn.conf file:
push “redirect-gateway def1”
push “dhcp-option DNS 8.8.8.8”
Chose your own DNS server by all means, this is a Google one.
Hi Joe,
First of all, great tutorial 🙂
I’ve followed the OpenVPN one, and then i wanted to configure some firewall rules.
I’ve followed all of the tutorial,yet the openVPN port is still CLOSED from outside.
Have you any idea why it’s happening ?
Thanks in advance,
Regards,
Bastien DINE
Hi there-
I assume you forwarded the port through your router. The VPN works without the firewall rules in place…correct? Do you have the correct interface in your ruleset? Might be em0, re0…or a slew of other possibilities.
Also, thanks for the compliment and thanks for reading.
Hi Joe, I have a question.
I followed your guide and everything goes smoothly but when I reboot my FreeNAS I cannot access to my Plugins, I need to stop pf and start again, and then everything works.
Do you know why this is happening?
Maybe it’s a hardware issue?
It’s like the system needs to load first the interfaces for each plugin and then start the firewall.
Thanks for all your blog 🙂
Of and vimage jails don’t get along. Sounds like an issue with that.
Question about:
“change xxx.xxx.xxx.xxx to the external IP of your FreeNAS box”
Is that referring to the LAN IP it’s getting from the router, or referring to actual external IP (WAN)?
If you are passing an IP through then it is the public IP for the FreeNAS box. If you are forwarding a port on the router, then it is the LAN IP for FreeNAS. If you are forwarding the port, then on the client config, be sure to use the public IP of your router.
Hmmm, just realized which post you were commenting on. My answer probably didn’t make a lot of sense. I had assumed if you are setting up a firewall, that you have a public IP assigned to your FreeNAS box. I guess it would probably work without one. So yeah, you should be able to use an internal IP. Long story short, it needs to be an IP on the FreeNAS box.
Thanks =] Your first reply answered it, I am forwarding 1194 within OpenWRT to the LAN IP and have the server.conf setup to use the ddns host name for the WAN IP
(As a heads up, I just recently delved into UNIX based OSes and am learning as I go)
Also, i wanted to understand what each command meant, so I looked at the rule syntax page, and according to the description of pf (link below), it states:
“Filter rules are evaluated in sequential order, first to last. Unless the packet matches a rule containing the quick keyword, the packet will be evaluated against all filter rules before the final action is taken. The last rule to match is the “winner” and will dictate what action to take on the packet.”
I take this to mean that it starts at the top of script and works it way down, so does this mean the default of deny all should be at the bottom of the script instead of the top? Thanks =]
Hi James. Read that snippet again. Last rule to match is the winner. So if you end with Deny All, no packets would get through. The exception are any “quick” rules.
Thanks a bunch =]
I have a server board with 2 LANS and an IPMI LAN… can I simply add the additional LAN on the next line below the first, such as:
ext_if=”igb0″
ext_if=”igb1″
vpn_if=”tun0″
vpn_net='”10.8.0.0/24″‘
table persist
Nope. I don’t think so. ext_if is a variable, so by defining it twice, my guess is, only the last setting would stay. So in your example, ext_if = igb1
Not sure if you can define multiple interfaces with commas. I’d say, just use ext_if and ext2_if.
Are all three NIC’s being used? You probably only need to set up one of them for your standard rules and then the IPMP NIC you’d want separate rules for anyway.
Currently only eth1 and the IPMI LAN are being utilized. Once I get all the wiring ran (server will be in different board than the router) all three LANs will be hooked up with aggregation on eth0 and eth1 and the IPMI bound to its own port
**room, not board lol
OK, looks like this is maybe the way you’d want things:
ext_if = “igb0”
ext_if2 = “igb1”
all_ext_if = “{” $ext_if $ext_if2 “}”
Then use $all_ext_if anywhere you want a rule to refer to both interfaces.
Might also work to just do:
ext_if = “{ igb0, igb1}”
You’ll need to test that out.
Thanks a bunch =]
int_if=”em0″
localnet = $int_if:network
pass in on $int_if from $localnet to any keep state
now no need to edit to match your network
Thanks! =]
Thank you for this wonderfull tutorial – again 🙂
One error has been bugging me for an houre, maybe because I am some kind of noob.
Whenever I fired the pf up I got an error message on the last line of the pf.conf file, no matter what it contains.
I turns out that I edited the conf-file with Adobe Dreamweaver and had to add an extra empty line at the end of the file.
Maybe you can right it as an additional info for someone like me who has the same
problems.
Pingback: Configurando packet filtering en FreeNAS 9.3 | Tutoriales CECT
Hey, thank you very much for this!
I found this, because I was wondering, if it can be used to make the built-in TFTP-server a little bit more secure. I only need TFTP availably for a certain IP range in the network (like 10.0.0.20 – 10.0.0.30). In this range IP-Phones reside and should be able to load their config files – no other network client outside of the range should be able to do this.
Is it possible to adapt the code for this application?
Thanks a lot for your help and have a Merry Christmas!
Hi Michael-
I have not played with FreeNAS for a very long time. All of the custom stuff I did was on now, very old versions. I have no way of knowing if this will work on a current version of FreeNAS. All that said,
you should be able to do that. TFTP runs on port 69 by default. To specify a range of IP’s you just use a hyphen, so 10.0.0.20 – 10.0.0.30 .
So something like this might work:
#Allow traffic in from phones for TFTP
pass in on $ext_if from 10.0.0.20 – 10.0.0.30 to any port 69 flags S/SA keep state (max-src-conn 10, max-src-conn-rate 5/5, overload flush global)
Again, I’m no expert and do not know if any of my posts work at all with more recent versions of FreeNAS.
Hi, thank you very much for your quick reply and a happy new year. 😉