Using iptables on Debian Linux

My Draytek Vigor router supports NAT and Routed IP simultaneously, so I availed myself of my ISP's offer of a block of eight static IP addresses, rather than the single address I originally took.

Of the block of eight, the first is taken by the ISP, the second allocated to the WAN router interface, and the last is the broadcast address. This leaves five usable static IP addresses. Such riches...

I used four of the addresses for Multi-NAT (assigning them as WAN aliases). These WAN aliases can be used in the port forwarding rules within NAT, so that I can have two (or more) machines listening on port 80, for example.

The fifth address I wanted to use for Routed IP, meaning that the machine assigned that address would appear as if it were directly on the Internet. What sacrificial lamb did I have available? Hmm, steel, a Debian system, hosted on an elderly laptop seemed to be the best option.

It appeared sensible to setup a firewall on steel before exposing it to the evils of the Internet. I grappled with iptables, the inbuilt firewall capability on Linux. I found a University of Maryland, Physics Department article and this LinuxHomeNetworking webpage useful as references. I arrived at the following setup:

  # Firewall setup using iptables
  # mpw 2008-03-19

  # Clear out existing rules and policies (firewall is transparent)
  /sbin/iptables --policy INPUT ACCEPT
  /sbin/iptables --policy OUTPUT ACCEPT
  /sbin/iptables --flush
  /sbin/iptables --delete-chain

  # Create chain to allow machine to talk to itself and to allow responses
  # to connections started from inside the firewall
  /sbin/iptables --new-chain existing-connections
  /sbin/iptables --append INPUT -j existing-connections
  /sbin/iptables --append existing-connections --in-interface lo -j ACCEPT
  /sbin/iptables --append existing-connections -m state --state ESTABLISHED \
    -j ACCEPT
  /sbin/iptables --append existing-connections -m state --state RELATED \ 
    -j ACCEPT

  # Create chain to hold allowed input connections
  /sbin/iptables --new-chain allowed
  /sbin/iptables --append INPUT -j allowed

  # Create chain to hold non-logging drops (for very frequent accesses)
  /sbin/iptables --new-chain drop-nolog
  /sbin/iptables --append INPUT -j drop-nolog

  # Set new standard policies (INPUT and FORWARD are DROPped by default)
  /sbin/iptables --policy OUTPUT ACCEPT
  /sbin/iptables --policy INPUT DROP
  /sbin/iptables --policy FORWARD DROP

  # Define allowed inward connections (ssh, smtp and http)
  /sbin/iptables -A allowed -p tcp --dport 22  -j ACCEPT
  /sbin/iptables -A allowed -p udp --dport 22  -j ACCEPT
  /sbin/iptables -A allowed -p tcp --dport 25  -j ACCEPT
  /sbin/iptables -A allowed -p tcp --dport 80  -j ACCEPT
  # Allow pings (rate limited)
  /sbin/iptables -A allowed -p icmp --icmp-type echo-request \
    -m limit --limit 1/second -j ACCEPT

  # Define those inward connects to drop without logging
  /sbin/iptables -A drop-nolog -p udp -d -j DROP
  /sbin/iptables -A drop-nolog -p udp -d -j DROP

  # Create a LOGDROP chain to log and drop packets
  # Logging is limited to curtail log size
  /sbin/iptables -N LOGDROP
  /sbin/iptables -A LOGDROP -j LOG --log-level warn --log-prefix "FW: "\
    -m limit --limit 10/minute --limit-burst 5
  /sbin/iptables -A LOGDROP -j DROP

  # Log and drop all other traffic
  /sbin/iptables -A INPUT -j LOGDROP

The --log-level warn is matched by a change to the /etc/syslog.conf file, to log kernel warnings to the file /var/log/firewall.log. This is achieved by putting the following line in the /etc/syslog.conf file:

  kern.=warn                     /var/log/firewall.log

The /var/log/firewall.log file will also contain other messages flagged as kern.warn, so the "FW:" prefix can be used to filter out the firewall-related messages.

The final task was to make the firewall automatically active on boot. In Debian this can be achieved by putting appropriate scripts in the /etc/network/if-up.d and /etc/network/if-down.d directories.

For /etc/network/if-up.d I created a file called iptables with the following contents:

  # Load saved iptables rules
  iptables-restore </var/lib/iptables/rules

And for /etc/network/if-down.d, the iptables file contains:

  # Save iptables rules
  iptables-save >/var/lib/iptables/rules

Both scripts should be executable. I also had to create the directory /var/lib/iptables to hold the rules between boots.

Here's an example of the logging generated by the above rule set.

  FW: IN=eth0 OUT= MAC=00:09:5b:3b:90:23:00:50:7f:b4:78:58:08:00 
    SRC= DST= LEN=48 TOS=0x00 PREC=0x00 
    TTL=118 ID=60157 PROTO=TCP SPT=18959 DPT=10000 WINDOW=65535 RES=0x00 
    SYN URGP=0
  FW: IN=eth0 OUT= MAC=00:09:5b:3b:90:23:00:50:7f:b4:78:58:08:00 
    SRC= DST= LEN=64 TOS=0x00 PREC=0x00 
    TTL=40 ID=33561 DF PROTO=TCP SPT=3718 DPT=135 WINDOW=53760 RES=0x00 
    SYN URGP=0
  FW: IN=eth0 OUT= MAC=00:09:5b:3b:90:23:00:50:7f:b4:78:58:08:00 
    SRC= DST= LEN=64 TOS=0x00 PREC=0x00 TTL=40 
    ID=16697 DF PROTO=TCP SPT=3771 DPT=135 WINDOW=53760 RES=0x00 SYN URGP=0

The first looks like an attempt to expliot a Veritas remote backup facility. The other two are attempts to gain access to Windows RPC services. It's a nasty world out there...