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
Allowing Established and Related connections
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