Skip to main content

Network Address Translation - iptables

Lab Setup

  • We will be using a virtual machine in the faculty's cloud.
  • When creating a virtual machine in the Launch Instance window:
    • Name your VM using the following convention: scgc_lab<no>_<username>, where <no> is the lab number and <username> is your institutional account.
    • Select Boot from image in Instance Boot Source section
    • Select SCGC Template in Image Name section
    • Select a flavor that is at least m1.large.
  • The username for connecting to the VM is student.
  • For the following exercises, the resources can be found in the laboratory archive:
$ cd work/
$ wget https://repository.grid.pub.ro/cs/scgc/laboratoare/lab-iptables-nat.zip
$ unzip lab-iptables-nat.zip
$ bash runvm.sh
warning

Also run the following commands in you current shell.

$ source ~/.bashrc
$ prepare_lab

Topology

TopologyTopology

info

For all the exercises we will use the above topology.

MASQUERADE NAT

The depletion of IPv4 addresses has led to the use of private class IP addresses (e.g. 192.168.0.0/24). In addition to communication between hosts in a network, we also want Internet access. This is why the concept of Network Address Translation (NAT) has been introduced, where several hosts have access to the Internet using the same routable IP address: the gateway address. Enabling address translation (NAT) on the gateway causes the pair <source IP address, source port> (belonging to the station) to be replaced by the pair <gateway IP address, available port>.

Configuring NAT on Linux is done via the iptables command, just like configuring the firewall. Where we used the filter table (the default iptables table) for firewall configuration, we use the nat table for address translation configuration.

So, to enable NAT on a Linux server, run the command

root@host:~# iptables -t nat -A POSTROUTING -j MASQUERADE

In the above command:

  • -t specifies the table to which the rule applies, in our case the nat table.
  • -A means adding a rule to the end of the rule list.
  • POSTROUTING refers to when the address translation process will be performed: after routing. In iptables nomenclature this is also called a chain. Examples of other chains: INPUT, OUTPUT, FORWARD, PREROUTING.
  • -j is the action to be taken, MASQUERADE in this case (simple address translation action). To check and validate the rule, we display the POSTROUTING chain entries in the nat table using the command
root@host:~# iptables -t nat -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0

We want to verify that NAT is configured correctly. To do this we will send a packet from the red host to 8.8.8.8. The packet will pass through the gateway (i.e. host) and will be forwarded. On the red host we run the command

root@red:~# ping -c 2 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
From 192.168.1.2 icmp_seq=1 Destination Host Unreachable
From 192.168.1.2 icmp_seq=2 Destination Host Unreachable

--- 8.8.8.8 ping statistics ---
2 packets transmitted, 0 received, +2 errors, 100% packet loss, time 999ms

We notice that there is no connectivity from the red station to 8.8.8.8:

root@host:~# iptables -t nat -L -n -v
Chain PREROUTING (policy ACCEPT 2 packets, 168 bytes)
pkts bytes target prot opt in out source destination

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0

We observe that packets arrive in the PREROUTING chain (before routing), but do not arrive in the POSTROUTING chain (after routing). We think there may be a problem with routing on the gateway. We check if routing is enabled:

root@host:~# sysctl net.ipv4.ip_forward
net.ipv4.ip_forward = 0

Indeed, routing is not enabled. To enable routing on the host we run the command

root@host:~# sysctl -w net.ipv4.ip_forward=1
net.ipv4.ip_forward = 1

We log back into the red host and use ping to test connectivity to 8.8.8.8:

root@red:~# ping -c 2 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_req=1 ttl=61 time=92.9 ms
64 bytes from 8.8.8.8: icmp_req=2 ttl=61 time=81.2 ms

--- 8.8.8.8 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 81.272/87.094/92.917/5.829 ms

Now there is connectivity, which can also be observed by the presence of packets processed by the POSTROUTING chain:

root@host:~# iptables -t nat -L POSTROUTING -n -v
Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
2 168 MASQUERADE all -- * * 0.0.0.0/0 0.0.0.0/0

Observing NATed packets

We plan to analyze the IP header of packets that are generated by the red, green and blue hosts and destined to a network on the Internet. For this we use the tcpdump capture utility.

On red we start the ping command to 8.8.8.8:

root@red:~# ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_req=1 ttl=127 time=42.0 ms
[...]
note

When running the tcpdump command the sequence to follow is:

  • Run the tcpdump command with the corresponding options in a terminal, starting packet capture. The tcpdump utility now waits for packets to be transmitted on the interfaces it is listening on.
  • In another terminal run a specific network client command that generates traffic.
  • Return to the terminal running the tcpdump command and watch for captured packets.
  • When you no longer need the tcpdump utility, stop capturing packets using the Ctrl+c key combination.

To capture traffic, on the host station we run the command

root@host:~# tcpdump -n -i eth0 ip dst host 8.8.8.8
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
12:59:20.976707 IP host > 8.8.8.8: ICMP echo request, id 625, seq 6, length 64
12:59:21.977708 IP host > 8.8.8.8: ICMP echo request, id 625, seq 7, length 64

We notice that the source IP address is host even though it is the red host that executes the ping command and generates the ICMP echo request packets.

To see the packets as originally generated, we run the tcpdump command on the usernet interface instead of eth0:

root@host:~# tcpdump -n -i usernet ip dst host 8.8.8.8
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on usernet, link-type EN10MB (Ethernet), capture size 65535 bytes
13:01:12.557692 IP red > 8.8.8.8: ICMP echo request, id 626, seq 6, length 64
13:01:13.559726 IP red > 8.8.8.8: ICMP echo request, id 626, seq 7, length 64

To see how traffic is translated we capture traffic on all interfaces (the ones of interest are usernet and eth0)

root@host:~# tcpdump -n -i any ip dst host 8.8.8.8
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
10:23:07.632412 IP red > 8.8.8.8: ICMP echo request, id 707, seq 237, length 64
10:23:07.632430 IP host > 8.8.8.8: ICMP echo request, id 707, seq 237, length 64
10:23:08.633936 IP red > 8.8.8.8: ICMP echo request, id 707, seq 238, length 64
10:23:08.633954 IP host > 8.8.8.8: ICMP echo request, id 707, seq 238, length 64

In the above list we notice both packets that are captured on the usernet interface (generated by the red host) and those captured on the eth0 interface (forwarded by the host).

Also capture the reply packets, which have as their source address 8.8.8.8. Use the ip src host 8.8.8.8 argument string for tcpdump.

Repeat the above tests for the green host.

Observing NATed packets (TCP)

We want to track the contents of TCP packets before and after address translation. In addition to the above exercise, we also want to track ports. For this we will generate TCP (HTTP) packets using wget on the red host and capture the packets using tcpdump on the host station.

On the host station, capture HTTP packets on all interfaces that have cs.pub.ro as destination address.

Use the following command:

root@host:~# tcpdump -n -i any ip dst host cs.pub.ro and tcp dst port 80

On the red host use wget to get the page from cs.pub.ro.

Watch the translation of the source IP address and source port in the packet capture performed with tcpdump on the host.

Notice in the capture that the source port chosen by the red host is the same as the one used after translation by the host station. NAT implementations will generally keep the port after translation. In the rare case that that port is busy on the host station (possibly due to another translation) another port will be assigned.

Do the same, but capture both outgoing and incoming traffic to/from cs.pub.ro port 80.

Port forwarding

In the exercises so far we have used NAT to allow hosts with private IPs on a local network to access the Internet. NAT can also be used to allow a host on the local network to be accessed from the Internet. This process is called port forwarding.

We want to be able to access the red host via SSH from the Internet. This is not possible by default as the red host has a private IP address. The solution is to "open a port" on the gateway (i.e. the host station) and forward this port (port forwarding) to the port corresponding to the SSH service (TCP port 22) on the red station.

We will apply a rule on the host station to forward the traffic coming to the host on port 10022 to port 22 (SSH) of the red station (IP address 10.10.1.2):

root@host:~# iptables -t nat -A PREROUTING -p tcp --dport 10022 -j DNAT --to-destination 10.10.1.2:22

We check the rule by consulting the PREROUTING chain in the NAT table:

root@host:~# iptables -t nat -L PREROUTING -n -v
Chain PREROUTING (policy ACCEPT 1 packets, 474 bytes)
pkts bytes target prot opt in out source destination
0 0 DNAT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:10022 to:192.168.1.2:22

To check that the rule works, from fep.grid.pub.ro we open a new terminal and connect via SSH using port 10022 to the host station:

user.name@fep:~$ ssh -l student 10.9.X.Y -p 10022
student@10.10.1.2's password:
[...]
student@red:~#

We notice that after authentication we are on the red host. Port forwarding worked.

Now, let's use SSH to connect to the host station from the green host:

student@green:~# ssh -l student host -p 10022
[...]
student@red:~#

We notice that from the green station we can also access the red station via port forwarding. We want to limit port forwarding only for connections from the Internet. For this we need to update the port forwarding rule.

Delete the port forwarding rule and add a new rule that only allows connections from the Internet to port forward to the red host.

note

Apply the rule only for packets arriving on the eth0 interface. Use the -i option of iptables to specify the incoming interface.

Then perform the SSH connection again on port 10022 of the host station from fep.grid.pub.ro and the green host. If you have configured it correctly, the SSH connection from the green host will not work but it will still work from fep.grid.pub.ro.

Port forwarding again

We want to access the green and blue hosts from the Internet/outside via SSH using the host station. We'll use:

  • port 20022 on the host station to port forward to port 22 of the green host;
  • port 30022 on the host station to port forward to port 22 on the blue host.

Similar to the previous exercise, perform the necessary configurations for this port forwarding. Verify by connecting from fep.grid.pub.ro (Internet/outdoor equivalent) on ports 20022 and 30022 on the host station.

Observing NATed packets (Port Forwarding)

We will capture SSH traffic initiated from the outside to the red host through port 10022 of the host station. This is traffic before port forwarding. For this, on the host station we use the command

root@host:~# tcpdump -n -i eth0 tcp dst port 10022

On another terminal, also on the host station, we capture the traffic after port forwarding, on the usernet interface to the SSH port (22) of the red host. On the host station we use the command

root@host:~# tcpdump -n -i usernet tcp dst port 22

In order to generate traffic, connect from fep.grid.pub.ro using SSH to the host station on port 10022, which will be redirected to port 22 of the red host:

user.name@fep:~$ ssh -l student 10.9.X.Y -p 10022
student@red's password:
[...]
student@red:~#

In the captures above, we see the IP address and destination port being translated from the <10.9.X.Y, 10022> pair to the <10.10.1.2, 22> pair.

Telnet port forwarding

In previous exercises we enabled port forwarding for the SSH service. We want the red, green and blue hosts to be accessible via telnet from the Internet as well so:

  • red can be accessed using port 10023
  • green can be accessed using port 20023
  • blue can be accessed using port 30023

Make the necessary configurations to enable port forwarding for telnet as described above.

Test from fep.grid.pub.ro using the telnet command:

user.name@fep:~# telnet 10.9.X.Y 10023