Creating a Firewall Bridge
using IPTables/Ebtables

3/24/2005
Eric Low

Download:
bridge-utils from http://bridge.sourceforge.net
iptables from http://www.netfilter.org
ebtables from http://ebtables.sourceforge.net/

First, install iptables:

cd /programs/iptables-1.3.1
make KERNEL_DIR=/usr/src/linux-2.6.11.5 BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/share/man
make KERNEL_DIR=/usr/src/linux-2.6.11.5 BINDIR=/usr/bin LIBDIR=/usr/lib MANDIR=/usr/share/man install

Next, install ebtables:

make install KERNEL_INCLUDES=/usr/src/linux-2.6.11.5/include BINDIR=/usr/bin MANDIR=/usr/share/man

Next, install bridge-utils:

./configure --bindir=/usr/bin --libdir=/usr/lib --mandir=/usr/share/man
make install

 

copy bridge script to /etc/rc.d/init.d directory.
copy firewall script to /usr/bin directory.

Add the following lines to /etc/rc.d/rc.local:

# ---- Disable Source Routing
for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do
echo 0 > $f
done

# ---- Enable IP forwarding
echo 1 >/proc/sys/net/ipv4/ip_forward
#IP forwarding does NOT need to be enabled in our current
#bridging configuration.

# ---- Disable Explicit Congestion Notification
if [ -e /proc/sys/net/ipv4/tcp_ecn ]; then
echo 0 >/proc/sys/net/ipv4/tcp_ecn
fi

# ---- Always defrag incoming packets
#echo 1 >/proc/sys/net/ipv4/ip_always_defrag

# ---- Tell kernel to drop spoofed packets
for f in /proc/sys/net/ipv4/conf/*; do
echo 1 >$f/rp_filter
done

# ---- Enable bad error message protection
echo 1 >/proc/sys/net/ipv4/icmp_ignore_bogus_error_responses

# ---- Kill timestamps
echo 0 >/proc/sys/net/ipv4/tcp_timestamps

# ---- Enable SYN cookies (helps protect against DoS attacks)
echo 1 >/proc/sys/net/ipv4/tcp_syncookies

# -------------- Shut off system beep --------------------
echo -e "\033[11;0]"

Create ebtables rules in the file /etc/sysconfig/ebtables, in the following format:

#Important: eth0 is the internal interface, and eth1 is the
# external interface. eth2 is DMZ. Our network is 198.173.192.0/24,
# and our gateway is 198.173.192.254.

-F
-t nat -F
#Flush all chains.

#-c y
#-t nat -c y
#Turn on counters (this option may not exist any longer?)

-t broute -P BROUTING ACCEPT
-t nat -P PREROUTING ACCEPT
-t nat -P OUTPUT ACCEPT
-t nat -P POSTROUTING ACCEPT
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
#Set default actions on all chains.

-A FORWARD -p IPV4 -j ACCEPT
-A FORWARD -p ARP -j ACCEPT
-A OUTPUT -p IPV4 -j ACCEPT
-A OUTPUT -p ARP -j ACCEPT
#Allow normal IP traffic through. Everything else is blocked,
# including IPX/SPX.

-A INPUT -p IPV4 -j ACCEPT
#IP traffic must be allowed through the INPUT chain, then CHECKED BY
#IPTABLES, so as not to show a 'blackhole' on any portscans.
#Note that ARP is not allowed through.

-A INPUT -p ARP -d FF:FF:FF:FF:FF:FF -j ACCEPT
#Allow ARP broadcasts (needed if you want to access firewall from remotely).

 

Create iptables rules in the file /etc/sysconfig/iptables, in the following format:

#Important: eth0 is the internal interface, and eth1 is the
# external interface. eth2 is DMZ. Our network is 198.173.192.0/24,
# and our gateway is 198.173.192.254.

-F
#Flush all chains before we do anything else.

-t nat -P PREROUTING ACCEPT
-t nat -P POSTROUTING ACCEPT
-t nat -P OUTPUT ACCEPT
-P INPUT DROP
-P FORWARD DROP
-P OUTPUT ACCEPT
-t mangle -P PREROUTING ACCEPT
-t mangle -P POSTROUTING ACCEPT
-t mangle -P INPUT ACCEPT
-t mangle -P FORWARD ACCEPT
-t mangle -P OUTPUT ACCEPT
#Drop all rules by default (unless specifically allowed).
#The FORWARD and INPUT chains are really the only ones we need to worry about.

-A FORWARD -i eth1 -p tcp --dport 135 -j DROP
-A FORWARD -o eth1 -p tcp -s 198.173.192.59 --dport 135 -j MIRROR
-A FORWARD -o eth1 -p tcp -s 198.173.192.84 --dport 135 -j MIRROR
-A FORWARD -o eth1 -p tcp --dport 135 -j LOG --log-level 0
-A FORWARD -i eth1 -p tcp --dport 139 -j DROP
#Block W32.Blaster virus immediately.

-A INPUT -m unclean -j DROP
-A FORWARD -m unclean -j DROP
#Kill any abnormal packets, including packets that are too short to have
#a full ICMP/UDP/TCP header, TCP/UDP packets with no source or
#destination ports, illegal combination of TCP flags, either zero length
#or too long TCP options or options that occur after the END-OF-OPTIONS
#portion, fragments of an illegal length or offset (such as used in the
#Ping of Death).

-A INPUT -m state --state INVALID -j DROP
-A FORWARD -m state --state INVALID -j DROP
#Also, kill packets with an illegal combination of flags.

-A INPUT -m state --state ESTABLISHED,RELATED -d 198.173.192.7 -j ACCEPT
-A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT
#Allow packets that are part of an already established connection.
#The input chain should only allow established connections from
#inside our own network.

-A INPUT -p tcp --dport 113 -m state --state NEW -j LOG --log-level 0
#Log NMAP connections. These appear to be port 113.

-A INPUT -i lo -j ACCEPT
#Allow packets from loopback
-A INPUT -d 127.0.0.0/8 -j REJECT
#But do not allow connections to the local interface from external.

-A FORWARD -i eth0 -o eth1 -p udp -s 0.0.0.0/32 --sport 68 -d 255.255.255.255/32 --dport 67 -m state --state NEW,ESTABLISHED -j ACCEPT
#Let DHCP requests come from internal (client port=68,server port=67).

-A FORWARD -p udp --dport 137:138 -j REJECT
-A FORWARD -p tcp --dport 139 -j REJECT
-A INPUT -p udp --dport 137:138 -j REJECT
-A INPUT -p tcp --dport 139 -j REJECT
#Block NETBIOS (both directions). Don't let people use the windows
#NET command against our network.

-A FORWARD -p udp --dport 520 -j DROP
#Block RIP.
-A FORWARD -p ospf -j DROP
#Block OSPF (another routing protocol).

-A FORWARD -i eth1 -p tcp --dport 3372 -j DROP
#Drop packets destined for Microsoft MSDTC (installed on W2k or SQL).

-A FORWARD -i eth1 -s 198.173.192.254/32 -j ACCEPT
#Let the gateway come through (this should be the only internal address
#coming in through the external interface).

-A FORWARD -i eth0 -s 198.173.192.254 -j DROP
-A FORWARD -i eth2 -s 198.173.192.254 -j DROP
#Do not allow any packets from internal or DMZ claiming to be gateway.

-A FORWARD -i eth1 -s 198.173.192.0/24 -j DROP
#Drop any packets from external claiming to be from an internal address

-A FORWARD -i eth0 -o eth1 -p tcp --dport 21:23 -j ACCEPT
#Allow FTP (21), Telnet (23), and SSH (22) from internal to external.

 

All of the above rules are recommended as a bare minimum ruleset. I keep an extremely conservative ruleset, allowing almost nothing inbound, and only specific ports outbound. My iptables ruleset is currently 760 lines long. And for the record, I've never, ever, ever noticed it running slow, even when that was on an 800MHz Athlon under a 2.4 kernel.

=================================
Next, I wanted to do SNAT, and therefore needed a DHCP server installed. DHCP is pretty fucking easy.

=================================

Next, I wanted to implement fair queueing. We currently have our main network and two DMZ's. DMZ 1 has all of our web servers in it, and since we do web surveys, it is very important that this particular zone has a guaranteed bandwidth and has top priority. DMZ 2 is the IT department, and for various reasons, we also need a guaranteed bandwidth, albeit small.

First, I enabled the following options in the kernel, under Device Drivers -> Networking -> Networking Options:

QoS and/or fair queueing
-- CBQ packet scheduler
-- HTB packet scheduler
-- SFQ queue
-- Packet classifier API
-- -- TC index classifier
-- -- Firewall based classifier
-- -- U32 classifier
-- -- -- Use nfmark as a key in U32 classifier

The base of my fair queueing would be the Heirarchical Token Bucket queueing discipline, perhaps with a Stochastic Fairness Queue underneath.

 

Here's what my tc commands look like to shape my traffic how I want it:

tc qdisc add dev eth0 root handle 1:0 htb default 1
tc class add dev eth0 parent 1:0 classid 1:1 htb rate 1400kbit
tc class add dev eth0 parent 1:1 classid 1:11 htb rate 256kbit ceil 1080kbit prio 10
tc class add dev eth0 parent 1:1 classid 1:12 htb rate 256kbit ceil 1080kbit prio 5
tc class add dev eth0 parent 1:1 classid 1:13 htb rate 64kbit ceil 888kbit prio 10
tc qdisc add dev eth0 parent 1:11 handle 11:0 sfq perturb 10
tc qdisc add dev eth0 parent 1:13 handle 13:0 sfq perturb 10
tc class add dev eth0 parent 1:12 classid 1:131 htb rate 128kbit ceil 1080kbit prio 2
tc class add dev eth0 parent 1:12 classid 1:139 htb rate 32kbit ceil 128kbit prio 10
tc filter add dev eth0 protocol ip parent 1:12 prio 2 u32 match ip dport 443 0xffff flowid 1:131
tc filter add dev eth0 protocol ip parent 1:12 prio 3 u32 match ip dport 80 0xffff flowid 1:131
tc filter add dev eth0 protocol ip parent 1:12 prio 10 u32 match ip dport 25 0xffff flowid 1:139

Now if you have a netfilter bridge running on your machine, tc will not see packets on the actual physical interface. To get around this, I simply marked all of my packets going out of my external physical interface, eth0, with iptables using the CLASSIFY target in the mangle table's POSTROUTING chain. Here is the one simple rule I added, right up at the top of my rule list:

iptables -t mangle -A POSTROUTING -m physdev --physdev-out eth0 -j CLASSIFY --set-class 1:0

 


To see what that qdisc is looking like, as far as packets go, type tc -s -d qdisc dev eth0.

 

 

     Downloads:

 

sshd SSHD startup script, goes in /etc/rc.d/init.d/ directory.
sshd_config   My SSHD configuration file, /usr/etc/sshd_config
putkeys   my /logins/keys/putkeys script to put users' RSA keys in the appropriate places.
keyconfig   /logins/keys/keyconfig configuration file for my putkeys script.