Using iptables to protect a Linux workstation

Using iptables to protect your Linux workstation

Purpose of this document

Here I describe how I use iptables to protect my Linux workstation against unwanted access from the internet.

What I want to protect

Until autumn 2003 my Linux workstation connected to the internet via a ISDN-line. Locally I use a 10 Mbps Ethernet network.

I do not provide any services to the internet (no webserver, no ftp server, etc.)

At this moment I connect to the internet via ADSL. My PC and the ADSL-modem are connected through a wireless LAN.

Some other PC's are also connected to the WLAN.


How I have set up the firewall

When I used ISDN the firewall appeared to be very needed, although I only was connnected to the internet during rather short periods.
At this moment the firewall seems to be redundant: the ADSL-modem provides firewall functionality. But I still use it: beter use too much security than too little.

I started by reading the IPTables-HOWTO. For ease of development I made two Perl-scripts:

  • iptables_clear_all
    This script removes all iptables.
  • iptables_mkruleset
    This scripts reads from its standard input some lines and transforms them into calls to iptables to create a table. If needed the table is created first. It uses the Perl-module Net::DNS (from

The firewall is set up in the script init_packet_filtering.

It starts with the lines

1 IPTABLES=/usr/sbin/iptables

2 ETCDIR=/usr/local/etc/firewall

3 INSMOD=/sbin/insmod

4 $ETCDIR/iptables_clear_all

5 $INSMOD ip_conntrack
6 $INSMOD ip_conntrack_ftp
Lines 1-3 defines some variables, line 4 clears the iptables rules and lines 5-6 insert some needed modules.

iptables uses 3 standard tables

  • OUTPUT: all packets which are put out are filtered through this filter.
    The default policy is accept all. As I trust my own workstation I keep it this way.
  • FORWARD: used for all packets which enter this system via one interface and must be forwarded to an other interface.
    My intention was to use my Linux station as a router to the internet for the other computers connected to ethernet. Therefore I accept all packets.
    Alas: the ADSL-modem does not accept an other router on the local net. So this does not work.
  • INPUT: mentioned later on


Accepting all forwarding is done via

$ETCDIR/iptables_mkruleset FORWARD <<EOT
--jump ACCEPT

Note that referencing tables which are not defined is not allowed. So the definition of tables in the script is in an different sequence than mentioned here

I started with a very restricting set of rules and logging all offending packets. By using the applications I need (a browser, telnet, ssh, ftp and so on) and inspecting the logging I created rules which enabled only those applications.


The INPUT-table is the most interesting: all packets which are directed to the computer itself are filtered through this table.

1 $ETCDIR/iptables_mkruleset INPUT <<EOT
2 --in-interface et+ --jump ACCEPT
3 --in-interface lo+ --jump ACCEPT
4 --source --jump ACCEPT
5 --protocol icmp --jump checkicmp
6 --protocol tcp --jump checktcp
7 --protocol udp --jump checkudp
8 --jump denyall

Line 2 and 3 accept all packets which enter the computer via the ethernet interface or the local loop interface (localhost). Line 4 accepts all packets from computers on the wireless LAN.
Three protocols (ICMP, TCP and UDP) are checked by their own tables. All other packets are handled by the table 'denyall'


The denyall table is defined as follows:


$ETCDIR/iptables_mkruleset denyall <<EOT
--match limit --limit 100/hour --jump LOG --log-ip-options --log-prefix 'FIREWALL '
--jump DROP

The second line take cares of

  • logging all offending packets via syslog
  • prefixed by the text 'FIREWALL'
  • with all ip information
  • and a limit of 100 packets each hour to prevent flooding syslog
After logging all packets are dropped.


ICMP is used by a.o. ping and traceroute. I do not want to be pinged but I want to be able to use ping and traceroute. Therefore I needed to enable some ICMP-types which are sent by pinged hosts.

$ETCDIR/iptables_mkruleset checkicmp <<EOT
--protocol icmp --icmp-type echo-reply --jump ACCEPT
--protocol icmp --icmp-type destination-unreachable --jump ACCEPT
--protocol icmp --icmp-type ttl-exceeded --jump ACCEPT
--jump denyall


UDP is rather simple: only responses from DNS-servers and time-servers are accepted. They are handled by their own tables.

$ETCDIR/iptables_mkruleset checkudp <<EOT
--protocol udp --source-port 53 --jump checkDNS
--protocol udp --source-port 37 --jump checkTIME
--jump denyall


checkDNS checks if the responses are from the DNS-servers from my ISP. All others are rejected.
$ETCDIR/iptables_mkruleset checkDNS <<EOT
--protocol udp --source --jump ACCEPT
--protocol udp --source --jump ACCEPT
--jump denyall


checkTIME accepts only responses from the three timeservers I periodically use to adjust the clock of my computer.
$ETCDIR/iptables_mkruleset checkTIME <<EOT
--protocol udp --source --jump ACCEPT
--protocol udp --source --jump ACCEPT
--protocol udp --source --jump ACCEPT
--jump denyall


TCP is more complicated.

1 $ETCDIR/iptables_mkruleset checktcp <<EOT
2 --protocol tcp --destination-port 113 --jump ACCEPT
3 --protocol tcp --match state --state ESTABLISHED --jump ACCEPT
4 --protocol tcp --match state --state RELATED --jump ACCEPT
5 --protocol tcp ! --syn --jump ACCEPT
6 --jump denyall
Line 2 is needed because otherwise applications as telnet and ssh take a very long time to establish the connection.
Line 4 states that all packets which belong to an already establised connection are accepted. Line 5 says the same about related connections.
Related connections are a.o. used by ftp. ftp uses two connections: one for the commands and for each filetransfer an other connection.

Line 5 accepts all packet without a SYN-bit. As the first packet which starts a connection always carries a SYN-bit, it means that no connections from outside can be set up. Now I am writing this I think that specifying both line 3 and 5 is overdone but this I must test.
All other packets are refused.

Routing and Masquerading

I wanted access to the internet for my other computers (in the Simple configuring my workstation as router did not work. Adding a static route on the modem did not work either.
  • I could not ping the modem from a computer on the cabled Ethernet
  • ethereal showed packets sent to the modem but no answers
  • when I use a ping from the modem to a it works fie
So my conclusion was: the routing on the modem does not work correctly.
Via a Linux forum I got the suggestion to use network address translation (NAT) and Masquerading plus a link to a clear HOWTO. I tried it and it worked fine.
So I added at the end of the script

Nice and simple!


When I still used ISDN, I noticed about 10-20 messages each week in the logging. So the firewall appeared to be needed. Now I do not receive any offending packages: the ADSL-modem seems to block them