Linux Firewall Configuration and Setup - iptables

Table of Contents

Documents

This is a collection of notes about modifying your iptables packet filter to add blocking of specific IPs and systems on the Tor network.

Basic Setup

The following is a basic firewall script that you can load with

iptables-restore <iptables

It allows access to

Port Service Notes
25 SMTP Incoming email
110 POP3 POP3 service for email
53 DNS Name server requests
22 SSH Incoming SSH requests
80 HTTP Webserver requests
443 HTTPS Secure HTTP
4000:5200 BZFlag Servers BZFlag server ports
6000:6200 BZFlag Servers More server ports

Anything else is logged and dropped at the firewall.

The logwatch package makes nice daily summaries of the firewall logs.

Here is the iptables file:

# Generated by iptables-save v1.3.6 on Mon Oct 20 14:37:02 2008
*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [8574312917:611260898475]
-A INPUT -p tcp -m tcp --dport 25 -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 110 -j ACCEPT 
-A INPUT -p udp -m udp --dport 53 -m state --state NEW -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 53 -m state --state NEW -j ACCEPT 
-A INPUT -p icmp -j ACCEPT 
-A INPUT -i lo -j ACCEPT 
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 80 -m state --state NEW -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 443 -m state --state NEW -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 4000:5200 -m state --state NEW -j ACCEPT 
-A INPUT -p udp -m udp --dport 4000:5200 -m state --state NEW -j ACCEPT 
-A INPUT -p tcp -m tcp --dport 6000:6200 -m state --state NEW -j ACCEPT 
-A INPUT -p udp -m udp --dport 6000:6200 -m state --state NEW -j ACCEPT 
-A INPUT -j LOG 
-A INPUT -j DROP 
-A OUTPUT -o lo -j ACCEPT 
COMMIT
# Completed on Mon Oct 20 14:37:02 2008
# Generated by iptables-save v1.3.6 on Mon Oct 20 14:37:02 2008
*mangle
:PREROUTING ACCEPT [2345559699:169138998026]
:INPUT ACCEPT [2345528243:169135560350]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [9525683538:678126408324]
:POSTROUTING ACCEPT [9525683538:678126408324]
COMMIT
# Completed on Mon Oct 20 14:37:02 2008
# Generated by iptables-save v1.3.6 on Mon Oct 20 14:37:02 2008
*nat
:PREROUTING ACCEPT [8235419:506261976]
:POSTROUTING ACCEPT [1475984:91496805]
:OUTPUT ACCEPT [1475984:91496805]
COMMIT
# Completed on Mon Oct 20 14:37:02 2008

Tie the rules to the network interface

In Debian you can automatically run scripts when interfaces are started and stopped. We keep the firewall rules in /etc/iptables and have the following lines in /etc/network/interfaces to save and restore the current set of rules across reboots.

/etc/network/interfaces

# The loopback network interface
auto lo
iface lo inet loopback

# The primary network interface
iface eth0 inet static
  address 1.2.3.4
  netmask 255.255.255.255
  gateway 1.2.3.1
  #
  # iptables
  up /sbin/iptables-restore -c </etc/iptables
  down /sbin/iptables-save -c >/etc/iptables
  #

Custom Chains

Add custom chains to hold your custom firewall rules. Here we use two new chains TOR_BLOCK and BLOCK to keep two separate lists of IPs to be blocked at the firewall.

Adding Chains

Chains are where you keep your rules. You can add new chains with

iptables -N NEWCHAIN

Blocking Tor Systems

Here we use a new chain (TOR_BLOCK) and a script to populate the chain. This automatically keeps the list of IPs to block from the Tor network up to date hourly.

Adding TOR_BLOCK chain to the INPUT chain

Create your new chain to hold Tor network node IPs to block with

iptables -N TOR_BLOCK

and tie this chain to your input rules with

iptables -I INPUT -j TOR_BLOCK

This jumps from the INPUT chain to the TOR_BLOCK chain and then returns to the next rule in the INPUT after hitting the RETURN at the end of the TOR_BLOCK chain.

Populating the TOR_BLOCK chain

We will populate the TOR_BLOCK chain automatically using the torblock.fw script below.

This will empty the TOR_BLOCK chain, add a RETURN to the end of the chain so we can process it like a subroutine and enter each listed Tor network IP into the list.

You can run the torblock.fw script manually to wipe and populate the TOR_BLOCK chain. After you're happy with it I add a crontab entry that updates the list once an hour.

/usr/local/sbin/torblock.fw Script

#!/bin/bash

# A simple bash script to block IP traffic from TOR exit nodes.
# written by Andrew Vetlugin (antrew at gmail com)

wget='/usr/bin/wget'
iptables='/sbin/iptables'
#url='https://torstat.xenobite.eu/export/tor_exitnodes.csv'
url='http://torstatus.kgprog.com/ip_list_exit.php/Tor_ip_list_EXIT.csv'

iptables_target='DROP'
#iptables_target='REJECT'

# Quick guide:

# 1. add a separate chain for a list of TOR exit nodes
# (this should be done by hand once)
# iptables -N TOR_BLOCK

# 2. add a rule to INPUT chain
# Note: if you want to be able to connect to any TOR exit node yourself
# (e.g., if $url is a exit node you should be able to fetch a list of exit
# nodes from it) then you should add this rule AFTER accepting established
# and related connections)
# iptables -A INPUT -j TOR_BLOCK

# 3. add this script to crontab (I think 10-20 minutes interval should be OK)

# flush chain
$iptables -F TOR_BLOCK

# return to parent chain if the source is not TOR exit node
$iptables -I TOR_BLOCK -j RETURN 

# add TOR exit nodes to TOR_BLOCK chain with $iptables_target rule
for node in `$wget -q --no-check-certificate -O - $url | sort | uniq`; do
        $iptables -I TOR_BLOCK -s $node -j $iptables_target
done

Crontab Entry

The following crontab entry runs the torblock.fw script. This effectively erases all the Tor network nodes and re-populates the TOR_BLOCK list hourly.

00 * * * * root /usr/local/sbin/torblock.fw

Manual Blocking Chains

Now to deal with other problem systems we keep a separate BLOCK list which we can manually add, list, and remove entries from. The following scripts can be provided to trusted users via sudo so they can view and manipulate the firewall rules.

Add BLOCK chain

Add the new chain to hold the IPs to block.

iptables -N BLOCK

Add a single rule to return from the end of the list to the calling chain.

iptables -I BLOCK -j RETURN

and tie this chain to to your input rules.

iptables -I INPUT -j BLOCK

This jumps from the INPUT chain to the BLOCK chain and then returns to the next rule in the INPUT chain after hitting the RETURN at the end of the BLOCK chain.

Blocking IPs

Blocking IPs using the new BLOCK chain is pretty simple.

usage:
    sudo block IP
    sudo block IP/nn
    sudo block IP 'comment goes here'
    sudo block IP/nn 'comment goes here'

    e.g.
    sudo block 1.2.3.4 # Adds 1.2.3.4 as the IP to block
    sudo block 1.2.3.4 'reason for block' # Adds 1.2.3.4 with a comment
    sudo block 1.2.3.4/24 'block subnet' # Blocks all ips matching 1.2.3.*

Comments are kept in the iptables chain and are displayed by the blocklist script below.

Block Script - /usr/local/sbin/block

#!/bin/sh
INPUT="$(echo $1 | sed 's/[^.0-9/]//g')"
COMMENT="$2"
if [ "x$COMMENT" == "x" ]
then
        iptables -I BLOCK -s $INPUT -j DROP
else
        iptables -I BLOCK -s $INPUT -m comment --comment "$COMMENT" -j DROP
fi
/usr/local/sbin/blocklist

Unblocking IPs

To unblock an IP (removing it from the BLOCK chain) just pass the IP as parameter 1 and the comment (if any) exactly as it was entered when blocking the IP.

usage:
    sudo unblock IP
    sudo unblock IP 'comment goes here'

Unblock Script - /usr/local/sbin/unblock

#!/bin/sh
INPUT="$(echo $1 | sed 's/[^.0-9/]//g')"
COMMENT="$2"
if [ "x$COMMENT" == "x" ]
then
        iptables -D BLOCK -s $INPUT -j DROP
else
        iptables -D BLOCK -s $INPUT -m comment --comment "$COMMENT" -j DROP
fi
/usr/local/sbin/blocklist

Listing Blocked IPs

The following script simply displays the entries in the blocklist.

Usage: 
    sudo blocklist

Block List script - /usr/local/sbin/blocklist

#!/bin/sh
iptables -L BLOCK -n

Rate Limiting Connections

To effectively block script-kiddies that try dictionary attacks on the SSH server port I have the two following rules before the rule that accepts SSH connections

-A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --set
-A INPUT -p tcp -m tcp --dport 22 -m state --state NEW -m recent --update --seconds 60 --hitcount 4 -j DROP 

This allows up to 3 connections from the same IP in a 60 second period. Any more than that are blocked (and not logged).

Waiting 60 seconds before retrying the connection attempt makes it work again.

Saving and Restoring tables

I save the current iptables firewall rules with

/sbin/iptables-save -c >/etc/iptables

These can be restored with the following command

/sbin/iptables-restore -c </etc/iptables

These are normally added to my /etc/network/interfaces file on Debian when the appropriate network interface is brought up or down.

# Example /etc/network/interfaces entries

# The primary network interface
iface eth0 inet static
    address 1.2.3.4
    netmask 255.255.255.0
    gateway 1.2.3.1
    up /sbin/iptables-restore -c </etc/iptables
    down /sbin/iptables-save -c >/etc/iptables

Author: Bernt Hansen