Iptables is a packet filter included in most modern Linux distributions. It includes a lot of useful advanced features to do almost anything necessary at the firewall level, but this tutorial will focus on the basics.
All iptables rules are created and modified via passing command-line options to the iptables binary. There are also GUI utilities in some distributions to do this, but they tend to be much less powerful. For this demo, you must have permission to run iptables as root via sudo. The TAs should be able to do this on netsec-demos but you may want to try it on your own machine.
Iptables saves any rules you create only until the computer is rebooted, and so another solution is needed for permanent rule storage. Most distributions handle this by including two scripts,
iptables-restore. The tutorial will not worry about saving rulesets.
Iptables operates based on rules. The rules are applied top-down, with the first match being used. Therefore, more specific rules should be placed above more general rules. For example, a rule to allow all SSH packets should be above/before a rule to reject all packets, so that SSH connections are allowed, but nothing else. Each rule is specified by running the
iptables binary with the desired switches.
The switches specify first what table and chain a rule applies to, what packet characteristics it applies to, and to what target it should jump after determining that there is a match.
Tables and Chains
Iptables allows for different
chains. There are three main tables:
mangle. Each table has chains specific to their functions.
Users can also append new chains with special rules for different purposes. For example, in Redhat-based distributions, the GUI firewall configuration generates a custom input chain to separate GUI-generated rules from user-created rules.
filter is the default table if none is specified; this table parses all packets eventually regardless of what we do, and exists to drop or accept packets based on their characteristics. It includes 3 chains by default:
FORWARD. These chains act upon inbound, outbound, and forwarding packets (ie, when the current host is acting as a router).
nat handles network address translation, and includes two chains: prerouting and postrouting. This allows us to change the source or destination addresses based on packet characteristics. Two very common uses of this table is to allow for port forwarding or sharing an internet connection.
mangle includes the chains in both of the other tables, and allows for altering packet fields such as TOS, TTL, etc.
Command Line Options
-LList all rules
-FFlush (erase) all rules
-P <chain> <target>Set the default action for a packet when no rule matches; default is
ACCEPT, better practice is to allow all valid packets and set the policy to
-A <chain> <rule>Append a rule to the end of a chain
-D <chain> <rulenum>Delete a rule from a chain by number
-I <chain> <rulenum>Like append, but insert at rulenum location
When adding rules, switches must be used to specify what constitutes a match. The most typical switches typically match based on port, IP address, protocol, and connection state.
These are specified via the following switches:
--dport <portnumber>Match rules by destination port
--sport <portnumber>Match by source port
-d <ipaddress/hostname>Match rules by destination address
-s <ipaddress/hostname>Match rules by source address
-p <protocol>Match packets by protocol (tcp, udp, icmp, etc)
Iptables uses the
-m switch to allow for extended matching. This allows the use of specific extensions. A commonly used one is state, which allows for connection tracking.
For example, this allows us to allow only established packets inbound (ie, those that are not SYN packets for TCP), to ensure that we can always connect out but nobody else can initiate inbound connections.
Here are some common ways to use
-m state --state RELATED,ESTABLISHEDMatch packets that are part of an ongoing connection
-m state --state NEWMatch packets that are trying to establish a new connection
All rules must end with a target to jump to. This specifies what to do when a rule does match.
Here are some common targets (Not all of these can be used with all tables):
-j ACCEPTAccept / Allow a given packet
-j REJECTReject a packet with a negative response
-j DROPDrop packet on the floor - reject with no response
-j DNATChange the destination address (port forwarding)
-j MASQUERADEChange the source address / ip masquerade
Things to Try
Start the firewall lab by running
firewall-lab.bash in the labs folder of the student repository. It will tell you the hostname of the system which you will be setting up the firewall for, and also tell you how to connect to it.
On the attacker machine (the one which opens when you run
attacker# nmap <lab-hostname> -p 5000-8000
You can see that there are three ports open on this machine which are accessible from outside systems.
On the firewalled machine, we can check the outbound connectivity:
firewall# ping 184.108.40.206 PING 220.127.116.11 (18.104.22.168) 56(84) bytes of data. 64 bytes from 22.214.171.124: icmp_seq=1 ttl=54 time=13.7 ms
Let's begin configuring the firewall by blocking all inbound packets. Set the
INPUT policy as such:
firewall# iptables -P INPUT DROP
Notice that this will prevent you from connecting outbound. (Try running
ping again) Why? Response SYN-ACKs and DNS responses from remote servers are also dropped, along with all other inbound packets. Next, let's allow all related and established packets to come in, so that we can make outbound connections.
firewall# iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
Now you should be able to download files and ping, but nobody externally will be able to initiate a connection to the three running servers. You can confirm this by running nmap again, or trying to connect to any of them using
To allow specific connections to the server, add another rule to allow SYN packets with a destination port of 7777.
firewall# iptables -A INPUT -p tcp -m state --state NEW --dport 7777 -j ACCEPT
iptables is a complicated tool. When in doubt, a web search will tell you how to do whatever it is you are trying to do.
After trying the above examples, do the following:
- Block only outbound traffic to 126.96.36.199
- Allow inbound traffic to port 5555, but only from private IP address ranges.
- Redirect incoming traffic destined for port 80 to port 6666
- (Extra credit) While maintaining the above, make sure that port 6666 is still blocked from direct access
Show your work to teaching staff (they will need to run some commands in each of the containers, so please indicate which is which)