[RFC][patch] Two new actions: state-allow and state-deny

Julian Elischer julian at freebsd.org
Tue Feb 3 06:44:56 UTC 2015


On 2/3/15 3:17 AM, Lev Serebryakov wrote:
>
>   I propose two new actions: state-allow and state-deny.
>
>   They imply "keep-state" and create new dynamic rules, when called
> directly, but pass packet to NEXT rule after that (don't stop search).
>
>   When they are called as dynamic rule, they acts as "allow" and "deny".
>
>   So, stateful firewall with NAT could be rewritten like this:
>
> add 1000 skipto 2000 all from any to any out xmit outIface
> add 1010 skipto 3000 all from any to any in  recv outIface
>
> add 2000 state-allow from any to any // keep-state is implied
> add 2010 nat NR from any to any // No "out" here!
> add 2020 allow all from any to any
>
> add 3000 nat NR from any to any
> add 3010 check-state // Use dynamic rule based on 2000 as "allow" here
>
>   What do you think?

I understand what you are trying to do..
but part of the usefulness of the state runes is that they
can be any action, not just allow and deny.
I might try the following action:

record:  (or record-only)
it allows the rule to be stored, but the action is not performed.
on check-state, the rule is performed..

so skipto 2000 ip from me to fred 80 record-only out xmit bge0
would do nothing but record the session
and on input the session packet would skipto 2000

you could do deny or accept  as well so it's a superset of what you 
suggest.
I'm not sure where in the rule record-only shoudl be put..
maybe

ipfw add 1000 log record-only skipto 2000 tcp from me to fred 80,443 
out xmit bge0

might be more syntactically correct?

What I would find more useful, is separate state rules for each interface.
so you could not have the danger of a packet on interface A adding a 
rule that eventually does something unexpected on a packet on interface B.


looking at my own rules I don't seem to have a problem..
--------
Just for kicks,
here is the base ipfw script.. just sets up the framework

---
#!/bin/sh

fwcmd="/sbin/ipfw"

# Suck in the configuration variables.
if [ -z "${source_rc_confs_defined}" ]; then
         if [ -r /etc/defaults/rc.conf ]; then
                 . /etc/defaults/rc.conf
                 source_rc_confs
         elif [ -r /etc/rc.conf ]; then
                 . /etc/rc.conf
         fi
fi


         # set these to your outside interface network and netmask and ip
         oif="tun0"
         onet="192.168.36.0"
         omask="24"
         oip="x.x.x.x"


         # set these to your inside interface network and netmask and ip
         iif="vr0"
         inet="192.168.2.0"
         imask="255.255.255.0"
         iip="192.168.2.21"

         # for not the natd target is us but change this if you
         # change that in natd.conf
         natd_target=${oip}
         work_vpnserver=y.y.y.y

INCOMING=4000
OUTGOING=8000
LOCAL=1000
SET=1

         sysctl net.inet.ip.fw.enable=0
         ${fwcmd} -q flush
         ${fwcmd} -q table 1 flush
         ${fwcmd} -q table 2 flush
         ${fwcmd} -q table 3 flush
         ${fwcmd} -q table 4 flush

         ${fwcmd} table 1 add 10.0.0.0/8
         ${fwcmd} table 1 add 172.16.0.0/12
         #${fwcmd} table 1 add 192.168.0.0/16
          # Stop draft-manning-dsua-03.txt (1 May 2000) nets (includes
          # RESERVED-1, DHCP auto-configuration, NET-TEST, MULTICAST 
(class D),
          # and class E) on the outside interface
         ${fwcmd} table 1 add 0.0.0.0/8
         ${fwcmd} table 1 add 169.254.0.0/16
         ${fwcmd} table 1 add 192.0.2.0/24
         ${fwcmd} table 1 add 224.0.0.0/4
         ${fwcmd} table 1 add 240.0.0.0/4

         # add legit sources of ssh.. DNS is not up yet so use IPs
         # could add to /etc/hosts I guess.
         #      myotherhouse.org
         ${fwcmd} table 2 add a.b.c.d
         #      work
         ${fwcmd} table 2 add c.d.e.f/24
         #      my vps
         ${fwcmd} table 2 add f.g.h.i/24

         # add legit DNS tcp (zone) sources
         #      my.dns.friends
         ${fwcmd} table 3 add m.n.o.p
         #      my.dns.friend
         ${fwcmd} table 3 add q.r.s.t
         #      my.vps
         ${fwcmd} table 3 add f.g.h.i

         # Add our local networks here
         ${fwcmd} table 4 add 192.168.2.0/24
         ${fwcmd} table 4 add 172.16.15.0/24

# common spoofing code
# --------------- ALL PACKETS START HERE. ------------

         ${fwcmd} set disable ${SET}

         # Stop localhost spoofing
         ${fwcmd} add 100 pass all from any to any via lo0
         ${fwcmd} add 200 deny log all from any to 127.0.0.0/8
         ${fwcmd} add 300 deny log ip from 127.0.0.0/8 to any

         # If we've already decided on it. keep our word.
         ${fwcmd} add check-state

#-------- Interception rules for external interfaces go here -------

#-------- Internal traffic. generally don't care
         # except to stop spoofing.
         # make extra sure we don't block DHCP to our server (me)
         # as initial request will be from 0.0.0.0/0

         ${fwcmd} add ${LOCAL} set ${SET} allow udp from any to any 67 
in recv ${iif}
         ${fwcmd} add allow udp from any 67 to any out xmit ${iif}

         # other wise it has to be to and from a net we actually have.
         ${fwcmd} add deny log all from not "table(4)" to any in recv 
${iif}
         ${fwcmd} add deny log all from any to not "table(4)" out xmit 
${iif}

         ${fwcmd} add reject log tcp from "table(5)" to any 80,443 in 
recv vr0

         ${fwcmd} add allow ip from any to any



         sysctl net.inet.ip.fw.enable=1

--------
and now the rules for my external interface..
you run this after the other file. note that the rules go into a 
separate set
and can be disabled at once.

#!/bin/sh
set -x
fwcmd="/sbin/ipfw"

# Suck in the configuration variables.
if [ -z "${source_rc_confs_defined}" ]; then
         if [ -r /etc/defaults/rc.conf ]; then
                 . /etc/defaults/rc.conf
                 source_rc_confs
         elif [ -r /etc/rc.conf ]; then
                 . /etc/rc.conf
         fi
fi

         # set these to your outside interface network and netmask and ip
         oif="vr2"
         oip="$1"
         onet="$2"
         omask="$3"


         # set these to your inside interface network and netmask and ip
         iif="vr0"
         inet="192.168.2.0"
         imask="255.255.255.0"
         iip="192.168.2.21"

         # for not the natd target is us but change this if you
         # change that in natd.conf
         natd_target=${oip}
         work_vpnserver=64.244.102.15

INTERCEPT=610
INCOMING=14000
OUTGOING=18000
NATD2=8669
SET=2

         sysctl net.inet.ip.fw.enable=0
         ${fwcmd} -q delete ${INTERCEPT}
         ${fwcmd} set disable ${SET}
         # effectively clear it
         ${fwcmd} set move 30 to ${SET}


# common spoofing code
# --------------- ALL PACKETS START HERE. ------------


         ${fwcmd} add ${INTERCEPT} set ${SET} skipto ${INCOMING} ip 
from any to any in recv ${oif}
         ${fwcmd} add ${INTERCEPT} set ${SET} skipto ${OUTGOING} ip 
from any to any out xmit ${oif}

#-------- Internal traffic. generally don't care
Add in a new incoming and outgoing section for the new interface

#------- INCOMING
         # don't allow packets from the wrong net!
         ${fwcmd} add ${INCOMING} set ${SET} deny log all from 
"table(4)" to any

         # in fact don't accept packets that are not for this 
interface exactly
         ${fwcmd} add set ${SET} deny log ip from any to not ${oip}

         # Allow access to our ssh from trusted places (work, freebsd 
(sometimes))
         ${fwcmd} add set ${SET} pass tcp from "table(2)" to ${oip} 22 
setup keep-state

         # allow our DNS secondaries to get zone transfers
         ${fwcmd} add set ${SET} pass tcp from "table(3)" to ${oip} 53 
setup keep-state

         # allow DNS requests, since we are authoratitive
         ${fwcmd} add set ${SET} pass udp from any to ${oip} 53

         # Allow setup of incoming email # not on this interface

         # me, DNS and root can start outgoing sessions and have
         # them come in if there is a waiting socket
         #  !!@#  Don't do this it breaks ntp from inside
#       ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 0
#       ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 53
#       ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 1000

         # ignore any mention of RFC1918 nets on the outside interface
         ${fwcmd} add set ${SET} deny log all from any to "table(1)"
         # except the
         ${fwcmd} add set ${SET} deny log not icmp from "table(1)" to any

#^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v INCOMING NAT POINT 
^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v
         # NAT anything that is left and trust NATD
         ${fwcmd} add set ${SET} divert ${NATD2} all from any to any

#After translation.. trust Nat to filter non matches

         # explicitly allow NAT-T from the vpn server to inside nets
         ${fwcmd} add set ${SET} allow udp from ${work_vpnserver} to 
"table(4)"

         # Allow TCP through if setup succeeded .
         # bypass the logging step. too much data
         ${fwcmd} add set ${SET} allow tcp from any to any established

         # take note of unexpected stuff. then drop it.
         # hmm, NAtd must have let this through  why?
         ${fwcmd} add set ${SET} drop log ip from any to ${natd_target}

         # Allow IP fragments to pass through  (NOPE)
         # ${fwcmd} add set ${SET} pass all from any to any frag

         # XXX remove this if you turn on the target option on
         # natd to allow a server
         # Reject & Log all setup of incoming connections from the outside
         # that have not been explicitly allowed above.
         ${fwcmd} add set ${SET} deny log tcp from any to 
${natd_target} setup

         # anything here should be logged. it's intersting.
         ${fwcmd} add set ${SET} count log ip from any to any

         # after that gauntlet, allow it to proceed.
         ${fwcmd} add set ${SET} allow ip from any to any



#----- OUTGOING
         # Stop RFC1918 nets getting out to the outside interface
         # except for the wierdness of our next hop being such an address.
         ${fwcmd} add ${OUTGOING} set ${SET} allow icmp from ${oip} to 
${onet}:${omask} keep-state
         ${fwcmd} add set ${SET} deny log all from any to "table(1)"

         # The firewall and inside can talk out if it wants to.
         # these are local sessions by definition.
         ${fwcmd} add set ${SET} pass all from ${oip} to any keep-state

#^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v OUTGOING NAT POINT 
^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v
         ${fwcmd} add set ${SET} divert ${NATD2} all from any to any 
out recv ${iif}

         # just in case natd goes wierd.
         ${fwcmd} add set ${SET}  deny log all from "table(1)" to any
         # in fact don't allow packets out  that are not from this 
interface exactly
         ${fwcmd} add set ${SET} deny log ip from not ${oip} to any
         ${fwcmd} add set ${SET} allow all from any to any

         ${fwcmd} set enable ${SET}
         sysctl net.inet.ip.fw.enable=1

and here's rules for un untrusted tunnel
this file can also be run after the main one when hte tunnel is in use.
I'm not using this one at the moment, so I'm not sure it works but you 
can get the idea..
I add a separate script file for each interface.. and they slot in 
separate rules as needed.

#!/bin/sh
fwcmd="/sbin/ipfw"

# Suck in the configuration variables.
if [ -z "${source_rc_confs_defined}" ]; then
         if [ -r /etc/defaults/rc.conf ]; then
                 . /etc/defaults/rc.conf
                 source_rc_confs
         elif [ -r /etc/rc.conf ]; then
                 . /etc/rc.conf
         fi
fi



         # set these to your inside interface network and netmask and ip
         iif="vr0"
         inet="192.168.2.0"
         imask="255.255.255.0"
         iip="192.168.2.21"

         # set these to your outside interface network and netmask and ip
         oif="tun0"
         onet="192.168.36.0"
         omask="24"
         oip="l.m.n.o"
INTERCEPT=500
INCOMING=4000
OUTGOING=8000
SET=1

         # for now the natd target is us but change this if you
         # change that in natd.conf
         natd_target=${oip}
         work_vpnserver=t.u.v.w

         sysctl net.inet.ip.fw.enable=0

#-------- Select direction and interface class
         ${fwcmd} add ${INTERCEPT} set ${SET} skipto ${INCOMING} ip 
from any to any in recv ${oif}
         ${fwcmd} add ${INTERCEPT} set ${SET} skipto ${OUTGOING} ip 
from any to any out xmit ${oif}


#------- INCOMING
         # don't allow packets from the wrong net!
         ${fwcmd} add ${INCOMING} set ${SET} deny log all from 
"table(4)" to any

         # in fact don't accept packets that are not for this 
interface exactly
         ${fwcmd} add set ${SET} deny log ip from any to not ${oip}

         # Allow access to our ssh from trusted places (work, freebsd, 
(sometimes))
         ${fwcmd} add set ${SET} pass tcp from "table(2)" to ${oip} 22 
setup keep-state

         # allow our DNS secondaries to get zone transfers
         ${fwcmd} add set ${SET} pass tcp from "table(3)" to ${oip} 53 
setup keep-state

         # allow DNS requests, since we are authoratitive
         ${fwcmd} add set ${SET} pass udp from any to ${oip} 53

         # Allow setup of incoming email

         # julian and root can start outgoing sessions and have them 
come in if there is a waiting socket :-)
         ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 0
         ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 53
         ${fwcmd} add set ${SET} allow ip from any to ${oip} uid 1000

         # ignore any mention of RFC1918 nets on the outside interface
         ${fwcmd} add set ${SET} deny log all from any to "table(1)"
         ${fwcmd} add set ${SET} deny log not icmp from "table(1)" to any

#^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v INCOMING NAT POINT 
^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v
         # NAT anything that is left and trust NATD
         ${fwcmd} add set ${SET} divert natd all from any to any

#After translation

         # explicitly allow NAT-T from the vpn server to inside nets
         ${fwcmd} add set ${SET} allow udp from ${work_vpnserver} to 
"table(4)"

         # Allow TCP through if setup succeeded .
         # bypass the logging step. too much data
         ${fwcmd} add set ${SET} allow tcp from any to any established

         # take note of unexpected stuff. then drop it.
         ${fwcmd} add set ${SET} drop log ip from any to ${natd_target}

         # Allow IP fragments to pass through  (NOPE)
         # ${fwcmd} add set ${SET} pass all from any to any frag

         # XXX remove this if you turn on the target option on
         # natd to allow a server
         # Reject & Log all setup of incoming connections from the outside
         # that have not been explicitly allowed above.
         ${fwcmd} add set ${SET} deny log tcp from any to 
${natd_target} setup

         # anything here should be logged. it's interesting.
         ${fwcmd} add set ${SET} count log ip from any to any

         #currently slam that door .. this rule comes and goes 
depending on if I need it.
         ${fwcmd} add set ${SET} deny log ip from any to any

         # after that gauntlet, allow it to proceed.
         ${fwcmd} add set ${SET} allow ip from any to any



#----- OUTGOING
         # Stop RFC1918 nets getting out to the outside interface
         # except for the wierdness of our next hop being such an address.
         ${fwcmd} add ${OUTGOING} set ${SET} allow icmp from ${oip} to 
${onet}/${omask} keep-state
         ${fwcmd} add set ${SET} deny log all from any to "table(1)"

         # The firewall (and my laptop) can talk out if it wants to.
         # these are local sessions by definition.
         ${fwcmd} add set ${SET} pass all from ${oip} to any keep-state
#       ${fwcmd} add set ${SET} pass udp from ${oip} to any keep-state
#       ${fwcmd} add set ${SET} pass icmp from ${oip} to any keep-state

         # Allow NTP queries out in the world from the firewall.
#       ${fwcmd} add set ${SET} pass udp from ${oip} to any 123 keep-state

         # Allow DNS queries out in the world from the firewall.
#       ${fwcmd} add set ${SET} pass udp from ${oip} to any 53 keep-state

#^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v OUTGOING NAT POINT 
^v^v^v^v^v^v^v^v^v^v^v^v^v^v^v
         ${fwcmd} add set ${SET} divert natd all from any to any out 
recv ${iif}

         # just in case natd goes wierd.
         ${fwcmd} add set ${SET}  deny log all from "table(1)" to any
         # in fact don't allow packets out  that are not from this 
interface exactly
         ${fwcmd} add set ${SET} deny log ip from not ${oip} to any
         ${fwcmd} add set ${SET} allow all from any to any

         ${fwcmd} set enable ${SET}

         sysctl net.inet.ip.fw.enable=1






More information about the freebsd-ipfw mailing list