Forward y NAT en GNU/Linux con IPTABLES

En esta entrada vamos a hacer pruebas con las posibilidades que nos ofrece iptables en GNU/Linux para hacer forwarding y NAT, o lo que es lo mismo, comportarse como si de un router se tratara.

El escenario propuesto es el siguiente, si no disponéis de equipos suficientes podéis usar virtualbox o cualquier otro sistema de virtualización. En mi caso he trabajado con virtualbox:

Básicamente tenemos una red interna (10.0.0.0/24) a la cual están conectadas las dos máquinas virtuales. Una de ellas tiene otra interfaz de red conectada a Internet a través de la red 192.168.1.0/24. Asumimos que esta es la IP pública (en un entorno real, aquí por supuesto no) y es la interfaz que nos va a proveer internet a través de NAT a la red privada 10.0.0.0/24.

NAT

Tenemos configurado entonces PC1 con una única interfaz de red privada y con PC2 configurado como GATEWAY. PC2 tendrá activado el forwarding entre sus dos interfaces y a su vez tendrá especificado hacer NAT en su interfaz externa (192.168.1.111).

Las configuraciones se centran entonces en PC2 ya que en PC1 únicamente especificamos la puerta de enlace/gateway de PC2. Para permitir a PC2 comportarse como un router es permitir el enrutamiento/forward IP, el cual está desactivado por defecto. Modificamos el parámetro de kernel correspondiente (ver la entrada sysctl y /proc/sys – modificar parámetros de kernel):

[root@pc2 ~]# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf 
[root@pc2 ~]# sysctl -p
...
...
net.ipv4.ip_forward = 1

No nos podemos olvidar tampoco de, además de a nivel de kernel, permitir en IPTABLES el forward (en el caso de que la política de forwarding sea denegar/drop por defecto) para la interfaz pública:

[root@pc2 ~]# iptables -A FORWARD -i eth1 -j ACCEPT
[root@pc2 ~]# iptables -A FORWARD -o eth1 -j ACCEPT

Una vez hecho esto, si esnifamos tráfico de la red veremos que efectivamente el forwarding ya se está realizando, pero al no tener NAT configurado cualquier petición de PC1 al exterior se realiza a través de su IP privada, lo que impide por ejemplo recibir un echo-reply en un paquete ICMP:

En este caso el tcpdump lo he lanzado en la máquina física mientras hacía un ping a 8.8.8.8 desde PC1 (10.0.0.101). Esto nos confirma por un lado que PC1 llega a PC2 y que PC2 reenvía el tráfico desde su interfaz privada a la pública:

# tcpdump -i eth0 -vv icmp
11:15:58.869830 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.101 > google-public-dns-a.google.com: ICMP echo request, id 35590, seq 4, length 64
11:15:59.870369 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.101 > google-public-dns-a.google.com: ICMP echo request, id 35590, seq 5, length 64
11:16:00.870459 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    10.0.0.101 > google-public-dns-a.google.com: ICMP echo request, id 35590, seq 6, length 64

Para solucionar este problema sólo nos queda configurar NAT en PC2 sobre su interfaz pública (recordad, en este laboratorio es 192.168.1.111, que está en eth1):

[root@pc2 ~]# iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

A partir de ese momento, si volvemos a lanzar un ping a una IP de internet desde PC1, veremos que en el tcpdump la IP de salida ya no es la privada 10.0.0.101 de PC1 sino la ip de la interfaz pública de PC1 192.168.1.111, lo que confirma que el NAT está funcionando correctamente:

11:20:35.623753 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.1.111 > google-public-dns-a.google.com: ICMP echo request, id 36102, seq 8, length 64
11:20:35.685047 IP (tos 0x0, ttl 48, id 33768, offset 0, flags [none], proto ICMP (1), length 84)
    google-public-dns-a.google.com > 192.168.1.111: ICMP echo reply, id 36102, seq 8, length 64
11:20:36.625537 IP (tos 0x0, ttl 63, id 0, offset 0, flags [DF], proto ICMP (1), length 84)
    192.168.1.111 > google-public-dns-a.google.com: ICMP echo request, id 36102, seq 9, length 64
11:20:36.686543 IP (tos 0x0, ttl 48, id 33769, offset 0, flags [none], proto ICMP (1), length 84)
    google-public-dns-a.google.com > 192.168.1.111: ICMP echo reply, id 36102, seq 9, length 64

Podemos revisar la configuración de NAT en iptables:

# iptables -t nat -L
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
MASQUERADE  all  --  anywhere             anywhere            

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Esto no es mas que un primer acercamiento a las posibilidades de GNU/Linux en terminos de routing y NAT, las posibilidades son mucho mayores así que no dudéis en revisar cualquier documentación alternativa para profundizar en el tema.