Iptables Essentials: The Ultimate Linux Firewall Configuration Guide

Iptables is a powerful tool for managing network security on Linux systems. It allows you to filter and control the flow of your server's incoming and outgoing network traffic. It provides you with a layer of protection against unauthorized access and malicious attacks.

For this tutorial, we are going to use an AlmaLinux 8 VPS with a created sudo user.


Core Concepts

Iptables organizes its rules into chains. Chains are sequences of rules that are executed in order. Each chain is associated with a specific type of traffic, such as incoming or outgoing traffic.

Each chain consists of a set of rules. A rule specifies the conditions under which it should be applied and the action to take for the related incoming or outgoing traffic when those conditions are met.

Iptables uses tables to organize its chains. Each table is associated with a specific type of traffic, such as TCP or UDP traffic.

Iptables defines three chains by default.

  • INPUT: All packets that are addressed to your server.
  • OUTPUT: All packets that are sent from your server.
  • FORWARD: All traffic destined for other servers that is not created on your server.

Iptables also allows you to set default policies for each chain. These policies specify the action to take when traffic does not match any of the rules in the chain.

IPv4 & IPv6

iptables only handles IPv4 traffic. For IPv6 traffic, you can use the ip6tables utility.


Installing Iptables

In most cases, Iptables is already installed on your Linux system by default.
To check if Iptables is installed on your Linux system, you can run the following command:

sudo iptables --version

If it is available on your system, you should receive a similar output:

iptables v1.8.4 (nf_tables)

Alternatively, it can be installed via the following commands:

Debian/Ubuntu

sudo apt-get install iptables

CentOS/Alma/Rocky

sudo yum install iptables

After installation, you can check the  status of Iptables by running:

systemctl status iptables

You can start and enable Iptables by running:

systemctl start iptables
systemctl enable iptables

If you wish to stop and disable it, you can run:

sudo systemctl stop iptables
sudo systemctl disable iptables

Persisting Iptables rules

Debian/Ubuntu

To save your rules and have them automatically applied at boot, you will need to install the Persistent Firewall Service.
This can be done by running the following commands:

sudo apt update
sudo apt install iptables-persistent

You can now save your rules via the following command:

sudo /etc/init.d/iptables-persistent save

CentOS/Alma/Rocky

For RHEL-based distributions, a similar service is provided - Iptables Services.
It can be installed with the following commands:

sudo dnf update
sudo dnf install iptables-services

You can also run these commands with the yum package manager.

sudo yum update
sudo yum install iptables-services

After it is installed, you can now save your rules by running:

sudo /sbin/iptables-save > /etc/sysconfig/iptables

Listing Rules

You can check your active firewall rules with the following commands:

Listing by specification

sudo iptables -S

Output:

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

The default policy for all of your chains will be to allow all incoming connections.

Listing rules as tables

sudo iptables -L

Output:

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

Setting default policies

In Iptables, default policies are used to determine what action should be taken for traffic that does not match any of the rules in a given chain.

The default policy can be set to one of the following actions:

  • ACCEPT: Accept the traffic and let it pass through the chain.
  • DROP: Drop the traffic without sending any response back to the source.
  • REJECT: Reject the traffic and send an error response back to the source.

When traffic enters a chain, Iptables evaluates each rule in the chain in the order they are defined until it finds a rule that matches the traffic. If no rule matches the traffic, then the default policy for that chain is applied.

To set the default policy of a specific chain, you can use the command below and substitute the policy and chain names:

iptables -P "chain-name-here" "policy-name-here"

An example command is:

iptables -P INPUT DROP

NOTE: Setting the default policy for your INPUT chain to DROP is a common and secure practice. It is recommended to set it so, but in advance, you will need to specify rules, which allow incoming connection from trusted sources, for example, a specific IP or subnet. This way you will ensure that you will not lock yourself out of the VPS by setting the default policy for the INPUT chain directly to DROP.


Setting rules

To set Iptables rules, you can use the following generic command:

iptables -A "chain-name-here" [options] -j <target>

You will need to specify the name of the chain you want to add the rule to, [options] are the match criteria you want to use to filter traffic, and <target> is the action to take for the matching traffic.

Some common options that can be used to specify the match criteria are:

  • -s <source>: Match traffic from a specific source IP address or network.
  • -d <destination>: Match traffic to a specific destination IP address or network.
  • -p <protocol>: Match traffic with a specific protocol, such as TCP or UDP.
  • --dport <port>: Match traffic with a specific destination port.
  • --sport <port>: Match traffic with a specific source port.


You will notice the -A flag added just after iptables.
The -A and -I options in Iptables are used to add rules to a chain, but they differ in how they add the rule to the chain.

The -A option adds the rule to the end of the chain, which means it will be evaluated after all the existing rules in the chain. This is useful for appending rules to a chain when you want to add a new rule without disturbing the existing rules.

The -I option, on the other hand, inserts the rule at a specific position in the chain. You can specify the position by using an index number, where 1 is the first rule in the chain, 2 is the second rule, and so on. This is useful when you want to add a new rule at a specific position in the chain.

Some real example commands are:

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -I INPUT 2 -p tcp --dport 22 -j ACCEPT

The first command allows incoming connections on port 80 /HTTP/ and appends the rule at the end of the chain.

The second command allows incoming ssh connection on the default ssh port /22/. The rule is inserted at the second position via the -I 2 options. If the position is not specified, it will default to 1.


Common commands


Allowing Loopback connections

Loopback connections are used for communication between processes running on the same host. By allowing loopback connections, you can enable important services and applications that rely on loopback communication to function properly.

You can allow them with the following commands:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A OUTPUT -o lo -j ACCEPT

When a new connection is established, it consists of both incoming and outgoing traffic. The incoming traffic is a response to outgoing traffic that was sent by the system initiating the connection.

  • Incoming connections

By allowing established and related incoming connections, you can enable responses to return to the initiating system and complete the connection.

You can enable incoming connections with the following command:

sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
  • Outgoing connections

By allowing established and related outgoing connections, you can enable the system to initiate new connections and communicate with other machines on the internet.

You can enable outgoing connections with the following command:

iptables -A OUTPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Dropping Invalid Packets

Dropping invalid packets is an important security measure that can help protect your system. Invalid packets are packets that do not match any known or expected protocol or do not have valid header information.

You can do this by running:

sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP

Blocking specific connections

You can block connections from specific IP addresses or whole subnets in Iptables by creating rules that match traffic from those addresses or subnets. You can either DROP it or REJECT it.

NOTE: These rules are necessary only if you have left your default policies to ACCEPT, which is not the recommended way. A common practice is to set your default policies to REJECT/DROP and instead allow access from trusted sources. You  can set the default policy of the INPUT chain to DROP with the following command:

iptables -P INPUT DROP
  • Block incoming connections from a specific IP address:
sudo iptables -A INPUT -s "ip-address-here" -j DROP

or

sudo iptables -A INPUT -s "ip-address-here" -j REJECT
  • Blocking incoming connection to a specific network interface:
iptables -A INPUT -i eth0 -s "ip-address-here" -j DROP
  • Block traffic from a specific subnet:
iptables -A INPUT -s "ip-address-subnet-here"/"prefix-length" -j DROP

or

iptables -A INPUT -s "ip-address-subnet-here"/"prefix-length" -j REJECT
  • Block traffic to a specific port
iptables -A INPUT -p tcp --dport "port-number-here" -j DROP

You can also block traffic to a port only for a specific IP address or subnet.

iptables -A INPUT -s "ip-address-here" -p tcp --dport "port-number-here" -j DROP
  • SSH

Blocking SSH access is a common security measure to prevent unauthorized access to your system through SSH.

Of course, it's a bad idea to directly block SSH connections - this will sever your connection and lock you out. You will certainly need to take the necessary precautions. You will need to allow incoming SSH connection for a specific IP address or subnet. This can be done with the following commands:

iptables -A INPUT -p tcp -s "ip-address-here" --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -s "ip-address-subnet-here"/"prefix-length" --dport 22 -j ACCEPT

To block connections, use the following command:

iptables -A INPUT -p tcp --dport 22 -j DROP

If your SSH service is running on a custom port, simply replace "22" with it.

  • HTTP/HTTPS

Blocking both HTTP and HTTPS requests can be done by creating rules that block incoming traffic to port 80 and port 443.

It's important to note that blocking HTTP and HTTPS requests can also prevent legitimate users from accessing your website.

Of course, this is a decision that depends on your use case. You most likely will need to specify an IP address or subnet, which be allowed to connect to these ports. You will need to either do this in advance or insert the rule before the DROP rule above (via the -I flag).

iptables -A INPUT -p tcp -m multiport --dports 80,443 -s "ip-address-here" -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -s "ip-address-subnet-here"/"prefix-length" -j ACCEPT

or

iptables -I INPUT 1 -p tcp -m multiport --dports 80,443 -s "ip-address-here" -j ACCEPT
iptables -I INPUT 1 -p tcp -m multiport --dports 80,443 -s "ip-address-subnet-here"/"prefix-length" -j ACCEPT

Connections can be blocked via:

iptables -A INPUT -p tcp -m multiport --dports 80,443 -j DROP

Allowing specific connections

As previously mentioned, an appropriate and secure approach would be to set your default policies to DROP or REJECT and allow connections to either specific ports or from trusted sources - a known IP address or subnet.
The implementation of this will highly depend on your own requirements.
You  can set the default policy of the INPUT chain to DROP with the following command:

iptables -P INPUT DROP
  • Allowing incoming connections from a specific IP address:
iptables -I INPUT -s "ip-address-here" -j ACCEPT
  • Allow incoming connections from a specific subnet:
iptables -I INPUT -s "ip-address-subnet-here"/"prefix-length" -j ACCEPT
  • Allow connections to a specific port
iptables -A INPUT -p tcp --dport "port-number-here" -j ACCEPT
  • Allow connections to a specific port only from trusted sources
iptables -A INPUT -s "ip-address-here" -p tcp --dport "port-number-here" -j ACCEPT
iptables -A INPUT -s "ip-address-subnet-here"/"prefix-length" -p tcp --dport "port-number-here" -j ACCEPT
  • SSH

You can allow SSH connection via the following command:

sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

The second command actually enables outgoing SSH connections for already established connections, which is mandatory if your OUTPUT policy is also set to DROP/REJECT. After all, the connection is two-way communication.

If your SSH port is not the default one, specify it by replacing 22 with it.

It is highly advisable to enable SSH connections only from trusted sources.
You can do this with these:

 Specific IP Address:

sudo iptables -A INPUT -p tcp -s "ip-address-here" --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

 Specific Subnet:

sudo iptables -A INPUT -p tcp -s "ip-address-subnet-here"/"prefix-length" --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT

 Outgoing SSH

Outgoing SSH connections can be enabled by running:

sudo iptables -A OUTPUT -p tcp --dport 22 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A INPUT -p tcp --sport 22 -m conntrack --ctstate ESTABLISHED -j ACCEPT
  • HTTP/HTTPS

You can allow both HTTP and HTTPS traffic by creating a rule that allows both ports (80 & 443):

sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
sudo iptables -A OUTPUT -p tcp -m multiport --dports 80,443 -m conntrack --ctstate ESTABLISHED -j ACCEPT

The second command is only necessary if the OUTPUT chain default policy is set to REJECT or DROP.

Specific IP Address:

iptables -A INPUT -p tcp -s "ip-address-here" -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A OUTPUT -p tcp -s "ip-address-here" -m multiport --dports 80,443 -m conntrack --ctstate ESTABLISHED -j ACCEPT

Specific Subnet:

iptables -A INPUT -p tcp -s "ip-address-subnet-here"/"prefix-length" -- -m multiport --dports 80,443 -j ACCEPT
sudo iptables -A OUTPUT -p tcp -s "ip-address-subnet-here"/"prefix-length" -- -m multiport --dports 80,443 -m conntrack --ctstate ESTABLISHED -j ACCEPT

Deleting rules

Iptables rules can be deleted by using the iptables -D command. You will need to point out the chain name and the rule specification.

The rules can be specified in two different ways.
You can either determine by its specification, for example:

sudo iptables -D INPUT -m conntrack --ctstate INVALID -j DROP

or you can determine the rule by its rule number.
An example would be:

sudo iptables -D INPUT 2

You can determine a rule's number via the following command:

sudo iptables -L --line-numbers

Flushing Chains

You are able to delete all rules in a chain by flushing it.

To flush all chains at once, run the following command:

sudo iptables -F

To only flush a specific chain, you can specify the chain just after the command above. You can do this by using any of these three:

sudo iptables -F INPUT
sudo iptables -F OUTPUT
sudo iptables -F FORWARD