This section contains all the information and FAQs that I couldn't fit inside the structure above.
This question requires some thought. You can try to organize them to optimize speed (minimize the number of rule-checks for the most common packets) or to increase manageability.
If you have an intermittent link, say a PPP link, you might want to
set the first rule in the input chain to be set to `-i ppp0 -j DENY' at
boot time, then have something like this in your ip-up
script:
# Re-create the `ppp-in' chain.
ipchains-restore -f < ppp-in.firewall
# Replace DENY rule with jump to ppp-handling chain.
ipchains -R input 1 -i ppp0 -j ppp-in
Your ip-down
script would look like:
ipchains -R input 1 -i ppp0 -j DENY
There are some things you should be aware of before you start filtering out everything you don't want.
ICMP packets are used (among other things) to indicate failure for other protocols (such as TCP and UDP). `destination-unreachable' packets in particular. Blocking these packets means that you will never get `Host unreachable' or `No route to host' errors; any connections will just wait for a reply that never comes. This is irritating, but rarely fatal.
A worse problem is the role of ICMP packets in MTU discovery. All good TCP implementations (Linux included) use MTU discovery to try to figure out what the largest packet that can get to a destination without being fragmented (fragmentation slows performance, especially when occasional fragments are lost). MTU discovery works by sending packets with the "Don't Fragment" bit set, and then sending smaller packets if it gets an ICMP packet indicating "Fragmentation needed but DF set" (`fragmentation-needed'). This is a type of `destination-unreachable' packet, and if it is never received, the local host will not reduce MTU, and performance will be abysmal or non-existent.
If you're trying to block outgoing TCP connections, remember that DNS doesn't always use UDP; if the reply from the server exceeds 512 bytes, the client uses a TCP connection (still going to port number 53) to get the data.
This can be a trap because DNS will `mostly work' if you disallow such TCP transfers; you may experience strange long delays and other occasional DNS problems if you do.
If your DNS queries are always directed at the same external source
(either directly by using the nameserver
line in
/etc/resolv.conf
or by using a caching nameserver in forward
mode), then you need only allow TCP connections to port domain
on that nameserver from the local domain
port (if using a caching
nameserver) or from a high port (> 1023) if using
/etc/resolv.conf
.
The classic packet filtering problem is FTP. FTP has two modes; the traditional one is called active mode and the more recent one is called passive mode. Web browsers usually default to passive mode, but command-line FTP programs usually default to active mode.
In active mode, when the remote end wants to send a file (or even the
results of an ls
or dir
command) it tries to open a TCP
connection to the local machine. This means you can't filter out
these TCP connections without breaking active FTP.
If you have the option of using passive mode, then fine; passive mode makes data connections from client to server, even for incoming data. Otherwise, it is recommended that you only allow TCP connections to ports above 1024 and not between 6000 and 6010 (6000 is used for X-Windows).
Linux boxes are now immune to the famous Ping of Death, which involves sending an illegally-large ICMP packet which overflows buffers in the TCP stack on the receiver and causes havoc.
If you are protecting boxes which might be vulnerable, you could simply block ICMP fragments. Normal ICMP packets aren't large enough to require fragmentation, so you won't break anything except big pings. I have heard (unconfirmed) reports that some systems required only the last fragment of an oversize ICMP packet to corrupt them, so blocking only the first fragment is not recommended.
While the exploit programs I have seen all use ICMP, there is no reasons that TCP or UDP fragments (or an unknown protocol) could not be used for this attack, so blocking ICMP fragments is only a temporary solution.
Teardrop and Bonk are two attacks (mainly against Microsoft Windows NT machines) which rely on overlapping fragments. Having your Linux router do defragmentation, or disallowing all fragments to your vulnerable machines are the other options.
Some less-reliable TCP stacks are said to have problems dealing with large numbers of fragments of packets when they don't receive all the fragments. Linux does not have this problem. You can filter out fragments (which might break legitimate uses) or compile your kernel with `IP: always defragment' set to `Y' (only if your Linux box is the only possible route for these packets).
There are some timing issues involved in altering firewall rules. If you are not careful, you can let packets through while you are half-way through your changes. A simplistic approach is to do the following:
# ipchains -I input 1 -j DENY
# ipchains -I output 1 -j DENY
# ipchains -I forward 1 -j DENY
... make changes ...
# ipchains -D input 1
# ipchains -D output 1
# ipchains -D forward 1
#
This drops all packets for the duration of the changes.
If you changes are restricted to a single chain, you might want to create a new chain with the new rules, and then replace (`-R') the rule that pointed to the old chain with one that points to the new chain: then you can delete the old chain. This replacement will occur atomically.
IP spoofing is a technique where a host sends out packets which claim to be from another host. Since packet filtering makes decisions based on this source address, IP spoofing is uses to fool packet filters. It is also used to hide the identity of attackers using SYN attacks, Teardrop, Ping of Death and the like (don't worry if you don't know what they are).
The best way to protect from IP spoofing is called Source Address
Verification, and it is done by the routing code, and not firewalling
at all. Look for a file called
/proc/sys/net/ipv4/conf/all/rp_filter
. If this exists, then
turning on Source Address Verification at every boot is the right
solution for you. To do that, insert the following lines somewhere in
your init scripts, before any network interfaces are initialized
(eg. Debian users would put them in /etc/init.d/netbase if they are
not already there):
# This is the best method: turn on Source Address Verification and get
# spoof protection on all current and future interfaces.
if [ -e /proc/sys/net/ipv4/conf/all/rp_filter ]; then
echo -n "Setting up IP spoofing protection..."
for f in /proc/sys/net/ipv4/conf/*/rp_filter; do
echo 1 > $f
done
echo "done."
else
echo PROBLEMS SETTING UP IP SPOOFING PROTECTION. BE WORRIED.
echo "CONTROL-D will exit from this shell and continue system startup."
echo
# Start a single user shell on the console
/sbin/sulogin $CONSOLE
fi
If you cannot do this, you can manually insert rules to protect every
interface. This requires knowledge of each interface. The 2.1
kernels automatically reject packets claiming to come from the 127.*
addresses (reserved for the local loopback interface, lo
).
For example, say we have three interfaces, eth0
, eth1
and
ppp0
. We can use ifconfig
to tell us the address and
netmask of the interfaces. Say eth0
was attached to a network
192.168.1.0 with netmask 255.255.255.0, eth1
was attached to a
network 10.0.0.0 with netmask 255.0.0.0, and ppp0
connected to
the Internet (where any address except the reserved private IP
addresses are allowed), we would insert the following rules:
# ipchains -A input -i eth0 -s ! 192.168.1.0/255.255.255.0 -j DENY
# ipchains -A input -i ! eth0 -s 192.168.1.0/255.255.255.0 -j DENY
# ipchains -A input -i eth1 -s ! 10.0.0.0/255.0.0.0 -j DENY
# ipchains -A input -i ! eth1 -s 10.0.0.0/255.0.0.0 -j DENY
#
This approach is not as good as the Source Address Verification approach, because if your network changes, you have to change your firewalling rules to keep up.
If you are running a 2.0 series kernel, you might want to protect the loopback interface as well, using a rule like this:
# ipchains -A input -i ! lo -s 127.0.0.0/255.0.0.0 -j DENY
#
There is a userspace library I have written which is included with the source distribution called `libfw'. It uses the ability of IP Chains 1.3 and above to copy a packet to userspace (using the IP_FIREWALL_NETLINK config option).
Things such as stateful inspection (I prefer the term dynamic firewalling) can be implemented in userspace using this library. Other nifty ideas include controlling packets on a per-user basis by doing a lookup in a userspace daemon. This should be pretty easy.
The `mark' capability of the firewalls is underutilized: it could easily be used to represent a priority for the Quality of Service code, which would make it simple to control packet priorities.
Firewalling and NAT are being redesigned for 2.3. Plans and discussions are available on the netdev archive. These enhancements should clear up many outstanding usability issues (really, firewalling and masquerading shouldn't be this hard), and allow growth for far more flexible firewalling.