Lo hice y lo entendí

El blog de Vicente Navarro
05 nov

VPN con OpenSSH

Tras Creando túneles TCP/IP (port forwarding) con SSH: Los 8 escenarios posibles usando OpenSSH y Reenvío dinámico de puertos / montar un servidor SOCKS con SSH, puede ser buena idea hablar de cómo crear una VPN improvisada entre dos redes cuyos nodos no pueden llegar de una red a la otra exceptuando un sistema de una de las redes que puede crear una conexión OpenSSH con otro sistema de la otra red.

OpenSSH permite esto desde su versión 4.3, y es tan sencillo que en la página man de ssh se describe en apenas 5 párrafos (sección SSH-BASED VIRTUAL PRIVATE NETWORKS). La parte no tan sencilla es la de gestionar las rutas.

Además, como digo, esta configuración es adecuada para una red improvisada y no como solución definitiva porque, como indica la propia página de manual, la sobrecarga por el protocolo SSH es muy alta:

     Since an SSH-based setup entails a fair amount of overhead, it may be
     more suited to temporary setups, such as for wireless VPNs.  More
     permanent VPNs are better provided by tools such as ipsecctl(8) and
     isakmpd(8).

Vamos a trabajar con el escenario mostrado en el siguiente esquema:

Tenemos dos oficinas de una misma empresa conectadas internamente y a Internet con el típico router/switch ADSL que tiene una única IP pública y que hace NAT para compartirla. Ahora queremos interconectarlas, de forma que todos los nodos de una oficina accedan a los de la otra y viceversa. En la primera de ellas se usa la subred 192.168.10.0/24 para la red local, y en la segunda, la subred 172.16.20.0/24. En la configuración del NAT del router ADSL de la segunda oficina abrimos el puerto 22 de SSH y lo redirigimos hacia el nodo servidorssh172 (172.16.20.2), que está ejecutando el sshd de OpenSSH. La IP pública de la segunda oficina debería ser fija o tal vez se podría usar un servicio de DNS dinámico. El nodo clientessh192 (192.168.10.2) de la primera red establecerá una conexión OpenSSH con el nodo servidorssh172 (172.16.20.2) de la segunda red usando la IP pública de la segunda oficina.

Establecemos la conexión SSH especificando la opción “-w 0:1“, con la que estamos solicitando que se cree un túnel entre el interface virtual tun0 del cliente SSH y el interfaz virtual tun1 del servidor SSH (interfaces TUN/TAP):

clientessh192 # ssh -w 0:1 root@ip-publica-red172

Sólo los sistemas operativos que soporten este tipo de interfaces virtuales podrán crear VPNs con OpenSSH. Por ejemplo, usando el OpenSSH de Cywin en Windows nos encontraremos con el siguiente error:

$ ssh -w 0:1 root@ip-publica-red172
Tunnel interfaces are not supported on this platform
Tunnel device open failed.
Could not request tunnel forwarding.

Como en general sólo el usuario root puede crear interfaces TUN, necesitaremos que el usuario que lanza el ssh en el cliente sea root y que se conecte al servidor también como root. Además, en el servidor, el demonio sshd tiene que tener habilitada la opción PermitTunnel en el fichero /etc/ssh/sshd_config.

Si la conexión se ha establecido correctamente, encontraremos un nuevo interfaz en cada extremo (a partir de ahora suponemos que ambos extremos de la conexión SSH son Linux; en otros sistemas operativos que soporten esta característica los comandos serían diferentes):

clientessh192 # ifconfig tun0
tun0      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          POINTOPOINT NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

servidorssh172 # ifconfig tun1
tun1      Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00  
          POINTOPOINT NOARP MULTICAST  MTU:1500  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:500 
          RX bytes:0 (0.0 b)  TX bytes:0 (0.0 b)

A los que les podemos asignar una IP de la misma subred (10.0.0.0/30), puesto que están virtualmente conectadas:

clientessh192 # ifconfig tun0 10.0.0.1 netmask 255.255.255.252

servidorssh172 # ifconfig tun1 10.0.0.2 netmask 255.255.255.252

y ya podemos verificar que tenemos conectividad entre las máquinas:

clientessh192 # ping 10.0.0.2
PING 10.0.0.2 (10.0.0.2) 56(84) bytes of data.
64 bytes from 10.0.0.2: icmp_seq=1 ttl=64 time=86.3 ms
64 bytes from 10.0.0.2: icmp_seq=2 ttl=64 time=98.1 ms
64 bytes from 10.0.0.2: icmp_seq=3 ttl=64 time=61.2 ms

servidorssh172 # ping 10.0.0.1
PING 10.0.0.1 (10.0.0.1) 56(84) bytes of data.
64 bytes from 10.0.0.1: icmp_seq=1 ttl=64 time=96.2 ms
64 bytes from 10.0.0.1: icmp_seq=2 ttl=64 time=88.3 ms
64 bytes from 10.0.0.1: icmp_seq=3 ttl=64 time=81.6 ms

Ahora introducimos rutas en cada uno de estos nodos para que sepan llegar a la otra red:

clientessh192 # route add -net 172.16.20.0 netmask 255.255.255.0 gateway 10.0.0.2

servidorssh172 # route add -net 192.168.10.0 netmask 255.255.255.0 gateway 10.0.0.1

Si el escenario no fuera exactamente el mostrado inicialmente, sino que clientessh192 tuviera conectividad directa con servidorssh172 y no hubiera una IP pública involucrada, con estas rutas podríamos tener un problema, y es que estas rutas podrían cortar inmediatamente el SSH que mantiene el túnel. Éste es un problema real cuando, por ejemplo, tenemos una red aislada dentro de una empresa cuyos nodos pueden salir hacia fuera usando NAT, pero que no es posible entrar a ella desde fuera. Podemos hacer fácilmente un SSH saliente pero no podemos establecer conexiones entrantes, algo que queremos solucionar improvisadamente con una VPN por OpenSSH. Las rutas romperán la conexión SSH.

Usando los sistemas de nuestro ejemplo inicial: Supongamos que clientessh192 puede establecer conexión directa con servidorssh172 habiendo sólo un router que hace NAT entre ambos. Esto funciona porque en clientessh192 hay una ruta específica hacia 172.16.20/24 o porque su ruta por defecto ya va hacia el router que hace NAT. En el momento en que pongamos la ruta propuesta, el SSH se cortará instantáneamente y el túnel se perderá (de hecho, la conexión se queda colgada, porque los paquetes los enviamos por el túnel SSH, pero a su vez los paquetes de SSH también los mandamos por el túnel, causando un tremendo lío). En cambio, la ruta en servidorssh172 no causaría problemas, porque en 172.16.20.0/24 no saben nada de la red 192.168.10.0/24, ya que está NATeada.

En estos casos, una solución que he encontrado es hacer rutas más específicas (con máscaras más pequeñas) que no incluyan a los nodos involucrados en el SSH. Por ejemplo, si el rango de nodos a los que queremos acceder es 172.16.10.35-75, podríamos poner estas rutas en clientessh192 que no afectan a cómo se llega a 172.16.20.2:

clientessh192 # route add -net 172.16.20.32 netmask 255.255.255.224 gateway 10.0.0.2

clientessh192 # route add -net 172.16.20.64 netmask 255.255.255.224 gateway 10.0.0.2

El peor de los casos sería tener que llegar a IPs muy cercanas a las de servidorssh172, como 172.16.20.1, 172.16.20.3 o 172.16.20.4. En tal caso, tendríamos que poner rutas con máscaras muy grandes (/30, /31) y rutas directas a hosts. Las siguientes rutas permitirían llegar a .1, .3, .4 y .5:

clientessh192 # route add -host 172.16.20.1 gateway 10.0.0.2

clientessh192 # route add -host 172.16.20.3 gateway 10.0.0.2

clientessh192 # route add -net 172.16.20.4 netmask 255.255.255.254 gateway 10.0.0.2

Pero si las rutas no crean otras complicaciones, como en el caso expuesto inicialmente, desde este momento, los paquetes IP de clientessh192 y de servidorssh172 podrían llegar a los nodos de la otra red, pero aquellos nodos no sabrían enviar sus paquetes de vuelta, puesto que no tienen una ruta para ello. Por tanto, en todos los nodos de la red 192.168.10.0/24 habría que poner esta ruta:

  • Sistemas Linux:
route add -net 172.16.20.0 netmask 255.255.255.0 gateway 192.168.10.2
  • Sistemas Windows:
C:\route add 172.16.20.0 mask 255.255.255.0 192.168.10.2

En todos los nodos de la red 172.16.20.0/24 habría que poner esta ruta:

  • Sistemas Linux:
route add -net 192.168.10.0 netmask 255.255.255.0 gateway 172.16.20.2
  • Sistemas Windows:
C:\>route add 192.168.10.0 mask 255.255.255.0 172.16.20.2

En Linux podemos verificar las rutas introducidas con “netstat -rn“, en Windows con “route print“.

Pero además, tenemos que configurar que tanto el cliente como el servidor de SSH permitan que los paquetes IP vayan de unos interfaces a otros (IP Forwarding). Si queremos hacerlo sólo temporalmente (que es lo adecuado si estamos montando esta red para una necesidad puntual), ejecutaremos en ambos sistemas:

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

Si quisiéramos que esta configuración sobreviviera a reinicios de la máquina, editaríamos /etc/sysctl.conf y pondríamos:

net.ipv4.ip_forward = 1

Podemos pedirle al kernel que relea este fichero haciendo “sysctl -p“.

Además, es posible que las reglas de iptables impidan el tráfico. Podemos ajustarlas a medida o, si conocemos los riesgos de seguridad que asumimos, podemos eliminar cualquier restricción ejecutando en ambos extremos:

iptables -F

A partir de este punto, todos los nodos de cada una de las redes será capaz de comunicarse libremente con todos los nodos de la otra red. Podemos verificar esto, por ejemplo, con el comando traceroute de Linux o el tracert de Windows:

  • Sistema Windows en la red 192.168.10.0/24 llegando al nodo sistema101.red172.net (172.16.20.101) de la red 172.16.20.0/24:
C:\>tracert sistema101.red172.net

Tracing route to sistema101.red172.net [172.16.20.101]
over a maximum of 30 hops:

  1    <1 ms    <1 ms    <1 ms  clientessh192.red192.net [192.168.10.2]
  2    93 ms    81 ms    82 ms  10.0.0.2
  3    83 ms    80 ms    81 ms  sistema101.red172.net [172.16.20.101]

Trace complete.
  • Sistema Linux en la red 172.16.20.0/24 llegando al nodo sistema35.red192.net (192.169.10.35) de la red 192.168.10.0/24:
# traceroute sistema35.red192.net
traceroute to sistema35.red192.net (192.169.10.35), 30 hops max, 38 byte packets
 1  servidorssh172.red172.net (172.16.20.2)  0.358 ms  0.295 ms  0.201 ms
 2  10.0.0.1 (10.0.0.1)  80.756 ms  80.443 ms  81.502 ms
 3  sistema35.red192.net (192.169.10.35)  79.250 ms  81.179 ms  81.858 ms

Para finalizar, una curiosidad: El vetusto VPN HOWTO muestra cómo crear una VPN mediante SSH y PPP.

:wq

Entradas relacionadas

11 Comentarios a “VPN con OpenSSH”

  • David dice:

    Muchas gracias SuperCoco, había echado mucho de menos tu blog

  • RuBiCK dice:

    Buen artículo!!

  • Grohl dice:

    Hola:

    gracias por tus artículos. Creo que te lo he dicho más de una vez: tu blog es uno de los más interesantes y didácticos de la blogosfera.

    Por otra parte ¿ que pasaría si el rango de ambas redes locales fuese 192.168.10.x ? No es extraño que redes locales diferentes usen el mismo rango privado de direcciones.

    • ¡Gracias a ti por leerlo!

      En tal caso, habría que configurar algún tipo de NAT en uno o ambos de los puntos de acceso. Por ejemplo, que los nodos de una red 192.168.10.x vieran a los de la otra red 192.168.10.x como 192.168.20.x, de forma que para acceder al servidor 192.168.10.210 de la segunda red, los nodos de la primera usaran la IP 192.168.20.210.

      Usaríamos comando similares a estos (NAT HOWTO) para mapear cada IP de una red con una IP de otra red:

      iptables -t nat -A POSTROUTING -s 192.168.10.0/24 -o eth0 -j SNAT –to 192.168.20.0/24
      iptables -t nat -A PREROUTING -s 192.168.20.0/24 -o eth0 -j DNAT –to 192.168.10.0/24

      Alternativamente, también podríamos usar masquerading para hacer lo mismo que hacen los routers, compartir una única IP pública entre todos los nodos de la red interna. Tendríamos que usar comandos similares a éste en ambos nodos de acceso:

      iptables -t nat -A POSTROUTING -o eth1 -j MASQUERADE

  • Jordi Ver dice:

    Me has dejado con la boca abierta! Impresionante artículo Super ;) !

  • Jonathan dice:

    Vicente,
    Te saluda un colega de Argentina, tu blog me pareció muy interesante y ya lo estoy añadiendo a mis feeds, se ve que tenes experiencia y le das mucha dedicación a lo que haces.

    Siempre es bueno leer información de personas así.

    Mi blog bastante más chico tiene una temática similar.

    Saludos desde Argentina,
    Jonathan

  • Der Teufel dice:

    Hola!

    Muy buenos artículos, como siempre :-)

    Estaría bien que hicieras una página en FB y se publiquen automáticamente las noticias, jeje, últimamente me es mas fácil que leer los feeds:

    http://www.facebook.com/RSS.Graffiti

    ¡Saludos!

Trackbacks y pingbacks:

Tema LHYLE09, creado por Vicente Navarro