3.2. Firewall

We are going to secure our web server using iptables as a firewall. To make sure that are firewall rules are not lost when we reboot the server we are also going to install a package called iptables-persistent.

apt-get install iptables iptables-persistent

If you check iptables rules with iptables -L you should see a clean chain list:

example:~# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

To help us set up iptables rules we are going to use a modified version of a firewall script that is used for setting up QmailToaster on CentOS, the script looks like this:

# Fedora Core 2
# Nick Hemmesch <nick@ndhsoft.com>
# June 2, 2004
# Customised for Debian/Ubuntu by Goran Juric, April 14, 2009.
## Set your IP address
## Flush rules & reset counters
iptables -F
iptables -X
## Set policies
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT DROP
## Drop all incoming fragments
iptables -A INPUT -i eth0 -f -j DROP
## Drop outside packets with local addresses - anti-spoofing measure
iptables -A INPUT -s $MYIP -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
iptables -A INPUT -s -i ! lo -j DROP
## Pass all locally-originating packets
iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
## Accept ICMP ping echo requests
## (this allows other people to ping your machine, among other things),
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
## Accept all traffic from a specific machine with IP specified in the ROUTER variable
# Traffic coming from the company router (address of the clients behind NAT)
#iptables -A INPUT -p tcp -m tcp --syn -s $ROUTER -j ACCEPT
## Accept traffic on port p from a specific machine with IP x.x.x.x
## replace p with the desired port number, and replace x.x.x.x with
## the desired IP, then uncomment the line.
#iptables -A INPUT -p tcp -m tcp --syn -s x.x.x.x --dport p -j ACCEPT
## Accept ftp-data and ftp (ports 20 & 21)
#iptables -A INPUT -p tcp -m tcp --syn --dport 20 -j ACCEPT
#iptables -A INPUT -p tcp -m tcp --syn --dport 21 -j ACCEPT
## Accept ssh (port 22)
#iptables -A INPUT -p tcp -m tcp --syn --dport 22 -j ACCEPT
## Accept telnet (port 23)
##iptables -A INPUT -p tcp -m tcp --syn --dport 23 -j ACCEPT
## Accept smtp (port 25 | 587)
iptables -A INPUT -p tcp -m tcp --syn --dport 25 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --syn --dport 587 -j ACCEPT
## Accept dns (port 53)
#iptables -A INPUT -p udp -m udp -s 0/0 --dport 53 -d 0/0 -j ACCEPT
#iptables -A INPUT -p tcp -m tcp -s 0/0 --dport 53 -d 0/0 -j ACCEPT
## Accept http (port 80)
iptables -A INPUT -p tcp -m tcp --syn --dport 80 -j ACCEPT
## Accept Subversion (port 3690)
#iptables -A INPUT -p tcp -m tcp --syn --dport 3690 -j ACCEPT
## Accept pop3 (port 110)
#iptables -A INPUT -p tcp -m tcp --syn --dport 110 -j ACCEPT
## Accept pop3s (port 995)
#iptables -A INPUT -p tcp -m tcp --syn --dport 995 -j ACCEPT
## Accept inbound identd (port 113)
#iptables -A INPUT -p tcp -m tcp --syn --dport 113 -j ACCEPT
## or you can reject and send back a TCP RST packet instead
#iptables -A INPUT -p tcp -m tcp --dport 113 -j REJECT --reject-with tcp-reset
## Accept imap (port 143)
iptables -A INPUT -p tcp -m tcp --syn --dport 143 -j ACCEPT
## Accept imaps (port 993)
iptables -A INPUT -p tcp -m tcp --syn --dport 993 -j ACCEPT
## Accept https (port 443)
iptables -A INPUT -p tcp -m tcp --syn --dport 443 -j ACCEPT
## Accept smtps (port 465)
#iptables -A INPUT -p tcp -m tcp --syn --dport 465 -j ACCEPT
## Accept msp (port 587)
#iptables -A INPUT -p tcp -m tcp --syn --dport 587 -j ACCEPT
## Accept SpamAssassin (port 783)
#iptables -A INPUT -p tcp -m tcp --syn --dport 783 -j ACCEPT
## Accept pop3s (port 995)
#iptables -A INPUT -p tcp -m tcp --syn --dport 995 -j ACCEPT
## Accept mysql (port 3306)
#iptables -A INPUT -p tcp -m tcp --syn --dport 3306 -j ACCEPT
## Allow inbound established and related outside communication
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
## Drop outside initiated connections
iptables -A INPUT -m state --state NEW -j REJECT
## Allow all outbound tcp, udp, icmp traffic with state
iptables -A OUTPUT -p tcp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p udp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p icmp -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
## Save rules
#service iptables save#
iptables-save > /etc/iptables/rules
echo ""
echo "Check your rules - iptables -L -n and move the iptables.rules to appropriate place"
echo ""

There are a couple of things that you have to modify in this script.

First, and the most important one, is the MYIP variable that you should set to the IP address of your server.

If you are always accessing the server from the same IP addres you can set that IP address to the variable called ROUTER and uncomment the line where this variable is defined as well as the line containing iptables -A INPUT -p tcp -m tcp --syn -s $ROUTER -j ACCEPT.


Be carefull setting the ROUTER variable

This IP address will never be firewalled and all ports on the server will be open for this address. If you know what you are doing, by setting the ROUTER variable and allowing all traffic from that address, you will be able to close SSH port for everybody else.

If you do not have ROUTER variable set and appropriate lines uncommented you will have to uncomment the line with the rule that allows port 22 (SSH) to pass through. If you do not do this, you will lock yourself out of the system once we run the script (unless you are sitting and terminal and not accessing it remotely via SSH).


This is important

Please do not run this script if you haven't read and understood the last couple of paragraphs. You could lock yourself out of the system.

Make the necessary modifications and save the script to /root/scripts/firewall.sh. Now we just have to make it executable and change it's ownership to the root user.

chown root:root /root/scripts/firewall.sh
chmod 700 /root/scripts/firewall.sh
chmod +x /root/scripts/firewall.sh

After the script is run it will save the information that is needed for the rules to be restored into /etc/iptables/rules. This file is used by iptables-persistent to restore the rules if you reboot your server.

As you have maybe noticed we already opened some ports that we are going to need (for sending and recivieng email and for access to our web server). If you plan to use POP3 access to your mail you will have to open the ports for that as well.

It's time to finally run the script.

You will see some warning beacuse this is the script I have beed using on Debian Lenny, but rest assured you can safely ignore them. If you check your iptables rules you will see that they are fine.

Commit the changes made to the firewall rules with etckeeper.