Iptables is a linux firewall built into the linux kernel. It is a proven solution that has been around for quite some time (since kernel 2.4). It is used for filtering, NAT and packet mangling. We will not be going through all of the iptables features here. We will focus on the basic features that will help us secure our home router. The assumption here is that you have a router with two interfaces (or subinterfaces): one connecting it to LAN (eth0.1 in our case) and one connecting it to the internet (eth0.2 in our case). You should be familiar with it if you read my previous tutorial.
Do I need this?
It all depends on what you are trying to protect and how your network environment looks like. If you only want to copy the behaviour of the regular home router then you only need NAT rule. It will protect you against any connections initiated from the internet. There are exceptions to this rule: 1. You explicitly specify that a port is forwarded to the internal host. 2. Router and user applications support UPnP protocol (I believe it was enabled by default on my TP-LINK router). With the help of this protocol application can inform router that it needs to forward specific port to it. It happens automatically and without knowledge of the user (of course you can check the status of open ports manually in the web interface of the router). There is an implementation of upnp on linux but we will not be covering it. I am not using it and haven’t noticed any problems so far.
There are 4 tables:
- filter – used for packet filtering
- nat – used for address translation
- mangle – used for packet modification
- raw – preprocessing, packets go to this table first
I will only cover filter an nat tables (limited).
To disply contents of specific table you can issue command:
iptables -L -t table_name
To see more details (like input and output interface) you can add -v flag. Filter table is the default table. Let’s see the default contents of filter table:
root@raspberrypi:~# iptables -L -v Chain INPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain FORWARD (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes) pkts bytes target prot opt in out source destination
You can see that there are three so-called “chains”: INPUT, OUTPUT and FORWARD. INPUT chain filters packets that are destined to the machine itself (for example someone is trying to connect to server listening on the linux box or there are returning packets for application that initiated connection on this box). In other words it filters packets destined for local process. OUTPUT chain filters packets that originate from that box. FORWARD chain filters packets that are routed through the box. The linux box is not source nor destination of the packet.
Methods of applying rules
You can add rule immediately using iptables command. Please note that you need root privileges to modify iptables rules.
iptables -A INPUT -i eth0.2 -j DROP
It will cause all packets which are destined for local system and are incoming on the eth0.2 interface to be dropped. It will be applied immediately. However, it won’t persist through the reboot. You need to save them to a file first.
iptables-save > /etc/iptables
If you display contents of the /etc/iptables file you will see the skeleton of a valid configuration file that you can edit manually and apply to the running configuration.
# Generated by iptables-save v1.4.14 on Sun Dec 1 22:59:53 2013 *nat :PREROUTING ACCEPT [10:674] :INPUT ACCEPT [9:561] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] COMMIT # Completed on Sun Dec 1 22:59:53 2013 # Generated by iptables-save v1.4.14 on Sun Dec 1 22:59:53 2013 *filter :INPUT ACCEPT [9:501] :FORWARD ACCEPT [135:11104] :OUTPUT ACCEPT [377:48999] COMMIT # Completed on Sun Dec 1 22:59:53 2013 # Generated by iptables-save v1.4.14 on Sun Dec 1 22:59:53 2013 *raw :PREROUTING ACCEPT [640:55452] :OUTPUT ACCEPT [377:48999] COMMIT # Completed on Sun Dec 1 22:59:53 2013 # Generated by iptables-save v1.4.14 on Sun Dec 1 22:59:53 2013 *mangle :PREROUTING ACCEPT [640:55452] :INPUT ACCEPT [503:43636] :FORWARD ACCEPT [135:11104] :OUTPUT ACCEPT [377:48999] :POSTROUTING ACCEPT [512:60103] COMMIT # Completed on Sun Dec 1 22:59:53 2013 I will come back and explain the file syntax later. To restore the rules from the file (apply them to the running configuration) use:
iptables-restore < /etc/iptables
How do rules in a chain work
Chain in a table is a list of rules. When a packet arrives the rules are examined from top to bottom. If the criteria match, the action is taken. The packet is processed until it is dropped, rejected or accepted. If it is accepted then it moves to another table. For example, when packet is routed through my home router with NAT enabled, the packet goes through filter table, nat table, raw table and mangle table. In this case raw and mangle table will not do anything to the packet (they have default policy ACCEPT on all their chains). If you want to learn more here is a good tutorial on iptables.
ACCEPT – Packet is accepted
DROP – Packet is silently dropped
REJECT – Packet is dropped but the sender is notified by packet with RST flag sent (if it was TCP packet) or ICMP port unreachable (udp).
So should we use drop or reject?
I prefer to use REJECT in a trusted environment. It makes troubleshooting network problems easier.
In the untrusted networks I prefer to drop packets. If you reject them actively then someone can flood your host with packets destined for closed port and saturate not only your downstream bandwidth but also upstream bandwidth by forcing you to reply to those packets (or even use you in the DDoS attack).
The default policy is the action that is taken when no rules in a chain match the packet or if the rules that match don’t apply DROP, REJECT or ACCEPT policy. You can configure it using -P flag:
iptables -t table_name -P chain_name action
for example (filter is the default table)
iptable -P OUTPUT ACCEPT
Text file syntax
In the abovementioned text file the tables are marked with asterisk. After semicolon there is chain name along with the default policy. In the brackets there are number of packets and bytes that matched the policy (they are there so that you don’t lose your counters state after the reboot or configuration changes). Below them you can enter the rules you want applied to the chain. You enter them in the same way as you do in command line but without ‘iptables’ at the front. At the end of table configuration there is COMMIT keyword. See the examples below.
In order to clear all rules in a table use
iptables -t table_name -F
Please note it does not reset default policy of the chains to ACCEPT!
This is bare minimum (remember to enable ip forwarding!) I already discussed in the previous post .
iptables -t nat -A POSTROUTING -o eth0.2 -j MASQUERADE
Where eth0.2 is the interface facing the internet. If you want to write it directly to the configuration file you can do this in this manner:
... *nat :PREROUTING ACCEPT [10:674] :INPUT ACCEPT [9:561] :OUTPUT ACCEPT [0:0] :POSTROUTING ACCEPT [0:0] -A POSTROUTING -o eth0.2 -j MASQUERADE COMMIT ...
Remember you can always generate the config file using iptables-save command.
If you want to forward specifc port on your local machine use (in this case there is a web server on the 192.168.33.10 internal address that I want accessible from the internet):
iptables -t nat -A PREROUTING -i eth0.2 -p tcp --dport 80 -j DNAT --to-destination 192.168.33.10:80
or in the config file:
-A PREROUTING -i eth0.2 -p tcp --dport 80 -j DNAT --to-destination 192.168.33.10:80
Instead of specifying ACCEPT, DROP or REJECT actions you can redirect the packet to be processed by your custom chain. If your custom chain doesn’t perform any of these actions on the packet, then the packet returns to the chain it was before in.
Create custom chain by:
iptables -t table -N my_custom_chain
Iptables supports stateful firewall. What it means is that it remembers that a connection has been made and allows returning traffic. By using this you can allow connections to be initiated from one zone of your firewall to the other, but not the other way around. It is similar to the NAT: clients from LAN can easily initiate connections to the outside world, but hosts in the internet can not initiate connections to the clients in LAN unless you forward a port. There are 5 connection states in iptables:
- NEW – the packet initiates new connection
- ESTABLISHED – packet is a part of already established connection
- RELATED – some protocols, like FTP open secondary connections using data exchanged in the original connection. Iptables can detect it (for chosen protocols, like FTP) and allow such traffic
- INVALID – packet can’t be identified or doesn’t have state
- UNTRACKED – you can mark the connection as untracked in the raw table
Let’s see the example:
*filter #default policies :INPUT DROP [9:501] :FORWARD DROP [135:11104] :OUTPUT ACCEPT [377:48999] #custom chains created :lan_bad_conn - [0:0] :wan_incoming - [0:0] #allow loopback traffic so that local applications can communicate by TCP/IP protocol -A INPUT -i lo -j ACCEPT #allow ssh and icmp to our router -A INPUT -i eth0.1 -p tcp -s 192.168.33.0/24 --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT -A INPUT -i eth0.1 -p icmp -j ACCEPT #For DHCP info check the end of the post! #let's filter and log bad traffic incoming from WAN -A INPUT -i eth0.2 -j wan_incoming #and from LAN -A INPUT -i eth0.1 -j lan_bad_conn #don't forget to allow traffic to your internal server that you forwarded ports to #-A FORWARD -i eth0.2 -p tcp --dport 80 -j ACCEPT #allow initiating connections from LAN to the internet. Don't forward packets with source ip addresses that shouldn't be there. These forward rules are actually obsolete when you're behind NAT. -A FORWARD -i eth0.1 -s 192.168.33.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT #allow returning traffic -A FORWARD -i eth0.2 -m state --state RELATED,ESTABLISHED -j ACCEPT #filter and log bad traffic -A FORWARD -j lan_bad_conn # filter and log packets with source address that is not expected on our LAN. Limit filtering to 1 message per minute with 5 messages burst. Add prefix "lan_bad_conn: " to logged messages. Messages will be logged to /var/log/messages by default but that depends on the configuration of your syslog daemon. '!' means negation. -A lan_bad_conn ! -s 192.168.33.0/24 -m limit --limit 1/min --limit-burst 5 -j LOG --log-prefix "lan_bad_conn: " # filter and log bad traffic -A lan_bad_conn -m state --state INVALID,UNTRACKED -m limit --limit 1/min --limit-burst 5 -j LOG --log-prefix "lan_bad_conn: " # drop those packets -A lan_bad_conn -j DROP #Allow only returning traffic from connections established by the router itself. Log and drop the rest -A wan_incoming -m state --state RELATED,ESTABLISHED -j ACCEPT -A wan_incoming -m state --state INVALID,NEW,UNTRACKED -m limit --limit 1/min --limit-burst 5 -j LOG --log-prefix "wan_bad_incoming: " -A wan_incoming -m state --state INVALID,NEW,UNTRACKED -j DROP #commit COMMIT
You can view the current connections tracked by iptables by viewing file /proc/net/ip_conntrack
Do iptables always work?
No. Try to block DHCP requests using iptables. It won’t work. That’s why I didn’t include the rule allowing my clients to obtain ip address from DHCP server. Iptables only work with standard TCP/IP stack. However application can open raw socket which allows it bypass TCP/IP stack and create custom packets, including custom protocols. The DHCP discover packets will eventually be blocked by iptables, but they will be seen by the application first. Is it a security threat? Not so much, because you need root privileges to open raw socket. If malicious application has root privileges then your server is already fully compromised because the application will have access to everything (for example it could modify iptables rules).There is an interesting discussion on this mailing list: https://email@example.com/msg03905.html .
These are the basics of iptables that come to my mind. I plan to explain some additional, more advanced features of iptables in the future.