FIREWALL: iptables: Mini howto (en)

From OnnoWiki
Jump to navigation Jump to search

iptables is a firewall, which is by default installed in almost all Linux distributions, such as Ubuntu, Kubuntu, Xubuntu, Fedora Core, etc. When we install Ubuntu, iptables is already installed, but by default, it allows all traffic to pass through.

Indeed, there are many and can become very complex configuration techniques for iptables. On this occasion, we will only try to perform a simple firewall / iptables configuration.

Basic Commands

You can write,

$ sudo iptables -L

This will display the existing "rules" in iptables. If we have just installed a server, usually there are no rules installed yet, we will see

Chain INPUT (policy ACCEPT)
target     prot opt source               destination 
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Basic iptables Options

Here are some basic options commonly used in configuring iptables.

-A – Add this rule to an existing chain of rules. Valid chains are INPUT, FORWARD, and OUTPUT. We usually use the INPUT chain, which impacts incoming traffic.
-L – Displays the list of existing rules in iptables.
-m state – Allows rules to be matched based on connection state. Enables the use of the -–state option.
--state – Defines a list of states for the rule to match. Some valid states are,
NEW – A new connection that has not been seen before.
RELATED – A new connection, but related to another allowed connection.
ESTABLISHED – A connection that has already been made.
INVALID – Traffic that cannot be identified for various reasons.
-m limit - Required by a rule if it wants to match within a certain time/number. Allows the use of the --limit option. Useful for limiting logging rules.
--limit – The maximum matching rate, given in the form of a number followed by "/second", "/minute", "/hour", or "/day" depending on how often we want to match the rule. If this option is not used, the default is "3/hour".
-p – The protocol used for the connection.
--dport – The destination port used by the iptables rule. It can be a single port, or a range written as start:end, which will match all ports from start to end.
-j - Jump to a specific target. iptables has four (4) default targets,
ACCEPT - Accept the packet and stop processing rules in this chain.
REJECT - Reject the packet and notify the sender that we are rejecting it, and stop processing rules in this chain.
DROP – Silently ignore the packet, and stop processing rules in this chain.
LOG - Log the packet, and continue processing rules in this chain. Allows the use of the --log-prefix and --log-level options.
--log-prefix – If logging is done, place text/writing before the record. Use quotes around the text/writing.
--log-level – Logs using the syslog level. 7 is a good choice, unless we need something else.
-i – Match if the incoming packet is from a certain interface.
-I – Insert a rule. Requires two (2) options, namely, which rule chain and the rule number. So -I INPUT 5 would insert into the INPUT chain and make it rule number 5 on the list.
-v – Displays more information on the screen. Very helpful if there are several rules that look similar when displayed without -v.

Allowing Established Connection Sessions

We can allow sessions of established connections to receive traffic, through the command,

$ sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Allow Incoming Traffic to Specific Ports.

At the beginning of the process, iptables should block all traffic. Usually, we need to work through an SSH channel, so we typically allow SSH traffic and block other traffic.

To allow incoming traffic to the default SSH port number 22, we must allow all incoming TCP traffic to port 22.

$ sudo iptables -A INPUT -p tcp --dport ssh -j ACCEPT

From the list of options above, we know that the iptables rule is set to

insert this rule into the input chain (-A INPUT) meaning we are looking at incoming traffic. check if the protocol used is TCP (-p tcp). If TCP, check if the packet is going to the SSH port (--dport ssh). If going to SSH, then the packet is accepted (-j ACCEPT).

Let's check the rule formed by the command above using the iptables -L command,

$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh

Next, we will allow all web traffic to enter, use the following command

$ sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT

Check the rule we created using the iptables -L command, as follows,

$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www

We must specifically allow TCP traffic to SSH and Web ports, but we have not blocked anything, and all incoming traffic can enter.

Blocking Traffic

If a rule has decided to accept a packet (ACCEPT), then the next rule will not affect that packet. Since the rules we created allow SSH and Web traffic, as long as the rule to block all traffic is placed last after the rules allowing SSH and Web, we will still be able to receive the SSH and Web traffic we want. So we must add (-A) a rule to block traffic at the end.

$ sudo iptables -A INPUT -j DROP
$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere

Since we do not specify an interface or protocol used, all traffic to all ports and all interfaces will be blocked, except for web and SSH.

Editing iptables

The main problem we will encounter is that the loopback port on the "lo" interface will be blocked. Therefore, we need to allow all traffic for the loopback ("lo"). This can be done by Inserting (-I) a rule in the INPUT chain for the lo interface, so it is placed at the very top.

$ sudo iptables -I INPUT 1 -i lo -j ACCEPT
$ sudo iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere            tcp dpt:www
DROP       all  --  anywhere             anywhere

If we look above, the top rule and the bottom rule are somewhat similar, to see more details of these rules, we can use the command,

$ sudo iptables -L -v
Chain INPUT (policy ALLOW 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere            state RELATED,ESTABLISHED
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:ssh
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere            tcp dpt:www
    0     0 DROP       all  --  any    any     anywhere             anywhere

We see more information here. The rule to allow loopback is very important, as many programs will use the loopback interface to communicate with each other. If loopback is not allowed, then we will likely damage those programs.

Logging / Recording

In all the examples above, all traffic is not logged. If we want to record packets that are dropped, the quickest way is,

$ sudo iptables -I INPUT 5 -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7

Please look at the top to see what happens in the logging process.

Saving iptables

If we reboot the machine we are using, then what we have done so far will be lost. Of course, we could retype all the commands we entered one by one every time we reboot, to make our lives easier, then we can use the iptables-save and iptables-restore commands to save and restore iptables.

For those of you who use Ubuntu, especially Ubuntu Fiesty, it seems that Ubuntu Network Manager (still beta) is somewhat in conflict with iptables. Therefore, it might be a good idea to bypass the Ubuntu Network Manager.

By not using Ubuntu Network Manager, we can save the iptables configuration to be started every time we boot using the command

$ sudo sh -c "iptables-save > /etc/iptables.rules"

We need to modify /etc/network/interfaces so that the iptables rules we use can run automatically. Indeed, we need to know which interface the rules we create will be used on. Typically, we use eth0. For wireless interfaces, we can check their use with the command,

$ iwconfig

We need to edit the /etc/network/interfaces file using the command

$ sudo nano /etc/network/interfaces

If we have found the name of the interface being used, then at the end of the interface we can add the command,

pre-up iptables-restore < /etc/iptables.rules

Next, we add the command after the interface is down, using the command,

post-down iptables-restore < /etc/iptables.rules

A real example of the interfaces configuration is as follows,

auto eth0
iface eth0 inet dhcp
  pre-up iptables-restore < /etc/iptables.rules
  post-down iptables-restore < /etc/iptables.rules

Startup Configuration in NetworkManager

Ubuntu Network Manager has the ability to run scripts when it activates or deactivates an interface. To save iptables rules at shutdown, and restore iptables at startup, we will create such a script. To start, we can edit the file,

$ gksudo gedit /etc/NetworkManager/dispatcher.d/01firewall

We can enter the script below through the editor, save and exit.

#!/bin/bash
if [ -x /usr/bin/logger ]; then
        LOGGER="/usr/bin/logger -s -p daemon.info -t FirewallHandler"
else
        LOGGER=echo
fi

case "$2" in
        pre-up)
                if [ ! -r /etc/iptables.rules ]; then
                        ${LOGGER} "No iptables rules exist to restore."
                        return
                fi
                if [ ! -x /sbin/iptables-restore ]; then
                        ${LOGGER} "No program exists to restore iptables rules."
                        return
                fi
                ${LOGGER} "Restoring iptables rules"
                /sbin/iptables-restore -c < /etc/iptables.rules
                ;;
        post-down)
                if [ ! -x /sbin/iptables-save ]; then
                        ${LOGGER} "No program exists to save iptables rules."
                        return
                fi
                ${LOGGER} "Saving iptables rules."
                /sbin/iptables-save -c > /etc/iptables.rules
                ;;
        *)
                ;;
esac

Finally, we need to ensure that Ubuntu Network Manager can run this script. Through the console, we can run the following command,

$ sudo chmod +x /etc/NetworkManager/dispatcher.d/01firewall

A Little Tip

If we often manually edit iptables. Frequent iptables changes usually occur during the development phase, during operational times there are actually not many changes to iptables rules. If there are quite a lot of changes, then we should add the following sentences to the /etc/network/interfaces file:

pre-up iptables-restore < /etc/iptables.rules
post-down iptables-save > /etc/iptables.rules

The sentence "post-down iptables-save > /etc/iptables.rules" will save the rules so they can be used again after booting.

Using iptables-save/restore for Rule Testing

If we are experimenting with iptables, it is advisable to use the iptables-save and iptables-restore commands to edit and test the rules we create. To edit the iptables rules we create we can use the following command (for example using gedit),

$ sudo iptables-save > /etc/iptables.rules
$ gksudo gedit /etc/iptables.rules

We will obtain a file similar to what we did,

# Generated by iptables-save v1.3.1 on Sun Apr 23 06:19:53 2006
*filter
:INPUT ACCEPT [368:102354]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [92952:20764374]
-A INPUT -i lo -j ACCEPT
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
-A INPUT -j DROP
COMMIT
# Completed on Sun Apr 23 06:19:53 2006

It appears from the file that these commands are iptables commands, without "iptables" itself. We can edit this file, and save it when finished. For testing it can be run using the command,

$ sudo iptables-restore < /etc/iptables.rules

After the test, we can save what is being tinkered with using the iptables-save command to the /etc/network/interfaces file through the command

$ sudo iptables-save > /etc/iptables.rules

More Detail About Logging

To see more details from the syslog we need to add an additional chain. Here is an example from /etc/iptables.rules showing how the iptables setup logs from syslog:

# Generated by iptables-save v1.3.1 on Sun Apr 23 05:32:09 2006
*filter
:INPUT ACCEPT [273:55355]
:FORWARD ACCEPT [0:0]
:LOGNDROP - [0:0]
:OUTPUT ACCEPT [92376:20668252]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -i eth0 -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j LOGNDROP
-A LOGNDROP -p tcp -m limit --limit 5/min -j LOG --log-prefix "Denied TCP: " --log-level 7
-A LOGNDROP -p udp -m limit --limit 5/min -j LOG --log-prefix "Denied UDP: " --log-level 7
-A LOGNDROP -p icmp -m limit --limit 5/min -j LOG --log-prefix "Denied ICMP: " --log-level 7
-A LOGNDROP -j DROP
COMMIT
# Completed on Sun Apr 23 05:32:09 2006

Notice there is a new CHAIN called LOGNDROP at the beginning of the file. The standard DROP that is usually at the bottom of the INPUT chain is now replaced by LOGNDROP and adds protocol descriptions to make the logs easier to read. Finally, we will discard/drop traffic at the end of the LOGNDROP chain. Some notes below will provide an explanation of what happens,

--limit regulates how much logging from a rule to syslog
--log-prefix "Denied..." adds a prefix to make reading the syslog easier
--log-level 7 sets the level of information detail in the syslog

Disabling the Firewall

If we need to temporarily disable/turn off the firewall, this can be done easily using the flush (-F) command, as follows,

$ sudo iptables -F

Ease of Configuration Using Graphics

For those of us who find it difficult to use the console, we will be greatly assisted by various applications to configure the firewall using graphics. One of the favorite software that might be very helpful is,

Webmin - http://www.webmin.com
Firestarter - http://www.fs-security.com

Interesting Links