In the previous post, we talked about how to Secure Linux Server Using Hardening Best Practices, some people asked me about firewall section which was a brief introduction about iptables firewall. Today we will discuss in detail the Linux iptables firewall and how to secure your server traffic using that awesome firewall.
Table of Contents
- 1 Iptables on CentOS 7
- 2 How Linux Firewall Work
- 3 iptables Firewall Tables
- 4 Table Chains
- 5 Chain policy
- 6 Adding iptables Rules
- 7 Iptables Rules Order
- 8 List iptables Rules
- 9 Deleting Rules
- 10 Replacing Rules
- 11 Listing specific table
- 12 Iptables User Defined Chain
- 13 Redirection to User Defined Chain
- 14 Setting Default Policy for Chains
- 15 Handling ICMP Traffic
- 16 Blocking Bad Flags
- 17 SYN Flooding
- 18 Drop INVALID State Packets
- 19 Drop Fragmented Packets
- 20 Save iptables Rules
Iptables on CentOS 7
If you are using CentOS 7, you will find that firewalld was introduced to manage iptables, so if you want to go back to iptables you have to stop and mask firewalld.
$ systemctl stop firewalld
$ systemctl mask firewalld
Then install iptables service and enable it:
$ yum install iptables-services
$ systemctl enable iptables
Then you can start it:
$ systemctl start iptables
How Linux Firewall Work
Iptables firewall functions are built on Netfilter framework that exists in the Linux kernel. Netfilter was written by an awesome guy called Rusty Russell.
Netfilter allows Linux to perform packet filtering and shaping at a kernel level.
There are two types of packet-filtering firewalls exist, stateful and stateless.
A stateless packet filtering firewall examines only the header of a packet for filtering. It sees each packet in isolation and thus has no way to determine if a packet is part of an existing connection or not.
A stateful firewall keeps information about the status of the connections passing through it. This allows the firewall to filter on the state of the connection, and this gives more control over the traffic.
Netfilter works by referring to a set of tables. These tables contain chains, and chains contain individual rules.
If the current packet being processed satisfies the selection criteria of a rule, then the action specified by that rule is carried out.
The actions can be: ignore the packet, accept the packet, reject the packet, or pass the packet on to other rules for more processing.
Any network connection between two hosts is a combination of a source IP address, source port, destination IP address, and destination port. Iptables can use these values to filter traffic coming in and out of hosts and something more which is the protocol type.
Netfilter is controlled and configured by the iptables command.
Before we start writing firewall commands we need to understand the firewall structure a bit so we can write firewall rules easily.
iptables Firewall Tables
Netfilter has three built-in tables that can hold rules for processing.
The iptables filter table is the default table used for all rules related to the filtering of your traffic.
The second is nat table, which handles NAT rules, and the last is the mangle table, which covers a variety of packet alteration functions.
The iptables rules are broken down into groups called chains. Each table contains default chains that are built into the table.
You can also create your own chains on each table to hold additional rules.
The built-in chains in the filter table are FORWARD, INPUT, and OUTPUT.
If a packet is coming to the host, then it needs to be evaluated by the rules in the INPUT chain.
If the packet is generated by the host itself and going out, then it needs to be evaluated by the rules in the OUTPUT chain.
The iptables FORWARD chain is used for handling packets that have accessed the host but are destined for another host.
Each chain in the filter table has a policy. A policy is the default action taken.
You can use these policies for packets DROP, REJECT, and ACCEPT.
The ACCEPT policy accepts the traffic and allows it to pass through the firewall. The DROP policy drops a packet without notifying the sender. The REJECT policy also discards the packet, but it sends an ICMP packet to the sender to tell him about the rejection.
From a security perspective, you should deny all traffic by default and open the host to only the traffic to which you have explicitly granted access.
DROP for the INPUT and OUTPUT chains means incoming and outgoing traffic are not allowed unless you explicitly add rules to allow the traffic.
Adding iptables Rules
To add a new rule, you can simply add one to a chain with the iptables command:
$ iptables -A INPUT -i eth0 -p tcp --dport 80 -d 192.168.1.2 -j ACCEPT
Let’s break this command into pieces so we can understand everything about it.
The –A means we are adding a new rule. By default, all new rules are added to filter table unless you specify another table.
The -i flag specifies which device the traffic will use to enter the host. If you do not specify a device, iptables assumes the rule applies to all incoming network traffic from all devices.
The –p flag specifies the protocol of the packets you are filtering, which is TCP in our case.
The –dport flag tells iptables to select only packets destined for port 80.
The -d selects only those packets destined for the specified IP address, 192.168.1.2. If you do not specify a destination IP address, then iptables would apply this rule to all incoming traffic on eth0 regardless of IP address.
The last flag in the rule, -j, specifies the action or the JUMP action to do, here we are accepting the packets using accept policy.
The above rule allows incoming HTTP traffic which is on port 80. We can add another rule to allow HTTPS traffic which is on port 443.
$ iptables -A INPUT -i eth0 -p tcp --dport 443 -d 192.168.1.2 -j ACCEPT
Easy, right? This time the only difference is the destined port 443.
Now we are allowing both HTTP and HTTPS, but this is only the incoming traffic, what about allowing outgoing traffic?
$ iptables -A OUTPUT -o eth0 -p tcp --sport 80 -j ACCEPT
$ iptables -A OUTPUT -o eth0 -p tcp --sport 443 -j ACCEPT
The -A flag is used to add rules to the OUTPUT chain.
The -o flag indicates traffic outgoing on the specified interface instead of -I flag in the previous rules.
The -sport flag, which defines the source port from which the HTTP or HTTPS traffic comes from.
You can replace the numeric port number on sport or dport with the service name like http or https. These service names are defined in /etc/services file.
It is recommended to use the service name rather than a port number, which makes reading rules easier.
Iptables Rules Order
When you add a rule using -A flag, it is added to the end of the current rules in a chain.
You can also add rules using the -I flag, which adds rules to the top of the chain of current rules.
The sequence of the rules matters. The rules are checked in the order they are added.
With the -I flag you can also add a rule in a chain using a line number, so we can specify where exactly our rules placed.
Look at the following rules to understand how rules ordering matters:
$ iptables -I INPUT 1 -i eth0 -p tcp -j ACCEPT
$ iptables -I INPUT 2 -i eth0 -p tcp --dport 80 -j DROP
The first rule accepts all TCP traffic comes to eth0, and the number 1 after the chain means to insert the rule in the first line of INPUT chain.
The second rule drops the traffic that enters port 80, and the number 2 after the chain means it is the second rule in the INPUT chain.
What will happen here is the first rule will accept all the traffic, then the second rule will be ignored because the first rule already accepts all the traffic so the second rule here makes no sense.
Your rules should make sense since the order of the rules in the chain matters.
List iptables Rules
You can list the rules in a chain using -L flag:
$ iptables -L INPUT
You can also show the line numbers for rules using –line-numbers:
$ iptables -L INPUT --line-numbers
The list shows the services names, you can show port numbers instead using –n option:
$ iptables -L INPUT -n --line-numbers
This will make the listing faster because it prevents iptables from DNS resolution and service lookups.
You can list all rules for all chains like this:
$ iptables -L -n --line-numbers
Each rule has an associated counter that tracks how many bytes and packets have been processed by that rule. You can see these counters using -v flag:
$ iptables -L -v
Also, you can reset the counters to zero using -Z flag.
Now we can add a new rule to any chain we want, we can insert the rule in a specific order and we can list the rules for any chain or all chains, but what about deleting a rule that you no longer need?
You can delete a rule using -D flag:
$ iptables -D INPUT -i eth0 -p tcp --dport https -d 192.168.1.2 -j ACCEPT
This command will delete the HTTPS rule that you specified earlier. You must match the exact specifications of the rule to be deleted otherwise the deletion will fail.
So make sure of the rule specification by listing it, then delete it.
You can delete the rule using the order number instead of writing the rule specifications.
$ iptables -D INPUT 2
You can delete all rules in a specific chain using -F flag which means flush all rules.
$ iptables -F INPUT
If you forget to mention the chain name when using -F flag, then all rules in all chains will be flushed.
You can replace existing rules with your own rule using -R flag:
$ iptables -R INPUT 1 -i eth0 -p tcp --dport https -d 192.168.1.2 -j ACCEPT
This command will replace the first rule in INPUT chain with the typed rule.
Listing specific table
Till now we list chains from the filter table which is the default table as we said. What about list another table rules?
You can specify the table using -t flag.
$ iptables -L -t nat
Here we list the rules in nat table.
Iptables User Defined Chain
You can create a new chain using -N flag.
$ iptables -N MY_CHAIN
Also, you can rename it using -E flag.
$ iptables -E MY_CHAIN NEW_NAME
And you can delete the user-defined chain using -X flag.
$ iptables -X MY_CHAIN
If you don’t mention the chain name when using -X flag it will delete all user-defined chains.
You can’t delete built-in chains like INPUT and OUTPUT.
Redirection to User Defined Chain
You can redirect packets to a user-defined chain like built-in chains using -j flag.
$ iptables -A INPUT -p icmp -j MY_CHAIN
So all incoming ICMP traffic will be redirected to the newly created chain called MY_CHAIN.
Setting Default Policy for Chains
You can use the -p flag to set the default policy for a specific chain. The default policy could be ACCEPT, REJECT and DROP.
$ iptables -P INPUT DROP
So now the input chain will drop any packet come unless you write a rule to allow any incoming traffic.
Handling ICMP Traffic
ICMP provides error, control, and informational messages such as the messages used by the ping command.
This is important for network troubleshooting and diagnostics, but the attacker can do some malicious things with it like:
- Sending huge amount of ICMP packets that leads to consuming the available bandwidth resulting in a Denial of Service.
- Sending forged ICMP echo packets to network broadcast addresses. The broadcast addresses reply with ICMP echo reply packets, resulting in a Denial of Service.
- Sending an ICMP echo packet larger than the maximum IP packet size that may crash your system.
You can prevent all these attacks using iptables firewall.
A good solution is all incoming ICMP traffic should be dropped except responses to outgoing connections. All incoming ping packets are dropped, but incoming ping reply packets that are in reply to pings generated on the local host are accepted.
You want to create some chains to hold the ICMP-related rules.
I will create two chains. The first one, I have called ICMP_IN to handle incoming ICMP traffic. The second I will call it ICMP_OUT to handle outgoing ICMP traffic. User-Defined chains enable you to structure your rules and allow you to group related rules that handle specific traffic types.
$ iptables -N ICMP_IN
$ iptables -N ICMP_OUT
Then we will redirect the traffic to those defined chains:
$ iptables -A INPUT -p icmp -j ICMP_IN
$ iptables -A OUTPUT -p icmp -j ICMP_OUT
Now when ICMP traffic is received by the INPUT chain, it is directed to the user-created chain ICMP_IN; and when it is received by the OUTPUT chain, it is handled by the ICMP_OUT chain.
The ICMP Message Types are
0 Echo Reply
3 Destination Unreachable
4 Source Quench
8 Echo Request
11 Time Exceeded
12 Parameter Problem
14 Timestamp Reply
15 Information Request
16 Information Reply
The iptables rules can target individual ICMP message types by selecting only ICMP traffic with the -p icmp flag in combination with the –icmp-type flag to select the particular ICMP message type.
$ iptables -A ICMP_IN -p icmp --icmp-type 8 -j DROP
Here we add our first rule to ICMP_IN chain which will drop echo request.
Now we should write rules to allow inbound echo reply, time exceeded, and destination unreachable messages:
$ iptables -A ICMP_IN -i eth0 -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j ACCEPT
$ iptables -A ICMP_IN -i eth0 -p icmp --icmp-type 3 -m state --state ESTABLISHED,RELATED -j ACCEPT
$ iptables -A ICMP_IN -i eth0 -p icmp --icmp-type 11 -m state --state ESTABLISHED,RELATED -j ACCEPT
Using the -m state allows you to perform state inspection and matching on the incoming packets, which is one of the key features of a stateful packet-filtering in iptables.
There are four states: NEW, ESTABLISHED, RELATED or INVALID.
The NEW connection state indicates a freshly initiated connection.
The ESTABLISHED connection state indicates an existing connection that is in the process of transferring data.
The RELATED state refers to a connection that is used to facilitate another connection.
The INVALID state refers to a connection that has been seen to have problems in processing packets.
Back to our rules. It does not allow NEW connections using ICMP to be made. That means attempts to ping that host will result in an error.
Now you add the rules to take care of the outbound ICMP traffic in case you want to ping remote hosts.
$ iptables -A ICMP_OUT -o eth0 -p icmp --icmp-type 8 -m state --state NEW -j ACCEPT
We are not discussing how to disable ICMP packets here in specific, we are investigating how iptables firewall works. There are a lot of firewalls you can use that build upon iptables, so instead of using a ready product without understanding what going on, you should be able to add your own rules and remove unnecessary ones or modify existing rules to fit your needs.
Blocking Bad Flags
When a new TCP connection is created, a process that is commonly referred to as the three-way handshake occurs.
You send SYN TCP packet, then the receiver sends as a response SYN ACK packet back to you then you send ACK to it. This is the complete three-way handshake.
You can use iptables to select packets with particular TCP flags using the –tcp-flags flag.
$ iptables -A INPUT -p tcp --tcp-flags ALL SYN -j DROP
This rule means look at all TCP flags (SYN, ACK, FIN, RST, URG, and PSH) and match the packets that have the flag SYN set.
Some combinations of flags that you want to block with your iptables firewall. These flags are not actually attacks, but rather more likely to be attempted by attackers to determine more information about your server using some tools like nmap.
Some of the well-known bad flag combinations are SYN/FIN, which is used by a variety of network scanners to perform operating system detection.
I will start by adding a chain to hold the bad TCP flag rules.
$ iptables -N BAD_FLAGS
Then we will redirect all TCP traffic to the bad TCP flags rules to be processed.
$ iptables -A INPUT -p tcp -j BAD_FLAGS
When traffic is redirected to a user-defined chain by a rule, it will be processed against all the rules in the new chain and then return to the chain that redirected it to be processed by the next rule in sequence. So all traffic comes to BAD_FLAGS chain and then return to the INPUT chain.
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
Other variations on the SYN/FIN flag combination are used for similar purposes.
SYN/RST, SYN/FIN/PSH, SYN/FIN/RST, and SYN/FIN/RST/PSH
We can add rules for those combinations too.
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN,PSH SYN,FIN,PSH -j DROP
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN,RST SYN,FIN,RST -j DROP
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN,RST,PSH SYN,FIN,RST,PSH -j DROP
Also, we can’t find in normal TCP/IP connections single FIN flag packets, so we can add a rule for that:
$ iptables -A BAD_FLAGS -p tcp --tcp-flags FIN FIN -j DROP
We also should drop null packets. Those packets are generally used for other forms of network probing used by scanning tools such as nmap.
$ iptables -A BAD_FLAGS -p tcp --tcp-flags ALL NONE -j DROP
$ iptables -A BAD_FLAGS -p tcp --tcp-flags ALL FIN,URG,PSH -j DROP
$ iptables -A BAD_FLAGS -p tcp --tcp-flags ALL SYN,RST,ACK,FIN,URG -j DROP
The attacker sends a packet with the SYN flag. The victim host replies with a packet with the SYN/ACK flags. The attacker not sending ACK to complete the handshake and that leaves a fully open connection till the connection reaches timeout.
The attacker sends SYN flags and as a result, the receiving host would have many opened connections.
We can use the limit module of iptables firewall to reduce the risk of this sort of attack.
$ iptables -A INPUT -p tcp -m limit --limit 10/second -j LOG
This command logs the tcp packets and limits the logging to ten entries per second.
With the same concept, we can limit the number of incoming SYN packets.
$ iptables -A INPUT -i eth0 -p tcp --syn -m limit --limit 10/second -j ACCEPT
Here we used the special TCP option –syn, which matches all packets with the ACK and RST bits cleared and SYN flag set. This is the same as –tcp-flags SYN,RST,ACK SYN.
We have limited the number of incoming SYN packets to ten per second. On a busy system, this can cause bottlenecks so this value depends on your network and performance.
If this will throttle your network you can use SYN cookies.
In /etc/sysctl.conf file and add this line
net.ipv4.tcp_syncookies = 1
Then save and reload.
$ sysctl -p
Drop INVALID State Packets
The INVALID state is not associated with any known connection. This means any incoming packets in the INVALID state are not from connections on the host and should be dropped.
$ iptables -A INPUT -m state --state INVALID -j DROP
This rule will cover all incoming packets on all interfaces and log and drop them.
Drop Fragmented Packets
Packet fragments occur when a packet is too large to be sent in one piece. The packet is broken up into small pieces called fragments that are then reassembled on the receiving host.
The -f flag tells iptables firewall to select all fragments. So if you are not using iptables as a router, you can drop fragmented packets.
$ iptables -A INPUT -f -j DROP
Save iptables Rules
All the rules we discussed will be lost if you reboot your server, so how to persist them.
You can save all of your rules using iptables-save command if you are using CentOS or Red Hat.
iptables-save > /etc/sysconfig/iptables
On CentOS 7 you can save rules like this:
$ service iptables save
You can save specific table like filter table only:
$ iptables-save -t filter
Also, you can use iptables-restore to restore rules that were saved.
I hope you find the post useful. There is more to cover about iptables and working with other tables and making iptables work as a router and much other cool stuff, maybe on future posts. Hope you find iptables firewall easy to work with.