PolarSPARC

Fundamentals of Linux iptables Firewall


Bhaskar S 11/22/2020


Overview

In networking, a Firewall is a security protection system, through which all network traffic is routed, to monitor and control the flow of network traffic coming in or going out using a set of rules. In other words, a firewall is used to enforce the security policies in a network.

A network firewall is also referred to as a Packet Filter, which basically implies inspect and regulate all network packets traversing between the nodes in the network.

netfilter is a Linux kernel space networking framework, with various hooks for performing different network inspections and operations, such that it provides the firewall functionality required for monitoring and directing packets in the network.

iptables is a user-space command-line interface for netfilter that allows system adminstrators to add, modify, or remove packet filtering rules.

To level set on the networking basics, the following are some of terms used in this article:


Term Description
Ethernet a family of computer networking protocols used for communication between nodes in a network
NIC Network Interface Controller (or Card) is a physical device that connects a system to the network
MAC Address Media Access Control address is a globally unique address assigned to a NIC and used by the Ethernet protocol
IP stands for Internet Protocol and is the standard of internet communication
ICMP stands for Internet Control Message Protocol and is used for exchaning informational data on the success or failure of communication between systems in the network
IP Address a logical numerical label assigned to each system on the IP network
IPv4 a 32-bit IP address that is often represented using the form A.B.C.D, where A, B, C, and D can have a value from 0 through 255. Example: 192.168.1.56
TCP stands for Transmission Control Protocol and is part of the IP protocol suite that is a connection-oriented, meaning, a connection channel must be established between two systems before data can be exchanged
UDP stands for User Datagram Protocol and is part of the IP protocol suite that is a connectionless, meaning, data can be exchanged between two systems without establishing any connection channel
NAT stands for Network Address Translation and is a method of mapping an IP address to another in the network

The following diagram illustrates the OSI Network Model and the simplified TCP/IP Network Model:


Network Models
Figure.1

In the Figure.1 above, the diagram on the left is the general OSI Network Model, while the diagram on the right is the simplified TCP/IP Network Model. The following are the descriptions of the layers from the simplified TCP/IP Network Model:

The packet filtering mechanism provided by iptables is organized into 4 core components:

There are 4 types of tables defined in iptables:

There are 5 types of chains defined in iptables:

There are 3 types of targets defined in iptables:

At a high-level, when a packet arrives (or leaves) a system, the packet traverses the different layers of the network model and in turn the various tables/chains of the iptables within the layer. Within a table, the packet starts at the top of the chain and is matched against each rule in the chain (till the last rule). If a rule is matched, the corresponding target is executed. If none of the rules match, the default iptables policy is applied.

The following diagram illustrates how a packet would traverse through the core components of the iptables in the simplified TCP/IP Network Model:


Packet Traversal
Figure.2

The following is the high-level traversal of a packet from the illustration in Figure.2 above:

  1. When a packet arrives through the NIC (ingress), it traverses the PREROUTING chain of the tables raw, mangle and nat in the Data Link layer

  2. The packet then arrives at the Network layer to determine if the packet is for this system or another system by the routing module

  3. If the packet is for THIS system, then the packet traverses the INPUT chain of the tables mangle and filter at the Network layer. If the packet is not dropped or rejected it moves to the Transport layer (step 5 below)

  4. If the packet is for ANOTHER system, then the packet traverses the FORWARD chain of the tables mangle and filter at the Network layer and moves to the Data Link layer (step 7 below)

  5. In the Transport layer, the local network application process to which the packet needs to be dispatched is determined (look up) using the destination IP and port

  6. The packet is processed by the local application process in the Application layer. The local application process creates and sends a response packet back to the source IP and port. This causes the packet to traverse the OUTPUT chain of the tables raw, mangle, nat, and filter in the Network layer and moves to the Data Link layer

  7. In the Data Link layer, the packet traverses the POSTROUTING chain of the tables mangle and nat to go out through the NIC (egress)

Installation and Setup

The setup will be on a Ubuntu 20.04 LTS based Linux desktop. For the demonstrations, we will create an environment with 3 virtual machines running on the hypervisor VirtualBox.

The following diagram illustrates the virtual machines environment setup:

VM Environment
Figure.3

The following are some of the highlights of the 3 virtual machines:

Open a Terminal window for each of the 3 virtual machines vm-1 thru vm-3 to install few pre-requisites by executing the following command:

$ sudo apt install python3-flask net-tools nmap openssh-server traceroute -y

The following would be a typical output (some portion trimmed ... [SNIP] ...):

Output.1

Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following additional packages will be installed:
  javascript-common libjs-jquery liblinear4 liblua5.3-0 lua-lpeg ncurses-term nmap-common openssh-sftp-server
  python3-itsdangerous python3-jinja2 python3-markupsafe python3-openssl python3-pyinotify python3-werkzeug
  ssh-import-id traceroute
Suggested packages:
  apache2 | lighttpd | httpd molly-guard monkeysphere ssh-askpass liblinear-tools liblinear-dev ncat ndiff zenmap
  python-flask-doc python-jinja2-doc python-openssl-doc python3-openssl-dbg python-pyinotify-doc ipython3
  python-werkzeug-doc python3-lxml python3-termcolor python3-watchdog
The following NEW packages will be installed:
  javascript-common libjs-jquery liblinear4 liblua5.3-0 lua-lpeg ncurses-term net-tools nmap nmap-common
  openssh-server openssh-sftp-server python3-flask python3-itsdangerous python3-jinja2 python3-markupsafe
  python3-openssl python3-pyinotify python3-werkzeug ssh-import-id
0 upgraded, 20 newly installed, 0 to remove and 0 not upgraded.
Get:1 http://us.archive.ubuntu.com/ubuntu focal/main amd64 javascript-common all 11 [6,066 B]
Get:2 http://us.archive.ubuntu.com/ubuntu focal/main amd64 libjs-jquery all 3.3.1~dfsg-3 [329 kB]
Get:3 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 liblinear4 amd64 2.3.0+dfsg-3build1 [41.7 kB]
Get:4 http://us.archive.ubuntu.com/ubuntu focal/main amd64 liblua5.3-0 amd64 5.3.3-1.1ubuntu2 [116 kB]
Get:5 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 lua-lpeg amd64 1.0.2-1 [31.4 kB]
Get:6 http://us.archive.ubuntu.com/ubuntu focal/main amd64 ncurses-term all 6.2-0ubuntu2 [249 kB]
Get:7 http://us.archive.ubuntu.com/ubuntu focal/main amd64 net-tools amd64 1.60+git20180626.aebd88e-1ubuntu1 [196 kB]
Get:8 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 nmap-common all 7.80+dfsg1-2build1 [3,676 kB]
Get:9 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 nmap amd64 7.80+dfsg1-2build1 [1,662 kB]
Get:10 http://us.archive.ubuntu.com/ubuntu focal-updates/main amd64 openssh-sftp-server amd64 1:8.2p1-4ubuntu0.1 [51.5 kB]
Get:11 http://us.archive.ubuntu.com/ubuntu focal-updates/main amd64 openssh-server amd64 1:8.2p1-4ubuntu0.1 [377 kB]
Get:12 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-itsdangerous all 1.1.0-1 [14.6 kB]
Get:13 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-markupsafe amd64 1.1.0-1build2 [13.9 kB]
Get:14 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-jinja2 all 2.10.1-2 [95.5 kB]
Get:15 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-werkzeug all 0.16.1+dfsg1-2 [183 kB]
Get:16 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-flask all 1.1.1-2 [80.3 kB]
Get:17 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-openssl all 19.0.0-1build1 [43.3 kB]
Get:18 http://us.archive.ubuntu.com/ubuntu focal/main amd64 python3-pyinotify all 0.9.6-1.2ubuntu1 [24.8 kB]
Get:19 http://us.archive.ubuntu.com/ubuntu focal/main amd64 ssh-import-id all 5.10-0ubuntu1 [10.0 kB]
Get:20 http://us.archive.ubuntu.com/ubuntu focal/universe amd64 traceroute amd64 1:2.1.0-2 [45.4 kB]
...
... [SNIP] ...
...
Setting up python3-openssl (19.0.0-1build1) ...
Setting up ssh-import-id (5.10-0ubuntu1) ...
Attempting to convert /etc/ssh/ssh_import_id
Setting up python3-pyinotify (0.9.6-1.2ubuntu1) ...
Setting up python3-itsdangerous (1.1.0-1) ...
Setting up python3-markupsafe (1.1.0-1build2) ...
Setting up python3-jinja2 (2.10.1-2) ...
Setting up libjs-jquery (3.3.1~dfsg-3) ...
Setting up ncurses-term (6.2-0ubuntu2) ...
Setting up python3-werkzeug (0.16.1+dfsg1-2) ...
Setting up python3-flask (1.1.1-2) ...
Setting up lua-lpeg:amd64 (1.0.2-1) ...
Setting up liblinear4:amd64 (2.3.0+dfsg-3build1) ...
Setting up nmap-common (7.80+dfsg1-2build1) ...
Setting up liblua5.3-0:amd64 (5.3.3-1.1ubuntu2) ...
Setting up nmap (7.80+dfsg1-2build1) ...
Processing triggers for systemd (245.4-4ubuntu3.3) ...
Processing triggers for ufw (0.36-6) ...
Processing triggers for man-db (2.9.1-1) ...
Processing triggers for libc-bin (2.31-0ubuntu9.1) ...

By default, Linux does *NOT* forward any packets in the network. In order for the 3 virtual machines to reach each other, we need to enable network packet forwarding as well as add network routes.

In the Terminal for vm-1, execute the following command:

$ ip addr

The following would be a typical output:

Output.2

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
        valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 11:11:11:11:11:11 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
        valid_lft 548sec preferred_lft 548sec
    inet6 fe80::5a1:8e6f:8f37:6d3a/64 scope link noprefixroute 
        valid_lft forever preferred_lft forever

From the Output.2 above, we see the IP address of vm-1 is 10.0.2.15.

Similarly, in the Terminal for vm-2, execute the following command:

$ ip addr

The following would be a typical output:

Output.3

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
        valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 22:22:22:22:22:22 brd ff:ff:ff:ff:ff:ff
    inet 172.20.1.6/24 brd 172.20.1.255 scope global dynamic noprefixroute enp0s3
        valid_lft 505sec preferred_lft 505sec
    inet6 fe80::c5bd:dd71:57f6:2d03/64 scope link noprefixroute 
        valid_lft forever preferred_lft forever

From the Output.3 above, we see the IP address of vm-2 is 172.20.1.6.

Finally, in the Terminal for vm-3, execute the following command:

$ ip addr

The following would be a typical output:

Output.4

1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
        valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host 
        valid_lft forever preferred_lft forever
2: enp0s3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 33:33:33:33:33:33 brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.4/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s3
        valid_lft 459sec preferred_lft 459sec
    inet6 fe80::f46c:140d:630d:82d4/64 scope link noprefixroute 
        valid_lft forever preferred_lft forever
3: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 44:44:44:44:44:44 brd ff:ff:ff:ff:ff:ff
    inet 172.20.1.5/24 brd 172.20.1.255 scope global dynamic noprefixroute enp0s8
        valid_lft 459sec preferred_lft 459sec
    inet6 fe80::f0c9:1f25:2962:c783/64 scope link noprefixroute 
        valid_lft forever preferred_lft forever

From the Output.4 above, we see vm-3 has two IP addresses - one is 10.0.2.4 and the other is 172.20.1.5.

Setup vm-1

To trace the network route of a packet from vm-1 to vm-2, execute the following command:

$ traceroute -m 3 172.20.1.6

The following would be a typical output:

Output.5

traceroute to 172.20.1.6 (172.20.1.6), 5 hops max, 60 byte packets
  1  _gateway (10.0.2.1)  0.255 ms  0.182 ms  0.162 ms
  2  * * *
  3  * * *

From the Output.5 above, we see vm-2 is NOT reachable.

To check if IP forwarding is enabled, execute the following command:

$ cat /proc/sys/net/ipv4/ip_forward

The following would be a typical output:

Output.6

0

From the Output.6 above, we see IP forwarding is disabled (value of 0) on vm-1.

To enable IP forwarding on vm-1, execute the following command:

$ sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

To check the IP routes on vm-1, execute the following command:

$ ip route show

The following would be a typical output:

Output.7

default via 10.0.2.1 dev enp0s3 proto dhcp metric 100 
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100 
169.254.0.0/16 dev enp0s3 scope link metric 1000

Given only vm-3 can communicate with vm-2, we need to add a route to vm-2 via vm-3 as the gateway.

To add an IP route on vm-1, execute the following command:

$ sudo ip route add 172.20.1.0/24 via 10.0.2.4 dev enp0s3

Again, to check the IP routes on vm-1, execute the following command:

$ ip route show

The following would be a typical output:

Output.8

default via 10.0.2.1 dev enp0s3 proto dhcp metric 100 
10.0.2.0/24 dev enp0s3 proto kernel scope link src 10.0.2.15 metric 100 
169.254.0.0/16 dev enp0s3 scope link metric 1000 
172.20.1.0/24 via 10.0.2.4 dev enp0s3

Setup vm-2

We will only indicate the steps to enable IP forwarding and adding a route on vm-2.

To enable IP forwarding on vm-2, execute the following command:

$ sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

Given only vm-3 can communicate with vm-1, we need to add a route to vm-1 via vm-3 as the gateway.

To add an IP route on vm-2, execute the following command:

$ sudo ip route add 10.0.2.0/24 via 172.20.1.5 dev enp0s3

Again, to check the IP routes on vm-2, execute the following command:

$ ip route show

The following would be a typical output:

Output.9

default via 172.20.1.1 dev enp0s3 proto dhcp metric 100 
10.0.2.0/24 via 172.20.1.5 dev enp0s3 
169.254.0.0/16 dev enp0s3 scope link metric 1000 
172.20.1.0/24 dev enp0s3 proto kernel scope link src 172.20.1.6 metric 100

Setup vm-3

We will only indicate the step to enable IP forwarding on vm-3.

To enable IP forwarding on vm-3, execute the following command:

$ sudo bash -c 'echo 1 > /proc/sys/net/ipv4/ip_forward'

At this stage, the 3 virtual machines should be able to reach each other via vm-3 acting as a gateway.

Hands-on iptables

In the Terminal for vm-1, execute the following command:

$ traceroute -m 3 172.20.1.6

The following would be a typical output:

Output.10

traceroute to 172.20.1.6 (172.20.1.6), 3 hops max, 60 byte packets
  1  10.0.2.4 (10.0.2.4)  0.464 ms  0.402 ms  0.383 ms
  2  172.20.1.6 (172.20.1.6)  0.654 ms  0.631 ms  0.755 ms

Similarly, in the Terminal for vm-2, execute the following command:

$ traceroute -m 3 10.0.2.15

The following would be a typical output:

Output.11

traceroute to 10.0.2.15 (10.0.2.15), 3 hops max, 60 byte packets
  1  172.20.1.5 (172.20.1.5)  0.446 ms  0.385 ms  0.365 ms
  2  10.0.2.15 (10.0.2.15)  0.709 ms  0.636 ms  0.615 ms

PERFECT !!! Our setup works as expected.

Given vm-3 is the gateway, we will make it the iptables firewall and enforce the security policies there.

!! ATTENTION !!

iptables needs elevated access to work - we will need to use sudo

The following illustration shows the general format of the iptables command:

iptables Syntax
Figure.4

To list all the firewall rules in the filter table, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -L

The following would be a typical output:

Output.12

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

As indicated earlier, the filter table is the default. Executing the following command will produce the same results as in Output.12 above:

$ sudo iptables -L

Copy the following Python based web application to vm-2 in a file named Web.py:

Web.py
import sys
from datetime import datetime
from flask import Flask

app = Flask(__name__)
HOST = ''
PORT = 0

HTML = """
<html>
<head>
  <title>From %s</title>
</head>
<body>
  <h3>Hello from %s:%d</h3>
  <h3>Timestamp: %s</h3>
</body>
</html>
"""

@app.route('/')
def index():
    return HTML % (HOST, HOST, PORT, datetime.now())

if __name__ == '__main__':
    if len(sys.argv) == 3:
        HOST = sys.argv[1]
        PORT = int(sys.argv[2])
        app.run(host=HOST, port=PORT)

To start the Python web application on vm-2 on the IP address 172.20.1.6 and port 8080, execute the following command in the Terminal for vm-2:

$ python3 Web.py 172.20.1.6 8080

The following would be a typical output:

Output.13

* Serving Flask app "Web" (lazy loading)
* Environment: production
  WARNING: This is a development server. Do not use it in a production deployment.
  Use a production WSGI server instead.
* Debug mode: off
* Running on http://172.20.1.6:8080/ (Press CTRL+C to quit)

To check if vm-1 can access the web application running on vm-2, execute the following command:

$ nmap -p 8080 172.20.1.6

The following would be the typical output:

Output.14

Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-22 19:55 EST
Nmap scan report for 172.20.1.6
Host is up (0.00072s latency).

PORT     STATE SERVICE
8080/tcp open  http-proxy

Nmap done: 1 IP address (1 host up) scanned in 0.12 seconds

To block access to the traffic from vm-1 to the web application running on vm-2, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -A FORWARD -p tcp --dport 8080 -j DROP

Given that the web application is a TCP process (-p tcp) running on vm-2 and vm-3 forwards the request to vm-2 on port 8080 (--dport 8080), we need to append (-A) the rule to the FORWARD chain of the filter table (-t filter -A FORWARD).

To list all the firewall rules in the filter table, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -L

The following would be a typical output:

Output.15

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  anywhere             anywhere             tcp dpt:http-alt

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

By default, the list option -L will display host names, network names, or services.

To list all the firewall rules in the filter table using numeric IP address(es) and port number(s), execute the following command in the Terminal for vm-3 by adding the -n option:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.16

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Once again, to check if vm-1 can access the web application running on vm-2 , execute the following command:

$ nmap -p 8080 172.20.1.6

The following would be the typical output:

Output.17

Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-22 20:22 EST
Nmap scan report for 172.20.1.6
Host is up (0.00066s latency).

PORT     STATE    SERVICE
8080/tcp filtered http-proxy

Nmap done: 1 IP address (1 host up) scanned in 0.31 seconds

BINGO !!! The request from vm-1 to vm-2 on port 8080 is BLOCKED (filtered).

Executing the above command from vm-3 to vm-2 on port 8080 will be ALLOWED and the result will be similar to the one in Output.14 above.

To block access to all ssh (port 22) traffic to vm-3, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -A INPUT -p tcp --dport 22 -j DROP

Given that ssh is a TCP process (-p tcp) running on port 22 (--dport 22), we need to append (-A) the rule to the INPUT chain of the filter table (-t filter -A INPUT).

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.18

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Execute the following command in the Terminal of vm-1:

$ nmap -p 22 10.0.2.4

The following would be the typical output:

Output.19

Starting Nmap 7.80 ( https://nmap.org ) at 2020-11-22 21:02 EST
Nmap scan report for 10.0.2.4
Host is up (0.00041s latency).

PORT   STATE    SERVICE
22/tcp filtered ssh

Nmap done: 1 IP address (1 host up) scanned in 0.31 seconds

Similarly, one can check from vm-2 as well and will get similar results as in Output.19 above.

To block access to all ping (ICMP) traffic to vm-3, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -A INPUT -p icmp -j DROP

Given that ping is a ICMP process (-p icmp), we need to append (-A) the rule to the INPUT chain of the filter table (-t filter -A INPUT).

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.20

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
DROP       icmp --  0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Execute the following command in the Terminal of vm-1:

$ ping -c 3 10.0.2.4

The following would be the typical output:

Output.21

PING 10.0.2.4 (10.0.2.4) 56(84) bytes of data.

--- 10.0.2.4 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2029ms

Similarly, one can check from vm-2 as well and will get similar results as in Output.21 above.

One should be able to ping from vm-3 to the other 2 virtual machines. To verify that, execute the following command in the Terminal of vm-3:

$ ping -c 3 10.0.2.15

The following would be the typical output:

Output.22

PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.

--- 10.0.2.15 ping statistics ---
3 packets transmitted, 0 received, 100% packet loss, time 2028ms

WHAT ??? Why is ping from vm-3 blocked ???.

The reason for this is because we have block all types of ICMP requests to vm-3. When we issue the ping from vm-3, it sends out an icmp request packet to vm-1. In response, vm-1 sends an icmp reply packet which is blocked by vm-3.

To fix this, we need to remove (-D) the existing ICMP rule from vm-3 and be more specific. To remove the existing rule, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -D INPUT -p icmp -j DROP

To only block ICMP request traffic to vm-3, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -A INPUT -p icmp --icmp-type echo-request -j DROP

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.23

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:22
DROP       icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         
DROP       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpt:8080

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Now vm-3 will be anle to ping the other 2 virtual machines, but not the other way around.

To remove all the firewall rules from vm-3, execute the following command in the Terminal for vm-3:

$ sudo iptables -t filter -F

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The result should be similar to the one in Output.12 above.

From the Output.12 above, we see the default policy is to accept all the network traffic (policy ACCEPT).

To reverse the policy to reject all the network traffic, execute the following commands in the Terminal for vm-3:

$ sudo iptables -t filter -P INPUT DROP

$ sudo iptables -t filter -P FORWARD DROP

$ sudo iptables -t filter -P OUTPUT DROP

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.24

Chain INPUT (policy DROP)
target     prot opt source               destination         

Chain FORWARD (policy DROP)
target     prot opt source               destination         

Chain OUTPUT (policy DROP)
target     prot opt source               destination

From the Output.24 above, we see the default policy is now to reject all the network traffic (policy DROP).

To verify the default policy, execute the following command in the Terminal of vm-1:

$ ping -c 3 10.0.2.4

The result should be similar to the one in Output.21 above. Similarly, one can check from vm-2 or vm-3 and one will get similar results as in Output.21 above.

To ONLY allow vm-3 to be able to ping the other 2 virtual machines, execute the following commands in the Terminal for vm-3:

$ sudo iptables -t filter -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT

$ sudo iptables -t filter -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT

We want to allow ICMP requests going OUT from vm-3 and also allow ICMP replies coming IN to vm-3.

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.25

Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 0

Chain FORWARD (policy DROP)
target     prot opt source               destination         

Chain OUTPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8

To verify the ICMP rules, execute the following command in the Terminal of vm-3:

$ ping -c 3 10.0.2.15

The following would be the typical output:

Output.26

PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.
64 bytes from 10.0.2.15: icmp_seq=1 ttl=64 time=0.407 ms
64 bytes from 10.0.2.15: icmp_seq=2 ttl=64 time=0.385 ms
64 bytes from 10.0.2.15: icmp_seq=3 ttl=64 time=0.361 ms

--- 10.0.2.15 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2052ms
rtt min/avg/max/mdev = 0.361/0.384/0.407/0.018 ms

WALLA !!! The firewall rules behave as expected.

To ONLY allow vm-1 to be able to access the web application that is hosted on vm-2 (on port 8080), execute the following commands in the Terminal for vm-3:

$ sudo iptables -t filter -A FORWARD -p tcp -s 10.0.2.15 -d 172.20.1.6 --dport 8080 -j ACCEPT

$ sudo iptables -t filter -A FORWARD -p tcp -s 172.20.1.6 -d 10.0.2.15 --sport 8080 -j ACCEPT

We want to allow HTTP requests forwarded from vm-1 to vm-2 and also allow HTTP responses forwarded from vm-2 to vm-1.

Execute the following command in the Terminal of vm-3 to list all the firewall rules in the filter table:

$ sudo iptables -t filter -n -L

The following would be a typical output:

Output.27

Chain INPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 0

Chain FORWARD (policy DROP)
target     prot opt source               destination         
ACCEPT     tcp  --  10.0.2.15            172.20.1.6           tcp dpt:8080
ACCEPT     tcp  --  172.20.1.6           10.0.2.15            tcp spt:8080

Chain OUTPUT (policy DROP)
target     prot opt source               destination         
ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0            icmptype 8

To verify the HTTP rules, launch a browser on vm-1 and open the url to http://172.20.1.6:8080.

The following diagram illustrates the screenshot of the browser:

Browser
Figure.5

Trying to access the url http://172.20.1.6:8080 from a browser on vm-3 will NOT work.

TA DA !!! The firewall rules works as expected.

References

Official netfilter Documentation

A Deep Dive into Iptables and Netfilter Architecture



© PolarSPARC