In the previous post, we talked about how to Secure Linux Server Using Hardening Best Practices, some people asked me about the 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 Works
- 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 a User Defined Chain
- 14 Setting The 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 Works
Iptables firewall functions are built on Netfilter framework that is available 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 firewalls:
A stateless firewall examines only the header of a packet for filtering. It sees each packet in isolation and so it can’t 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 enables 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 currently processed packet satisfies the selection criteria of a rule, then the action specified by that rule is carried out.
The actions can be: accept, reject, ignore 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 managed 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 tables that can carry 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 includes 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, it will be handled 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 to another host.
Each chain in the filter table has a policy. The policy is the default action taken.
The policy could be DROP, REJECT, and ACCEPT.
The ACCEPT policy allows the packets to pass the firewall. The DROP policy drops a packet without informing the client. The REJECT policy also drops the packet, but it sends an ICMP packet to the client to inform.
From a security perspective, you should deny all traffic by default and open the host only for the traffic which you have explicitly granted access.
Adding iptables Rules
To add a new rule, you can simply add one to a chain with the iptables command:
$ iptables -A INPUT -i eth1 -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 means which device will be used for the traffic to enter the host. If no device specified, the rule will be applied to all incoming traffic regardless the devices.
The -p flag specifies the protocol of the packets you are filtering, which is TCP in our case.
The –dport flag informs iptables to select only packets destined to port 80.
The -d selects only those packets destined to the specified IP address 192.168.1.2. If no destination IP address specified, the rule would apply to all incoming traffic on eth1 regardless of IP address.
The -j specifies the action or the JUMP action to do, here we are accepting the packets using the 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 to 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 is used for the device used for outgoing traffic.
The -sport flag is used for the source port from which the HTTP or HTTPS traffic comes from.
You can use the service name like http or https instead of the numeric port number on sport or dport. The service names can be found 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, it is added to the end of the chain.
You can use the -I flag to add rules which insert rules into the top of the chain of current rules.
The sequence of the rules matters. The rules are checked in the order they are added.
You can insert your rules exactly where you want using the I flag.
Look at the following rules to understand how rules ordering matters:
$ iptables -I INPUT 3 -i eth1 -p udp -j ACCEPT
$ iptables -I INPUT 4 -i eth1 -p udp --dport 80 -j DROP
The first rule accepts all UDP traffic comes to eth1, and the number 3 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 4 after the chain means it is the second rule in the INPUT chain.
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 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 listing another table rules?
To list a specific table, use the -t flag.
$ iptables -L -t nat
Here we list the rules in nat table.
Iptables User Defined Chain
To create a user defined chain, use the -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 a 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 The 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 gives 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 which leads to 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 to drop all incoming ICMP traffic 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.
We will create some chains to hold the ICMP-related rules.
I will create two chains. The first one, I will call it MY_IN to handle incoming ICMP traffic. The second one will be MY_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 MY_IN
$ iptables -N MY_OUT
Then we will redirect the traffic to these defined chains:
$ iptables -A INPUT -p icmp -j MY_IN
$ iptables -A OUTPUT -p icmp -j MY_OUT
When ICMP traffic enters the INPUT chain, it is directed to the user-created chain MY_IN; and when it is received by the OUTPUT chain, it will be handled by the MY_OUT chain.
ICMP Message Types
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 be applied to specific 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 MY_IN -p icmp --icmp-type 8 -j DROP
Here we add our first rule to MY_IN chain which will drop echo request.
Now we should write rules to allow the following:
- Inbound echo reply
- Time exceeded
- Destination unreachable messages
$ iptables -A MY_IN -i eth0 -p icmp --icmp-type 0 -m state --state ESTABLISHED,RELATED -j ACCEPT
$ iptables -A MY_IN -i eth0 -p icmp --icmp-type 3 -m state --state ESTABLISHED,RELATED -j ACCEPT
$ iptables -A MY_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:
The NEW means a new initiated connection.
The ESTABLISHED means an existing connection that is in the process of transferring data.
The RELATED means 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.
The following rule accepts the echo ICMP request so ping command is allowed.
$ iptables -A MY_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, so instead of using a ready script 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.
Iptables can be used to select packets with specific TCP flags using the –tcp-flags flag.
$ iptables -A INPUT -p tcp --tcp-flags ALL SYN -j DROP
This rule looks at all TCP flags (SYN, ACK, FIN, RST, URG, and PSH) and matches 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 they are attempts by attackers to gather some 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.
First, create a user defined chain to redirect traffic to it:
$ iptables -N BAD_FLAGS
Then we will redirect all TCP traffic to this chain to be processed.
$ iptables -A INPUT -p tcp -j BAD_FLAGS
When the traffic is redirected to a user-defined chain by a rule, it will be processed by 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 our chain will return to the INPUT chain.
$ iptables -A BAD_FLAGS -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
We can add rules for other bad 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, it is impossible to find single FIN flag packets in normal TCP/IP connections, 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 which are 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, and 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 -i eth0 -p tcp --syn -m limit --limit 10/second -j ACCEPT
Here we used the special TCP option –syn, this option 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, so all 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 the 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 iptables firewall easy. Keep coming back.