IPFW Issue with IPFW

Vulpes

New Member


Messages: 2

#1
Hello,

I just starting using FreeBSD and this is my first time using IPFW.

I'm interesting to learn and gain experience to write firewall rules.
I start making a personal project to configure FreeBSD into a router and firewall.

I try to make it as simple as possible to make the firewall and the NAT working
by passing all the traffic in both directions.

What puzzle me is when I test this present configuration I don't have internet connections...

I read in other post on the forum that to make this part work can be a headache.

With the present configuration, I can ping the local IP's and my public IP.
However, I cannot ping other public IP's or URLs.
In the security file logs it shows my DNS server traffic out/in of the WAN Ethernet port.

The traceroute to a URL, show that it is stuck at my gateway IP...

The release version I am using is FreeBSD-11.2

I'm not sure where to look for this issue.
Some guidance will be very appreciated.

--- To begin i run the command ---

kldload ipfw

-------------------------------------------------------
--- Change the options ----

net.inet.ip.forwarding="1"
net.inet.ip.fw.one_pass="1"

-------------------------------------------------------
--- /etc/rc.conf ---
Code:
 hostname="Router"

 gateway_enable="YES"
 ifconfig_igb0="DHCP"

 firewall_enable="YES"
 firewall_nat_enable="YES"
 firewall_nat_interface="ibg0"
 firewall_script="/etc/ipfw.rules"
 firewall_logging="YES"

 ifconfig_igb1="inet 192.168.5.1 netmask 255.255.255.0"
 defaultrouter="192.168.5.1"
 
 sshd_enable="YES"

ntpd_enable="YES"
ntpd_sync_on_start="YES"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
 dumpdev="AUTO"

 sendmail_enable="NONE"
-------------------------------------------------------
--- /etc/ipfw.rules ---
Code:
#!/bin/sh

 ipfw -q -f flush

 cmd="/sbin/ipfw -q add"

 Wan_Nic="igb0"
 Lan_Nic="igb1"
 Lan_Net="192.168.5.0/24"
 DNS_Svr="192.168.5.10"

# Allow all from Loopback
 $cmd 00010 allow all from any to any via lo0

#--- Incoming traffic filter
 $cmd 00100 nat 1 ip from any to any via $Wan_Nic in

 $cmd 00200 check-state

# Skip to outgoing connection
 $cmd 00300 skipto 10000 tcp from any to any via $Wan_Nic out keep-state
 $cmd 00310 skipto 10000 udp from any to any via $Wan_Nic out keep-state

# Allow all local trafic to the router
 $cmd 00400 allow all from $Lan_Net to 192.168.5.1 keep-state

 $cmd 00410 allow log all from any to any keep-state

#--- Outgoing traffic filter
 $cmd 10000 nat 1 ip from any to any via $Wan_Nic out

 $cmd 10100 allow log all from any to any keep-state
--- resolv.conf ---
--- This file is read only. Not to be override by the ISP Dhcp
Code:
 search example.com
 nameserver 192.168.5.10
Thank's for your help
 

SirDice

Administrator
Staff member
Administrator
Moderator

Thanks: 6,228
Messages: 27,205

#2
At quick glance, remove this line:
Code:
$cmd 00100 nat 1 ip from any to any via $Wan_Nic in
You never NAT incoming traffic, only outgoing.
 

Duffyx

Member

Thanks: 14
Messages: 48

#3
You need to set the sysctl net.inet.ip.fw.one_pass="0” in order to get a working statefull nating firewall.
Because after the nat rule has matched it will need to be allowed and thus a reinject in the firewall is necessary.
 

obsigna

Aspiring Daemon

Thanks: 431
Messages: 766

#5
Hello,

I just starting using FreeBSD and this is my first time using IPFW.

I'm interesting to learn and gain experience to write firewall rules.
I start making a personal project to configure FreeBSD into a router and firewall.

I try to make it as simple as possible to make the firewall and the NAT working
by passing all the traffic in both directions.

What puzzle me is when I test this present configuration I don't have internet connections...

I read in other post on the forum that to make this part work can be a headache.

With the present configuration, I can ping the local IP's and my public IP.
However, I cannot ping other public IP's or URLs.
In the security file logs it shows my DNS server traffic out/in of the WAN Ethernet port.

The traceroute to a URL, show that it is stuck at my gateway IP...

The release version I am using is FreeBSD-11.2

I'm not sure where to look for this issue.
Some guidance will be very appreciated.

--- To begin i run the command ---

kldload ipfw

-------------------------------------------------------
--- Change the options ----

net.inet.ip.forwarding="1"
net.inet.ip.fw.one_pass="1"

-------------------------------------------------------
--- /etc/rc.conf ---
Code:
hostname="Router"

gateway_enable="YES"
ifconfig_igb0="DHCP"

firewall_enable="YES"
firewall_nat_enable="YES"
firewall_nat_interface="ibg0"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"

ifconfig_igb1="inet 192.168.5.1 netmask 255.255.255.0"
defaultrouter="192.168.5.1"

sshd_enable="YES"

ntpd_enable="YES"
ntpd_sync_on_start="YES"

# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev="AUTO"

sendmail_enable="NONE"
-------------------------------------------------------
--- /etc/ipfw.rules ---
Code:
#!/bin/sh

ipfw -q -f flush

cmd="/sbin/ipfw -q add"

Wan_Nic="igb0"
Lan_Nic="igb1"
Lan_Net="192.168.5.0/24"
DNS_Svr="192.168.5.10"

# Allow all from Loopback
$cmd 00010 allow all from any to any via lo0

#--- Incoming traffic filter
$cmd 00100 nat 1 ip from any to any via $Wan_Nic in

$cmd 00200 check-state

# Skip to outgoing connection
$cmd 00300 skipto 10000 tcp from any to any via $Wan_Nic out keep-state
$cmd 00310 skipto 10000 udp from any to any via $Wan_Nic out keep-state

# Allow all local trafic to the router
$cmd 00400 allow all from $Lan_Net to 192.168.5.1 keep-state

$cmd 00410 allow log all from any to any keep-state

#--- Outgoing traffic filter
$cmd 10000 nat 1 ip from any to any via $Wan_Nic out

$cmd 10100 allow log all from any to any keep-state
--- resolv.conf ---
--- This file is read only. Not to be override by the ISP Dhcp
Code:
search example.com
nameserver 192.168.5.10
Thank's for your help
The rule for the actual NAT configuration is missing, something like:
/sbin/ipfw -q nat 1 config if $Wan_Nic unreg_only reset
 

VladiBG

Active Member

Thanks: 82
Messages: 235

#6
Stop copy/paste blindly from the internet and read the manual page of ipfw(8) (NAT, REDIRECT AND LSNAT) and https://www.freebsd.org/doc/handbook/firewalls-ipfw.html

A good configuration script on top of which you can build your own script is /usr/share/examples/etc/rc.firewall. <-- Read it it will give you a good starting point.

You are missing the nat configuration in your config
ipfw -q nat 1 config if igb0 log deny_in reset same_ports

When you start the IPFW it will load the kernel module ipfw.ko so you don't have to load it manually.
When you enable gateway_enable="YES" in /etc/rc.conf this will set net.inet.ip.forwarding="1" so you don't have to change it manually ether.
net.inet.ip.fw.one_pass="1" is by default set to 1 and unless you don't need to make some fancy mix queue/piping/fwd and nat you don't have to change it.

When you have firewall_script in /etc/rc.conf you overwrite the /etc/rc.firewall rules so you don't need to have firewall_nat_interface in your /etc/rc.conf

This is the kernel nat part from /usr/share/examples/etc/rc.firewall where the firewall_nat_interface is used.

${fwcmd}nat 123 config log
${fwcmd}add 50 nat 123 ip4 from any to any via ${firewall_nat_interface}



So the first option is to use the rc.firewall script and provide the script with required parameters in your rc.conf file

rc.conf
gateway_enable="YES"
firewall_enable="YES"
firewall_type="open"
firewall_nat_enable="YES"
firewall_nat_interface="ibg0"

#ipfw list
00050 nat 123 ip4 from any to any via igb0
00100 allow ip from any to any via lo0
00200 deny ip from any to 127.0.0.0/8
00300 deny ip from 127.0.0.0/8 to any
00400 deny ip from any to ::1
00500 deny ip from ::1 to any
00600 allow ipv6-icmp from :: to ff02::/16
00700 allow ipv6-icmp from fe80::/10 to fe80::/10
00800 allow ipv6-icmp from fe80::/10 to ff02::/16
00900 allow ipv6-icmp from any to any ip6 icmp6types 1
01000 allow ipv6-icmp from any to any ip6 icmp6types 2,135,136
65000 allow ip from any to any
65535 deny ip from any to any
The other option is to use your own firewall script file (place the rule file in /usr/local/ect/ipfw.rules) then your rc.conf file will look like:

rc.conf
gateway_enable="YES"
firewall_enable="YES"
firewall_nat_enable="YES"
firewall_script="/usr/local/ect/ipfw.rules"


Here is example
Code:
#!/bin/sh

# Flush out the list before we begin

ipfw -q -f flush

# Set rules command prefix

fwcmd="ipfw -q add"

# Populate table1 from a file
#cat /usr/local/etc/ipfw.table1 | xargs -n1 ipfw table 1 add


# Loopback address

$fwcmd 100 allow ip from any to any via lo0
$fwcmd 200 deny ip from any to 127.0.0.0/8
$fwcmd 300 deny ip from 127.0.0.0/8 to any
$fwcmd 400 deny ip from any to ::1
$fwcmd 500 deny ip from ::1 to any

# Deny table 1 list of ip addresses for fail2ban
$fwcmd 550 deny tcp from 'table(1)' to any

# ipv6 ND DAD
$fwcmd 600 allow ipv6-icmp from :: to ff02::/16

# ipv6 RS, RA, NS, NA, redirect
$fwcmd 700 allow ipv6-icmp from fe80::/10 to fe80::/10
$fwcmd 800 allow ipv6-icmp from fe80::/10 to ff02::/16

# Allow ICMPv6 destination unreachable
$fwcmd 900 allow ipv6-icmp from any to any ip6 icmp6types 1

# Allow NS/NA/toobig (don't filter it out)
$fwcmd 1000 allow ipv6-icmp from any to any ip6 icmp6types 2,135,136

# Allow packets for which a state has been build
$fwcmd 1100 check-state

# For services permitted below
$fwcmd 1200 allow tcp from me to any established

# Allow any connection out, adding state for each
$fwcmd 1300 allow tcp from me to any setup keep-state
$fwcmd 1400 allow udp from me to any keep-state
$fwcmd 1500 allow icmp from me to any keep-state
$fwcmd 1600 allow ipv6-icmp from me to any keep-state

# Allow DHCP
$fwcmd 1700 allow udp from 0.0.0.0 68 to 255.255.255.255 dst-port 67 out
$fwcmd 1800 allow udp from any 67 to me dst-port 68 in
$fwcmd 1900 allow udp from any 67 to 255.255.255.255 dst-port 68 in
$fwcmd 2000 allow udp from fe80::/10 to me dst-port 546 in

# ICMP echo8,DestUnreacable3,SourcheQuench4,TimeExceeded11
$fwcmd 2100 allow icmp from any to me icmptypes 8
$fwcmd 2200 allow ipv6-icmp from any to me ip6 icmp6types 128,129
$fwcmd 2300 allow icmp from any to me icmptypes 3,4,11
$fwcmd 2400 allow ipv6-icmp from any to me ip6 icmp6types 3

# MyServices Accept and log ssh on 22
$fwcmd 2500 allow log logamount 100 tcp from any to me dst-port 22 setup
$fwcmd 2510 allow tcp from any to me dst-port 22
$fwcmd 2600 allow tcp from any to me dst-port 80
$fwcmd 2700 allow tcp from any to me dst-port 443
#$fwcmd 2800 allow tcp from any to me dst-port 25
#$fwcmd 2900 allow tcp from any to me dst-port 465
#$fwcmd 3000 allow tcp from any to me dst-port 587
#$fwcmd 3100 allow tcp from any to me dst-port 110
#$fwcmd 3200 allow tcp from any to me dst-port 993
#$fwcmd 3300 allow tcp from any to me dst-port 143
#$fwcmd 3400 allow tcp from any to me dst-port 995

# Allow traffic via LAN interface
$fwcmd 5000 allow ip from any to any via igb1

# NAT with deny_in must be after allowed rules from above anything after this line will be denied if no matching nat is found.
ipfw -q nat 1 config if igb0 log deny_in reset same_ports
$fwcmd 55000 nat 1 all from any to any via igb0

# Accounting
$fwcmd 65000 count ip from any to any

# Drop Packets to ports where we don't want logging
#$fwcmd 65100 deny { tcp or udp } from any to any dst-port 23,135-139,445 in
#$fwcmd 65200 deny { tcp or udp } from any to any dst-port 1026,1027 in
#$fwcmd 65300 deny { tcp or udp } from any to any dst-port 1433,1434 in

# Broadcasts and multicasts
#$fwcmd 65400 deny ip from any to 255.255.255.255
#$fwcmd 65510 deny ip from any to 224.0.0.0/24 in

# Noise from routers
#$fwcmd 65520 deny udp from any to any dst-port 520 in

# Noise from webbrowsing
#$fwcmd 65530 deny tcp from any 80,443 to any dst-port 1024-65535 in

# Log and drop all other ports
#$fwcmd 65534 deny log logamount 500 ip from any to any

# Default rule DROP, this is set by default so it's not needed
#$fwcmd 65535 deny ip from any to any
 

VladiBG

Active Member

Thanks: 82
Messages: 235

#8
Only if you have some VPN traffic that you want to pass inside your network without apply any NAT then you can split incoming/outgoing NAT rule and using "not table(0)" to skip the NAT.
 

obsigna

Aspiring Daemon

Thanks: 431
Messages: 766

#9
Only if you have some VPN traffic that you want to pass inside your network without apply any NAT then you can split incoming/outgoing NAT rule and using "not table(0)" to skip the NAT.
You need to split incoming and outgoing NAT rules as well in order to stateful rules for outgoing traffic of clients behind the gateway do work, and in addition as Duffyx mentioned the syctl(8) parameter net.inet.ip.fw.one_pass needs to be set to 0. Otherwise returned packets from the outside are not matched to the actual initiator of the traffic.
 

obsigna

Aspiring Daemon

Thanks: 431
Messages: 766

#10
Is it? Bugger, I really need to read up on ipfw(8).
This is not explained well in ipfw(8). For an explanation see: https://www.freebsd.org/doc/en_US.ISO8859-1/books/handbook/firewalls-ipfw.html#network-natd, and here specially the following – for convenience of reading, I added comments in <purple>:
Consider an internal web browser which initializes a new outbound HTTP session over port 80. When the first outbound packet enters the firewall, it does not match rule 100 <incoming nat> because it is headed out rather than in. It passes rule 101 <check state> because this is the first packet and it has not been posted to the dynamic state table yet. The packet finally matches rule 125 <allow outgoing http by skipping to the outgoing nat> as it is outbound on an allowed port and has a source IP address from the internal LAN. On matching this rule, two actions take place. First, the keep-state action adds an entry to the dynamic state table and the specified action, skipto rule 500 <outgoing nat>, is executed. Next, the packet undergoes NAT and is sent out to the Internet. This packet makes its way to the destination web server, where a response packet is generated and sent back. This new packet enters the top of the ruleset. It matches rule 100 <incoming nat> and has its destination IPaddress mapped back to the original internal address. It then is processed by the check-state rule, is found in the table as an existing session, and is released to the LAN.
In the handbook the old NAT method (diverting to natd) is described, however, this setup works perfectly with the in-kernel NAT as well, only a few minor modifications are needed.
 

VladiBG

Active Member

Thanks: 82
Messages: 235

#11
With in-kernel NAT you have deny_in option. Any random packets from internet for which there's no matching NAT connection will be dropped.
 
OP
OP
V

Vulpes

New Member


Messages: 2

#12
Thank you for all your responses.

I’ve had done a fresh installation.

This time I try the option firewall_type="open"

I have figured out why I cannot ping URL's.

The problem was in the file rc.conf -> defaultrouter="192.168.5.1"
After I commented this line I was able having an internet connection.

Now that it works it make sense.
The gateway of the LAN is 192.168.5.1
The gateway of the router is the IPS IP not 192.168.5.1...

The hard part for me about writing rules will be to know where I am sitting on the firewall perceptive.

Thanks for all the information's.
I will read more attentively the handbook website, and the file in /usr/share/examples/etc/rc.firewall
and the firewall example above.

Now is time to experiment writing rules.
 
Top