Skip Navigation

Working with iptables

At first, configuring a firewall with iptables (or ip6tables, for IPv6) will seem daunting and complicated. However, it isn’t too difficult if the correct tools are used and time is taken to understand what the configuration directives are actually doing.

Page Contents

Video Lecture


Watch at Internet Archive

Basic iptables Commands

At the most basic level, a firewall can be created with the iptables (IPv4) or ip6tables (IPv6) commands. In fact, there are a plethora of online articles and forum posts demonstrating how to build a firewall command-by-command. While it is possible to build a firewall this way, it will not be saved whenever the system is rebooted. If the commands are put into some kind of script, then that script can be re-run instead of typing them in manually each time the system is rebooted or cold booted. However, there is a much easier solution.

The iptables-restore (and corresponding ip6tables-restore) command enables the firewall to be written as a plain text file. Loading and activating the firewall only requires one command:

iptables-restore <filename>

where <filename> is the path to the firewall configuration file. By default, this command will flush the current firewall rules and replace them with the ones from the configuration file. One brute-force way to get the firewall rules loaded at boot time would be to add a script that runs this command every time the system starts. Most Linux distributions already have such a script in place, typically in the form of an iptables (and ip6tables) service.

Services for iptables

The names, configuration locations, and capabilities of services for loading an iptables firewall vary from distribution to distribution. You will need to consult your distribution’s documentation to determine how to configure the service properly. I have some general guidelines for a few distributions below.

Alpine Linux

Please see Alpine Linux Services for iptables and ip6tables.

CentOS/Red Hat Enterprise Linux 7

On CentOS 7, firewalld is the default service for managing the system firewall, but we can install the iptables-services package to use iptables instead of firewalld. Either remove the firewalld package or stop, disable, and mask the service with systemctl. Start and enable the iptables service.

Once the iptables service is running, use the iptables-restore command to load the desired configuration from a text file. Once this configuration is loaded, save it with:

service iptables save

This command will save the firewall configuration to /etc/sysconfig/iptables, where it will be loaded automatically at boot time.

CentOS/Red Hat Enterprise 8 and Later

Red Hat really wants the system administrator to use firewalld. My suggestion is to use a different Linux distribution. I don’t care for “magic” tools like firewalld, as they sometimes wind up opening or closing ports, or changing functionality, as a result of system package changes and other unrelated activities. Network security is important enough that the administrator should be configuring the firewall manually, ensuring that the only open ports are the ones that are specifically intended to be open.

Simple Firewall

This simple example firewall has default ACCEPT policies for all chains in the filter table, but will REJECT unmatched incoming packets at the end of the INPUT chain. Only packets that are related to established connections, ICMP requests (such as ping), and incoming SSH connections will be permitted to pass through the firewall.

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
-A INPUT -j REJECT --reject-with icmp-host-prohibited
-A FORWARD -j REJECT --reject-with icmp-host-prohibited
COMMIT

Figure 1 explains what each section of this firewall configuration is doing. In the top section, we specify that we are configuring the filter table, then we set the default rules and zero the packet and byte counters for the INPUT, FORWARD, and ACCEPT chains. We add rules to the INPUT chain by appending them in the order that they need to be processed. Our final INPUT rule REJECTs any packet not matched by another rule in the INPUT chain.

annotated simple iptables rules

Figure 1: Annotated version of the above set of iptables rules.

Since this system is not a router, we REJECT any attempts to FORWARD packets through it. And the end of the rule specifications for this table, we have the COMMIT statement, which applies the rules to the running firewall whenever they are loaded.

Note that I do not recommend using this example firewall as a basis for a production system! The default policies on the INPUT and FORWARD chains are too lax. If we were to forget the explicit REJECT rules at the bottom of each of those chains, then the firewall would default to allowing all traffic. A firewall that allows all traffic is equivalent to not having a firewall at all.

Incoming packets related to connections initiated by our system need to be permitted, otherwise we couldn’t receive responses to network requests. Without a rule like the following one, no outbound connections (including Web browsing) would work correctly:

-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

This directive Appends a rule to the INPUT chain that matches the state of the packet. If the state is RELATED or ESTABLISHED, jump to the ACCEPT target.

Responding to ping

The security parameters of a system determine whether or not ICMP requests will be permitted through the firewall. To permit them, use:

-A INPUT -p icmp -j ACCEPT

This directive Appends a rule to the INPUT chain for the icmp protocol. Any packet using the icmp protocol causes a jump to the ACCEPT target.

The Loopback Interface

Linux systems have a special loopback interface (lo), which enables network connections to be made between different applications running on the local system. Traffic normally should be allowed on this interface, otherwise some programs will not work properly.

-A INPUT -i lo -j ACCEPT

For the loopback interface, we Append a rule to the INPUT chain. All packets arriving on the loopback (lo) interface cause a jump to the ACCEPT target.

Allowing Incoming SSH Connections

Secure Shell (SSH) is a standard remote access protocol widely used for remote system access and administration. By default, it runs on TCP port 22. To enable it, use:

-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT

This directive Appends a rule for the tcp protocol to the INPUT chain, which matches the state of the packet. If the state is a NEW packet, also match a tcp connection with a destination port (dport) of 22, then jump to the ACCEPT target if matched.

Rejecting Packets

Sometimes, we want to send an explicit rejection message back to the sender, instead of quietly dropping a packet. We can do this using:

-A INPUT -j REJECT --reject-with icmp-host-prohibited

This directive Appends a rule to the INPUT chain that unconditionally jumps to the REJECT target, sending an ICMP host-prohibited message back to the sender. Instead of host-prohibited, we could send a wide variety of alternate messages back to the sender instead.

Creative Commons License
This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.