Basic iptables tutorial with examples in Linux II

In my last post I had started a basic tutorial on iptables and implementing rules. Let me continue with examples and available options with iptables.
 

Creating user defined chains

Now by default as I had said in my last post Basic iptables tutorials in Linux I 3 chains are present in iptables which are NAT, filter and mangle but what if you want to create or add an extra CHAIN, so is it possible?
Well the answer is YES, you can create a new chain and use it as per your requirement where mostly this is done to reduce the complexity.

To create a new chain

# iptables -n INTRANET

This will create a new chain. You can verify the same

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
Chain INTRANET (o references)
target     prot opt source               destination

Now as you can see above a new chain has been created now you need to divide the traffic or rules to INTRANET chain as per your requirement. Now for our case we can take any example to show you how it works
 

Create references for user defined chain

For our scenario we want all the rules for source 192.168.0.0/24 should be under INTRANET chain so remove the complexity as I don't want to struggle find my rules in the complete rules list as it can be very hectic with more than 100 rules.

# iptables -I INPUT 1 -s 192.168.0.0/24 -j INTRANET

So as per the above rule I am creating and inserting a new rule inside INPUT chain at no. 1 position as I want to give this rule max priority which says any rule which is created with source IP 192.168.0.0/24 will be visible under INTRANET chain.

# iptables -I INTRANET -p tcp --dport telnet -j DROP

So as per the above rule I am inserting a new rule inside INTRANET chain for source 192.168.0.0/24 (as we have already defined this IP source using our last rule for INTRANET chain) connection to telnet port 23 should be dropped.
Now lets see if our rule has been implemented properly using

# iptables -L INTRANET
Chain INTRANET (1 references)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere            tcp dpt:telnet

As per the above rule list any connection coming from 192.168.0.0/24 using port 23 will be dropped.
Rules can be varied for various sport, dport, source destination inside any user defined chain.
Some more example
Block port 22 from 192.168.0.30 for the local machine inside INTRANET chain.

# iptables -A INTRANET -s 192.168.0.30 -p tcp --dport 22 -j DROP

So as you see in the above rule I am appending a new rule inside INTRANET chain for source 192.168.0.30 and blocking communication from port no. 22
 

Rename user defined chain

# iptables -E old_chain_name  new_chain_name
# iptables -E INTRANET EXTRANET
# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
EXTRANET   all  --  192.168.1.0/24       anywhere
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
Chain EXTRANET (1 references)
target     prot opt source               destination

 

Delete user defined chain

NOTE:

Make sure the chain you are deleting has no rules in it
# iptables -X INTRANET
IMPORTANT NOTE:

Default chains cannot be deleted

 

Chain Policies

By this term I mean the default policies for all the chains which most of us miss. In case you re look at the chains below, consider the highlighted term.

# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
INTRANET   all  --  192.168.0.0/24       anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     all  --  anywhere             anywhere
ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited
Chain FORWARD (policy ACCEPT)
target     prot opt source               destination
REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
Chain INTRANET (1 references)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere            tcp dpt:telnet

Now as you see by default all the chains have default ACCEPT policy which can be dangerous for critical machines as apart from any rule which you have created all other connections are allowed.
 

Change default policies of chains

Now since we have created a new chain so we should change the default policy of INPUT chain as we only want those users to communicate with us whom we have allowed in our rules

# iptables -P INPUT DROP
NOTE:

Default DROP policy may prevent typical TCP/UDP/ICMP communications

TCP - uses 3-way handshake

  1. SYN
  2. SYN-ACK
  3. ACK
# iptables -L INPUT
Chain INPUT (policy DROP)
target     prot opt source               destination
INTRANET   all  --  192.168.0.0/24       anywhere
ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED

So as you see now only those machines will be allowed to communicate with us which you will allow through rules in your machine.
 

TCP Matches - Connection Oriented

  1. TCP works on layer 4 also known as Connection oriented protocol.
  2. TCP packets contains information of sender as well as receiver.
  3. Eg: HTTP, FTP, telnet etc

The syntax used

-p tcp, --protocol tcp
--sport,--source-port
NOTE:

The source port is generally picked arbitrarily from > 1024

Example:
Block telnet connection from 192.168.1.0/24 for localhost using EXTRANET chain

# iptables -A EXTRANET -p tcp --dport telnet -j DROP

 

UDP Matches: - Connectionless

  1. UDP works on Layer 4 and is considered connection less protocol.
  2. UDP considered packets are considered much faster as compared to TCP because they are not as heavy as TCP as they do not contain much details on the packet.
  3. These are generally used for broadcasting.
  4. Eg: DNS, DHCP, syslog, NTP etc

The syntax used

-p udp, --protocol udp
--sport,--source-port - same source port as destination port

 

Internet Control Messaging Protocol (ICMP)

ICMP Types:

  • echo-request - PING
  • echo-reply - pong
  • PING - local system sends via OUTPUT chain an echo-request(PING)

Remote system received echo-request in its INPUT chain => Remote system responds with an echo-reply(Pong)
The sysntax used

-p icmp, --protocol icmp

 
For more details on icmp ping protocol usage follow the below link
Iptables rules to block/allow icmp ping request in Linux
 

Match multiple ports with fewer rules

Now suppose you want to block multiple ports more than 5-6 for the same machine so imagine you will have to write 5-6 rules which will make the rules list look messy which you can fix by using a single rule for multiple ports.
 
Let me give you an example
Block connection from 192.168.0.100 from port 22,23,80,8080 to your machine in a single rule

# iptables -A INTRANET -s 192.168.0.30 -p tcp -m multiport --dport 22,23,80,8080 -j DROP

Here in above rule I am appending a new rule in INTRANET chain for source 192.168.0.30 defining that the rule is for multiple ports using -m switch for --dport 22,23,80,8080 to drop any connection from these ports.
Lets see if our rule was implemented properly

# iptables -L INTRANET
Chain INTRANET (1 references)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere            tcp dpt:telnet
DROP       tcp  --  192.168.0.30         anywhere            multiport dports ssh,telnet,http,webcache

Block the above ports for all machines from source 192.168.0.0/24

# iptables -A INTRANET -p tcp -m multiport --dport 22,23,80,8080  -j DROP
NOTE:

Maximum of 15 ports can be blocked or permitted in a single rule

 

MAC address level filtering

Suppose you have blocked any source with IP 192.168.0.100 from connecting your machine, but what if it chages its ip to some other range. In that case your machine is not secure any more. So for these cases we use MAC address filtering where you block the MAC ID of the source machine from connection. This is a layer 2 blocking i.e. Data Link Layer
Block connection from machine with mac id 00:0C:29:BD:AC:81 from connecting your machine using port 23

# iptables -A INPUT -p tcp -m mac --mac-source 00:0C:29:BD:AC:81 --dport 23 -j DROP
# iptables -L INPUT
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
DROP       tcp  --  anywhere             anywhere            MAC 00:0C:29:BD:AC:81 tcp dpt:telnet

 

Log iptables traffic

Now how do you collect logs for all the rules in your iptables? 
By default all the generated logs are stored in /var/log/messages and it not easy to look out for particularly iptables log inside /var/log/messages as it stores many other logs as well. So it is always a good idea to use a different file for iptables logs
 
NOTE: To enable logging by firewall we need to enable kernel level logging in the linux machine
Follow these steps for the same

# vi /etc/rsyslog.conf (In RHEL 5 or older /etc/syslog.conf) #### RULES ####
# Log all kernel messages to the console.
# Logging much else clutters up the screen.
kern.*                                                 /var/log/firewall.log

Uncomment the above line and make necessary change as shown with blue color

# Log anything (except mail) of level info or higher.
# Don't log private authentication messages!
*.info;mail.none;authpriv.none;cron.none;kern.none                /var/log/messages

Add an extra word "kern.none" as shown above
Restart the services

# service rsyslog restart

For RHEL 5 or older

# service syslog restart

Create a new rule to log all the taffic for telnet port

# iptables -A INPUT 1 -p tcp --dport telnet -j LOG

Now I will try to do telnet to my linux box(10.10.20.26) from source (10.10.20.30)
Let us check the log generated in firewall.log

# less /var/log/firewall.log
Sep 16 12:23:34 localhost kernel: IN=eth0 OUT= MAC=00:0c:29:bd:ac:80:ac:16:2d:00:00:87:08:00 SRC=10.10.20.30 DST=10.10.20.26 LEN=52 TOS=0x00 PREC=0x00 TTL=64 ID=12317 DF PROTO=TCP SPT=58754 DPT=23 WINDOW=8192 RES=0x00 SYN URGP=0

 
Examples
Log all the traffic for ssh and telnet port on 10.10.20.26

# iptables -A INPUT -d 10.10.20.26 -p tcp -m multiport --dport 22,23 -j LOG
# less /var/log/firewall.log
Sep 16 12:32:22 localhost kernel: IN=eth0 OUT= MAC=00:0c:29:bd:ac:80:ac:16:2d:00:00:87:08:00 SRC=10.10.20.30 DST=10.10.20.26 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=4022 DF PROTO=TCP SPT=58707 DPT=22 WINDOW=256 RES=0x00 ACK URGP=0
Sep 16 12:32:22 localhost kernel: IN=eth0 OUT= MAC=00:0c:29:bd:ac:80:ac:16:2d:00:00:87:08:00 SRC=10.10.20.30 DST=10.10.20.26 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=4023 DF PROTO=TCP SPT=58707 DPT=22 WINDOW=246 RES=0x00 ACK URGP=0

So as you see a log has been reported for the attempt of connection inside firewall.log
But again what if you have created a log entry for multiple ports and sources so for those cases you can use --log-prefix to distribute the log traffic and makes easy visibility inside log.
 

LOG PREFIX

Examples:

# iptables -A INPUT -d 10.10.20.26 -p tcp -m multiport --dport 22,23 -j LOG --log-prefix "SSH ACCESS ATTEMPT: "
# less /var/log/messages
Sep 16 12:34:49 localhost kernel: SSH ACCESS ATTEMPT: IN=eth0 OUT= MAC=00:0c:29:bd:ac:80:ac:16:2d:00:00:87:08:00 SRC=10.10.20.30 DST=10.10.20.26 LEN=40 TOS=0x00 PREC=0x00 TTL=64 ID=11101 DF PROTO=TCP SPT=58707 DPT=22 WINDOW=2429 RES=0x00 ACK URGP=0
Sep 16 12:34:49 localhost kernel: SSH ACCESS ATTEMPT: IN=eth0 OUT= MAC=00:0c:29:bd:ac:80:ac:16:2d:00:00:87:08:00 SRC=10.10.20.30 DST=10.10.20.26 LEN=92 TOS=0x00 PREC=0x00 TTL=64 ID=11102 DF PROTO=TCP SPT=58707 DPT=22 WINDOW=2429 RES=0x00 ACK PSH URGP=0

We can create a new chain which will contain all the log related rules

# iptables -N LOGGER
# iptables -I INPUT 1 -j LOGGER

So here we are creating a refrence where all the INPUT chain traffic will take the refrence of LOGGER chain for additional rules.

# iptables -I LOGGER -m multiport --dport 80,443,22,23 -j LOG

Here we are creating a rule for LOGGER chain to log all the traffic on ports 80,443,22 and 23
Please let me know your suggestion and feedback.
Related Articles
Iptables rules to allow/block ssh incoming/outgoing connection in Linux
Iptables rules to block/allow icmp ping request in Linux
iptables rules for Samba 4 in Red Hat Linux
Iptables for Samba server