Linux Firewall Template

From Granizada

Jump to: navigation, search

This firewall template can be helpful to protect essential services running on a Linux machine, and the machine in general. If you care about the data on a server or workstation, it should have a firewall, no matter what network it is on. This firewall:

  • sets some security defaults on the IP stack (using /proc)
  • blocks all incoming traffic by default, unless it is a reply to outgoing traffic
  • allows important basic functionality, such as certain kinds of ICMP packets
  • rate-limits incoming ssh connections by default

There are comments, so modify to taste!

False Security?

There is no substitute for having services switched off. A good test is to remove all firewalling and then run the nmap command against all interfaces on the server -- do you need everything nmap reports as open? What about with the -sU nmap parameter for checking UDP protocols? And don't forget localhost, just because a daemon only listens on localhost doesn't mean it is safe. Rather than disabling, if possible remove unwanted server programs entirely from the system. It is all to easy to have something turn itself on again after a reboot, or a from a typing mistake at the root prompt.

A firewall can give some short-term protection for a sloppily-configured server listening on unecessary ports. But it only covers up the risk, not eliminates it.

An Ode to Daemon Authors

Think about the insecure habit many daemons have of listening on their port at address INADDR_ANY (address 0.0.0.0), which means all IP interfaces. Every networked machine has at least two interfaces (the network and the loopback) and many have more. So by default, many daemons listen on all interfaces! A better design is to require administrators to explicitly specify additional interfaces to listen on, perhaps with the default being the first IP address.

And more...

If you want to do traffic shaping and packet prioritising with the Linux tc command, you might want to look at the Linux Shaping Template. There are some iptables snippets there you will need to paste into your firewall to make traffic shaping work.

Last updated Dan 20:07, 12 June 2008 (CST)


#!/bin/sh
#
# Server firewall template
# (c) Dan Shearer 2002-2008 dan@shearer.org
# Latest version http://shearer.org/Linux_Firewall_Template
# Under the GPL version 3.0 or higher.
#
# Version 1.3
#
# Too many servers have no firewall at all. There is no excuse for thinking that a network
# behind a firewall is safe: every client and server should be running a firewall.
#
# This is a template host firewall for Linux 2.4.x, x > 6 and 2.6.x kernels
# The intent is that the host should be free to do what it needs to do such
# as serve web pages, database requests or print requests and everything else should
# be blocked. There should be no loss in functionality: if you really know what your
# server is doing then you ought not to get nasty surprises when you block access to 
# unused ports. (But, assume that if you don't test this carefully then you will get
# nasty surprises!)
#
# You will certainly need to tweak this script for your server. It is a template, not
# a solution. If you have lots of servers, consider using the same script on each one with 
# environment variables or commandline parameters to determine which workload a particular
# server is running. 
#
# The concept of "defence in depth" means that this firewall is just part of a 
# multilayered approach to security. It should be much better than an unsecured server, but 
# if you put too much faith in it the result will actually be worse :-) It is better than 
# a firewall generated by an automated tool in the sense that there's a better chance you know 
# what is going on if you've worked through deploying this script.
#
# How to use:
#
#  1. Try to be reasonably sure that your host server is not already compromised. There
#     are many ways of doing this; do a lot of reading and/or pay a security consultant if 
#     you don't know them.
#
# 2. Consider how you are going to get back in to your server during testing of this script
#    when you accidentally lock yourself out. Maybe a cron job to disable firewalling,
#    maybe serial console access, whatever. It can be very frustrating to run this script and

# 3. Enable a few things in your kernel, or, run this script and see if you get errors about
#    missing kernel functionality. The things you want enabled are at least:
#
#       CONFIG_PACKET
#       CONFIG_NETFILTER
#       CONFIG_IP_NF_CONNTRACK
#       CONFIG_IP_NF_FTP
#       CONFIG_IP_NF_IRC
#       CONFIG_IP_NF_IPTABLES
#       CONFIG_IP_NF_FILTER
#       CONFIG_IP_NF_NAT
#       CONFIG_IP_NF_MATCH_STATE
#       CONFIG_IP_NF_TARGET_LOG
#       CONFIG_IP_NF_MATCH_LIMIT
#       CONFIG_IP_NF_TARGET_MASQUERADE 
#
# 4. Do lots of testing, running with 'sh -x'. Does it work after a reboot? A kernel or distribution refresh?
#
# 5. Consider putting this script in you /etc/init.d directory so it starts when your machine 
#    starts . In Debian or Ubuntu, copy it to /etc/init.d/filename, then run 
#    ''chmod 700 filename'' followed by ''update-rc.d filename defaults''
#
# 6. Add a test in your machine monitoring system (eg Nagios) that fails if you can get through on a port 
#    that should be blocked. There all sorts of silly reasons why perfectly good firewalls
#    get deactivated. Since everything continues to work nobody notices!
#
#
# What this script does:
#
#   * Locks down eth0, leaves eth1 wide open except for kernel protections. That's for the 
#     case where eth1 is on some special network etc.. not a good idea but a lot of people
#     do have multihomed servers and want different rules on different interfaces, so for
#     simplicity this template assumes the second network is completely trusted. If you only have
#     one ethernet card delete references to eth1. If you have completely different card ids you
#     can change them in the variable definitions at the top.
#
#   * Drops all traffic except whitelisted packets, in this case for web, email and ssh
#  
#   * Accepts incoming traffic unconditionally provided the traffic is related to a previously
#     accepted packet. This connection state tracking saves a lot of rule writing and debugging
#
#   * Drops certain packets at the Linux stack level (before iptables sees them) if they
#     are obviously bad (eg spoofed to appear as if they come from this host.)

#Can replace with iptables6 
IPTABLES="/sbin/iptables"

#Clear everything
$IPTABLES -F
#Cut the cables!
$IPTABLES -P INPUT DROP
$IPTABLES -P OUTPUT DROP
$IPTABLES -P FORWARD DROP

#### IP stack protections
#### One of the many holes in this firewall is that no actions is taken if
#### can't mount /proc, modprobe an iptables module etc. Fine for a template :-)

#### No icmp ratelimits -- worth adding but not quite the same syntax between 2.4 and 2.6.

# this is a multihomed machine and it isn't a router, so stop it forwarding packets
echo "0" > /proc/sys/net/ipv4/ip_forward 

# stops people mapping the network with trivial broadcast pings. Of course nmap has 
# other ways to do this!
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts

# drop source routed packets
for f in /proc/sys/net/ipv4/conf/*/accept_source_route; do
  echo 0 > $f
done

# synflood control
echo 1 > /proc/sys/net/ipv4/tcp_syncookies

# hosts shouldn't send icmp redirects (routers do that.) Hosts
# accept redirects though.
for f in /proc/sys/net/ipv4/conf/*/send_redirects; do
  echo 0 > $f
done

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

#### end IP stack protections

# Main public LAN address
INET_IP="x.x.x.x"
# On Debian-derived distros you can also do this:
# INET_IP=`grep 'address ' /etc/network/interfaces | awk '{print $2}'`
INET_IFACE="eth0"
# Secondary LAN address, illustrating how to deal with another interface.
# If you don't have one, remove all lines that refer to INET2_IFACE and INET2_IP.
# If you comment only these lines out, you may be confused by errors later on although
# the firewall will not be affected.
INET2_IP="y.y.y.y"
INET2_IFACE="eth1"

# Clear all chains (but not policies; this is a race condition)
$IPTABLES -X

modprobe ip_tables
modprobe ip_conntrack
modprobe ipt_state

# Make some chains

$IPTABLES -N bad_tcp
$IPTABLES -N allowed
$IPTABLES -N icmpin
$IPTABLES -N tcpin
$IPTABLES -N udpin

#### start iptables rules

#Loopback - open
$IPTABLES -A INPUT -p ALL -i lo -j ACCEPT
$IPTABLES -A OUTPUT -p ALL -o lo -j ACCEPT

#Allow local connections to local interfaces
$IPTABLES -A INPUT -p ALL -i $INET_IFACE -s $INET_IP -j ACCEPT
$IPTABLES -A INPUT -p ALL -i $INET2_IFACE -s $INET2_IP -j ACCEPT

#Private ethernet - open.
$IPTABLES -A INPUT -p ALL -i $INET2_IFACE -j ACCEPT
$IPTABLES -A OUTPUT -p ALL -i $INET2_IFACE -j ACCEPT

#Rules for bad_tcp
$IPTABLES -A bad_tcp -p tcp ! --syn -m state --state NEW -j LOG \
--log-prefix "New packet no syn:"
$IPTABLES -A bad_tcp -p tcp ! --syn -m state --state NEW -j DROP

#Rules for allowed
#$IPTABLES -A allowed -p TCP --syn -j ACCEPT
$IPTABLES -A allowed -p TCP -m state --state NEW -j ACCEPT
$IPTABLES -A allowed -p TCP -j DROP

#Rules for icmp
#  Firstly echo reply/request 
$IPTABLES -A icmpin -p ICMP --icmp-type 0 -j ACCEPT
$IPTABLES -A icmpin -p ICMP --icmp-type 8 -j ACCEPT
#  Secondly time exceeded (for traceroute)
$IPTABLES -A icmpin -p ICMP --icmp-type 11 -j ACCEPT
#  Thirdly want to get dest unreachable because 3 error codes important:
#    1 is "Host unreachable"
$IPTABLES -A icmpin -p ICMP --icmp-type 3/1 -j ACCEPT
#    3 is "Port unreachable"
$IPTABLES -A icmpin -p ICMP --icmp-type 3/3 -j ACCEPT
#    4 is "Fragmentation Required but DF Bit Is Set", for PMTUD.
$IPTABLES -A icmpin -p ICMP --icmp-type 3/4 -j ACCEPT

#Rules for tcpin

#This is the magic that avoids us having to write lots
#of rules to get both sides of a conversation, thanks to CONNTRACK
$IPTABLES -A tcpin -p TCP -m state --state ESTABLISHED,RELATED -j ACCEPT

# And here we open up for incoming ssh, web and email. DNS is slightly trickier, as
# is RDP but it is all variations on a theme. This is whitelisting packets.

# Ratelimit ssh, and allow for a non-standard port
SSH=22

# Note!! You can DOS yourself with this if you aren't careful. Either by having regular jobs
# that log in (with ssh keys, say rsync runs) that trigger the threshholds, or because
# someone spoofs your address (nmap can show you how to do that) so you lock yourself
# out.
# The logic here is that the 3rd try in 20 seconds will be blocked, and 6th in 60
# and so on. The module stores a list and checks to see if the incoming
# connection comes from an IP that has been seen within that period of time.
# That's why there are several lines with different values, because there is no
# concept of the count being reset.
$IPTABLES -A tcpin -m recent --set --name SSH
$IPTABLES -A tcpin -m recent --update --seconds 20     --hitcount 3    --name SSH -j DROP
$IPTABLES -A tcpin -m recent --update --seconds 60     --hitcount 6    --name SSH -j DROP
$IPTABLES -A tcpin -m recent --update --seconds 250    --hitcount 20   --name SSH -j DROP
$IPTABLES -A tcpin -m recent --update --seconds 2100   --hitcount 50   --name SSH -j DROP
$IPTABLES -A tcpin -m recent --update --seconds 20000  --hitcount 200  --name SSH -j DROP

$IPTABLES -A tcpin -p TCP --dport $SSH -j allowed

$IPTABLES -A tcpin -p TCP --dport 80 -j allowed
$IPTABLES -A tcpin -p TCP --dport 25 -j allowed
$IPTABLES -A tcpin -p TCP -j DROP

#Rules for udp
# The default traceroute port range
$IPTABLES -A udpin -p udp --dport 33434:33524 -j ACCEPT
# Replies to outgoing DNS etc
$IPTABLES -A udpin -p UDP -m state --state ESTABLISHED -j ACCEPT
$IPTABLES -A udpin -p UDP -j DROP

#Rules for non-whitelisted incoming packets
$IPTABLES -A INPUT -p tcp -j bad_tcp
$IPTABLES -A INPUT -p ICMP -i $INET_IFACE -j icmpin
$IPTABLES -A INPUT -p TCP -i $INET_IFACE -j tcpin
$IPTABLES -A INPUT -p UDP -i $INET_IFACE -j udpin

$IPTABLES -A OUTPUT -p icmp --icmp-type 0 -j ACCEPT
$IPTABLES -A OUTPUT -p icmp --icmp-type 8 -j ACCEPT
$IPTABLES -A OUTPUT -p icmp --icmp-type 11 -j ACCEPT
$IPTABLES -A OUTPUT -p icmp --icmp-type 3/1 -j ACCEPT
$IPTABLES -A OUTPUT -p icmp --icmp-type 3/3 -j ACCEPT
$IPTABLES -A OUTPUT -p icmp --icmp-type 3/4 -j ACCEPT

# This is a bit odd, check rewriting habits of iptables.
$IPTABLES -A OUTPUT -p ALL -m state --state ESTABLISHED,RELATED -j ACCEPT

$IPTABLES -A OUTPUT -p ALL -s $INET_IP -j ACCEPT

$IPTABLES -A OUTPUT -p tcp -j bad_tcp
Personal tools
Navigation