Lo hice y lo entendí

El blog de Vicente Navarro
07 oct

Accediendo a una red mediante NAT en Linux

En las últimas entradas he tratado bastante de cómo acceder a nodos de una red remota por “métodos no estándar”:

Cuando digo “métodos no estándar”, me refiero a que normalmente la conectividad entre redes en una intranet o en Internet se consigue con las rutas definidas en los routers de la red. Los routers saben (o deberían saber) cómo llegar de un nodo en la red A a otro nodo en la red B.

Ahora supongamos que estamos en una gran intranet corporativa con muchas subredes. Supongamos también que tenemos un sistema con Linux con dos interfaces Ethernet. Uno de ellos conectado a la red “oficial” y el otro conectado a un switch al que conectamos varios servidores (Linux, Windows, etc.). Necesitamos que dichos servidores puedan llegar a nodos de la intranet “oficial” y necesitamos acceder a ciertos servicios de los servidores desde la intranet. De momento, esos servidores estarán en una “red oculta” o “inalcanzable” para el resto de nodos de la intranet.

El “método estándar” sería que el administrador de la intranet definiera en los routers de la intranet una ruta para que todos los nodos de la red supieran llegar. Pero si eso no es posible o conveniente (lo contrario implicaría mucha burocracia, va a ser una situación temporal, así proporcionamos más seguridad a la red oculta), es cuando tenemos que usar “metodos no estándar”.

Las soluciones usando OpenSSH descritas en entradas anteriores nos resolvían esta situación perfectamente. Pero, ¡ay! Resulta que OpenSSH introduce una sobrecarga importante: Los nodos que mantienen la conexión SSH estarán permanentemente cifrando toda la comunicación, usarán más CPU y la conexión será menos ligera y fluida.

La alternativa, en la que usamos únicamente técnicas estándar del protocolo IP, sería usar NAT. La idea sería que el sistema Linux se comportara como un router ADSL o de cable como los que solemos tener en casa, que hacen NAT y que nos permiten abrir sólo unos pocos puertos para acceder a ciertos servicios de la red interna.

Así, consideremos el siguiente diagrama en el que vemos que el Linux tiene la IP 10.25.25.2 (eth0) en la intranet “oficial” y la IP 172.16.20.2 (eth1) en la red interna “oculta”:

El primer paso sería permitir el “IP Forwarding”; esto es, que el kernel permita que paquetes de red pasen de unos interfaces a otros. Se puede hacer así:

echo 1 > /proc/sys/net/ipv4/ip_forward

o así:

sysctl -w net.ipv4.ip_forward=1

Y para que sea permanente tras un reinicio, habría que añadir esta línea al fichero /etc/sysctl.conf:

net.ipv4.ip_forward = 1

El segundo paso sería habilitar el NAT de forma que los nodos de la red oculta puedan acceder a nodos de la intranet usando la IP del sistema Linux que ellos conocen (IP Masquerade). Con el siguiente comando, le decimos al kernel de Linux que haga NAT a las conexiones que salgan por el interfaz eth0 con iptables:

/sbin/iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Tras ejecutarlo, ya todos los nodos de la red oculta podrán acceder a nodos de fuera.

Para permitir la comunicación en el sentido inverso, desde la intranet a la red oculta, tendríamos que hacer lo equivalente a cuando “abrimos puertos” en el router ADSL o de cable de casa. Por ejemplo, con el siguiente comandos especificamos que cuando llegue un conexión TCP dirigida la IP 10.25.25.2 y al puerto 224, que la redirija, convenientemente “NATeada” a la IP 172.16.20.4, puerto 22 (SSH):

/sbin/iptables -A PREROUTING -t nat -p tcp -d 10.25.25.2 --dport 224 -m state --state NEW,ESTABLISHED,RELATED -j DNAT --to 172.16.20.4:22

Hacemos lo mismo con conexiones que lleguen a la IP 10.25.25.2 y puerto 225, que redirigimos “NATeadas” a la IP 172.16.20.5, puerto 22 (SSH):

/sbin/iptables -A PREROUTING -t nat -p tcp -d 10.25.25.2 --dport 225 -m state --state NEW,ESTABLISHED,RELATED -j DNAT --to 172.16.20.5:22

Y lo mismo con conexiones que lleguen a la IP 10.25.25.2 y puerto 33896, que reenviamos a la IP 172.16.20.6, puerto 3389 (Remote Desktop):

/sbin/iptables -A PREROUTING -t nat -p tcp -d 10.25.25.2 --dport 33896 -m state --state NEW,ESTABLISHED,RELATED -j DNAT --to 172.16.20.6:3389

Desde un sistema de la intranet, para llegar al servidor SSH del Linux 10.25.25.4, tendríamos que ejecutar:

ssh -p 224 usuario@10.25.25.2

Para llegar al servidor SSH del Linux 10.25.25.5, sería:

ssh -p 225 usuario@10.25.25.2

y para llegar al Remote Desktop del Windows 10.25.25.6, tendríamos que acceder a 10.25.25.2:33896:

Y vemos que podríamos llegar a cualquier puerto de cualquiera de las máquinas sólo con introducir las reglas adecuadas.

Para añadir algo de seguridad, seguramente también podríamos querer especificar un rango de puertos hacia los que permitimos salir a las máquinas de la red oculta. Comenzaríamos por eliminar las reglas que pueda tener el sistema por defecto:

/sbin/iptables -F FORWARD

y a continuación, pondríamos reglas de salida para, por ejemplo, SSH/TCP 22, DNS/UDP 53, HTTP/TCP 80, HTTPS/TCP 443 y HTTP-PROXY/TCP 8080:

/sbin/iptables -A FORWARD -i eth1 -o eth0 -p tcp -m multiport --dports 22,80,443,8080 -m state --state NEW,ESTABLISHED -j ACCEPT
/sbin/iptables -A FORWARD -i eth1 -o eth0 -p udp --dport 53 -m state --state NEW,ESTABLISHED -j ACCEPT

También tenemos que permitir el tráfico asociado a las conexiones anteriores en el sentido contrario:

/sbin/iptables -A FORWARD -i eth0 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT

Y también añadiríamos reglas concretas para especificar a qué servicios de la red interna permitimos acceder. Por ejemplo, con estas reglas permitimos acceder por SSH a los nodos Linux 172.16.20.4 y 172.16.20.5 y por Remote Desktop al Windows 172.16.20.6:

/sbin/iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 22 --destination 172.16.20.4 -m state --state NEW,ESTABLISHED -j ACCEPT
/sbin/iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 22 --destination 172.16.20.5 -m state --state NEW,ESTABLISHED -j ACCEPT
/sbin/iptables -A FORWARD -i eth0 -o eth1 -p tcp --dport 3389 --destination 172.16.20.6 -m state --state NEW,ESTABLISHED -j ACCEPT

Y, de nuevo, una regla para permitir el tráfico asociado en el sentido contrario:

/sbin/iptables -A FORWARD -i eth1 -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT

Finalmente, tendríamos que añadir una última regla para rechazar reenviar cualquier otro tráfico que pase entre eth0 y eth1:

/sbin/iptables -A FORWARD -j REJECT

Podemos ver todas las reglas resultantes de NAT y de forwarding con:

# /sbin/iptables -t nat -L -v -n
Chain PREROUTING (policy ACCEPT 4607 packets, 397K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            10.25.25.2          tcp dpt:224 state NEW,RELATED,ESTABLISHED to:172.16.20.4:22
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            10.25.25.2          tcp dpt:225 state NEW,RELATED,ESTABLISHED to:172.16.20.5:22
    0     0 DNAT       tcp  --  *      *       0.0.0.0/0            10.25.25.2          tcp dpt:33896 state NEW,RELATED,ESTABLISHED to:172.16.20.6:3389

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

Chain OUTPUT (policy ACCEPT 149 packets, 13022 bytes)
 pkts bytes target     prot opt in     out     source               destination
# iptables -L FORWARD -v -n
Chain FORWARD (policy ACCEPT 72 packets, 8509 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  eth1   eth0    0.0.0.0/0            0.0.0.0/0           multiport dports 22,80,443,8080 state NEW,ESTABLISHED
   13   809 ACCEPT     udp  --  eth1   eth0    0.0.0.0/0            0.0.0.0/0           udp dpt:53 state NEW,ESTABLISHED
   24  2179 ACCEPT     all  --  eth0   eth1    0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
    0     0 ACCEPT     tcp  --  eth0   eth1    0.0.0.0/0            172.16.20.4         tcp dpt:22 state NEW,ESTABLISHED
    0     0 ACCEPT     tcp  --  eth0   eth1    0.0.0.0/0            172.16.20.5         tcp dpt:22 state NEW,ESTABLISHED
    0     0 ACCEPT     tcp  --  eth0   eth1    0.0.0.0/0            172.16.20.6         tcp dpt:3389 state NEW,ESTABLISHED
   23 17097 ACCEPT     all  --  eth1   eth0    0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
    2   176 REJECT     all  --  *      *       0.0.0.0/0            0.0.0.0/0           reject-with icmp-port-unreachable

Si necesitáramos empezar desde el principio, limpiaríamos previamente todas las reglas actuales con:

/sbin/iptables -F FORWARD
/sbin/iptables -t nat -F

Sólo faltaría configurar estos comandos para que se ejecutaran al arrancar el sistema, pero cómo hacerlo ya dependería de la distribución, ya que muchas de ellas llevan sus scripts para cargar ciertas reglas por defecto. En general, el comando iptables-save puede ser de utilidad. O también podríamos simplemente poner los comandos en un script de arranque que se ejecute de los últimos.

:wq

Entradas relacionadas

2 Comentarios a “Accediendo a una red mediante NAT en Linux”

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro