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.
- Name your VM using the following convention:
- 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
Also run the following commands in you current shell.
$ source ~/.bashrc
$ prepare_lab
Topology
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 thePOSTROUTING
chain entries in thenat
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
[...]
When running the tcpdump
command the sequence to follow is:
- Run the
tcpdump
command with the corresponding options in a terminal, starting packet capture. Thetcpdump
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.
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 thegreen
host; - port 30022 on the
host
station to port forward to port 22 on theblue
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 10023green
can be accessed using port 20023blue
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