Menu

Conexiones VPN con Azure y recursos on-premises

Lo que vamos a aprender hoy es cómo implementar una conexión VPN ‘site-to-site’ y ‘host-to-site’ entre nuestros recursos on-premises y Azure sin establecer una puerta de enlace virtual. Es decir, hacer la conexión sin usar el recurso de “Virtual GateWay” que nos ofrece Microsoft.

La intención de todo esto es que podamos interactuar, desde el centro de trabajo, con los diferentes recursos que tendríamos en Azure. Es sabido que si usamos un recurso “Virtual GateWay”, podemos interactuar perfectamente con ellos. En cambio, si no lo hacíamos así, es decir, si implementábamos un servidor VPN tal cual en Azure, lo que pasaba era que podíamos comunicarnos con el servidor, pero no con los demás recursos. Sin embargo, profundizando con la investigación, pudimos encontrar una manera de lograrlo.


Esta implementación, a cambio de que podamos controlar nosotros más aspectos de la misma, resulta ser un poco más técnica que el método que pudiera ofrecernos Microsoft. Además, haciendo pruebas de medición de ancho de banda, no difiere mucho cuando se hace de la otra forma. Pero bueno, como resultado de mucha investigación pues deviene en ser otra manera de realizar conexiones VPN.

Agradecer a Alejandro, ya que realmente fue él quien hizo el grueso de la investigación (en particular, descubrir la forma de usar las “UDR-tables” de Azure y los comandos “iptables” de Linux).

Nota: en este documento se asume que el usuario sabe implementar recursos en Azure, además de configurar el enrutamiento en el centro de trabajo local.


ESQUEMA DE LA IMPLEMENTACIÓN

Para ilustrar un poco cómo haríamos esta implementación, nos valdremos del siguiente esquema:

Bien, si nos fijamos, veríamos lo siguiente:

  • Una típica infraestructura abajo a la derecha, la cual englobaría nuestros recursos on-premises. Podemos ver que tenemos un dispositivo Raspberry Pi 3 que servirá como servidor VPN que se comunicará con el otro servidor VPN en Azure; tres portátiles de trabajo; y un dispositivo móvil, que está en una subred diferente a los demás recursos (tiene la IP 192.168.2.9).
  • Y en Azure, lo que veríamos es una máquina que servirá como servidor VPN para conectar con la Raspberry Pi, además de otras dos, una máquina Linux típica, y otra Windows Server 2016.

Para que funcione bien la implementación, necesitamos aislar en su propia subred al servidor VPN en Azure, de tal manera que se podría decir que lo convertimos en un “router”en sí.

Una de las cosas que íbamos a lograr es, aparte de poder comunicar con normalidad con los recursos, acceder por “Escritorio Remoto” con la máquina Windows Server 2016.


STRONGSWAN

El software con el que estableceríamos las conexiones VPN sería el programa “strongSwan”. Es una herramienta “open-source”, gratuita y disponible para muchas distribuciones Linux. Algo bueno que tiene esta herramienta es que, además de realizar conexiones “site-to-site”, podremos implementar también conexiones “host-to-site”. Nosotros conseguimos que funcionara directamente con equipos Windows (7, 8 y 10); no así con dispositivos Android.

En el caso de Android, existe un cliente “strongSWan”, el cual abstrae muchas características de configuración; es muy fácil configurarlo, lo único que no se realiza la conexión (creemos que por algún detalle en el certificado probablemente). En cuanto logremos hacer la conexión, haré otro documento explicando cómo configurarlo.

CONFIGURANDO AZURE

Lo primero de todo era abrir ciertos puertos en un grupo de seguridad en Azure, en particular los puertos 500, 4500 y 3389. Los dos primeros son los puertos con los que trabaja el “strongSwan”, y el tercero es el puerto por defecto que usan las conexiones de “Escritorio Remoto” de Windows. Nos quedaría más o menos así:


El puerto 1986 lo incluimos para hacer pruebas de ancho de banda con el programa “iperf3”, pero es totalmente opcional.

Luego, lo que haríamos sería emplazar la máquina Linux con el “strongSwan” en su propia subred para que, recibiendo peticiones de los recursos de Azure con destino a los recursos on-premises, sea capaz de enrutarlos correctamente:


Tal y como habíamos dicho antes, tendríamos dos subredes, una para el servidor (en este caso, una máquina llamada “javan-VM” con Linux), y otra para dos nodos (una máquina Linux llamada “node1”, y otra Windows Server 2016).

Hecho lo anterior, entraríamos en el servidor de Azure para instalar el “strongSwan” con el comando “sudo apt-get install strongswan strongswan-plugin-eap-mschapv2”. Una vez instalado, pasaríamos a editar dos archivos, “/etc/ipsec.conf” y “/etc/ipsec.secrets”.

En el primero es donde establecemos las conexiones que hará el servidor. Para realizar una, pondríamos lo siguiente, que son los parámetros mínimos para empezar:

conn %default
authby=secret
auto=add
type=tunnel
keyexchange=ikev2
ike=aes256-sha1
esp=aes256-sha1
left=10.0.9.4 # Depende de la configuración que hagamos
leftid=@irl.intelequia.com # Podemos poner cualquier cosa
leftsubnet=10.0.0.0/8 # Subred que expondremos

conn TEST
right=<IP pública on-premises>
rightid=@tfe.intelequia.com
rightsubnet=192.168.0.0/16


La primera sección, llamada “conn %default”, es para indicarle al servidor que los parámetros que pongamos a continuación serán válidos para todas las conexiones que hagamos. De esta manera, lo que pusimos fue lo siguiente:

  • Con “authby=secret”, le decimos que la autenticación se hará con el archivo “/etc/ipsec.secrets”.
  • En “auto=add”, el servidor, en cuanto detecte cualquier comunicación que concuerde con la configuración del mismo, levantará la conexión. Si ponemos “auto=start”, la levantará inmediatamente, sin esperar a que haya comunicación alguna.
  • Como la conexión que queremos hacer es comunicar ambos sitios, le indicamos que el tipo es de túnel (“type=tunnel”).
  • Los tres siguientes parámetros (“keyexchange”, “ike” y “esp”) se refieren al protocolo de conexión (el primero), y a los algoritmos de encriptación a usar (los dos siguientes).
  • Y los tres últimos son los que definen el servidor que realizará la conexión: “left=10.0.9.4” es el servidor en el que estamos (tiene que ser una IP privada), “leftid=@irl.intelequia.com” es un identificador para la conexión, y “leftsubnet=10.0.0.0/8” es la subred que expondremos en la conexión. Hay que tener en cuenta dos cosas con el identificador: primero, anteponiendo la “@” le indicamos al servidor que la identificación no la haga por DNS, de tal manera que podemos poner cualquier cosa; y segundo, es obligatorio en el caso de que usemos certificados para la autenticación con el servidor.

La segunda sección define el extremo de nuestra conexión, es decir, el servidor “strongSwan” en nuestro centro de trabajo (que en este caso es el dispositivo Raspberry):

  • Si antes pusimos la IP privada en “left”, en “right” ponemos la IP pública del centro de trabajo (“right=<IP pública on-premises>”).
  • En “rightid=@tfe.intelequia.com”, establecemos otro identificador.
  • Y por último, la subred que nos expondrá la Raspberry (“rightsubnet=192.168.0.0/16”). En este caso, lo que pusimos fue una máscara para que coja todas las subredes on-premises (tanto 192.168.1.0/24 como 192.168.2.0/24).

En el segundo archivo, “/etc/ipsec.secrets”, lo que ponemos es una clave para autenticar al servidor on-premises:

: PSK "insiemenoi"


Ni que decir que esta clave deberá ser la misma en el servidor on-premises.

Ahora viene la configuración de la tabla de rutas con el comando “iptables”. Se trata de una serie de comandos que configurarán al servidor para aceptar y/o rechazar las comunicaciones hacia el servidor on-premises. Nosotros hicimos un pequeño “script” para realizarlo todo de una vez:

# Primero, activamos el reenvío en el kernel
sysctl -w net.ipv4.ip_forward=1

# Instalamos la aplicación 'netfilter-persistent'
apt-get install netfilter-persistent

# Borramos cualquier regla que existiera mientras permitimos ciertas cadenas
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -F
iptables -Z

# Abrimos el puerto 22 para trabajar remotamente por 'ssh' y aceptamos paquetes de la interfaz de loopback iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT

# Abrimos los puertos 500 y 4500 para el 'strongSwan'
iptables -A INPUT -p udp --dport 500 -j ACCEPT
iptables -A INPUT -p udp --dport 4500 -j ACCEPT

# Definimos sobre qué subredes reenviará los paquetes
iptables -A FORWARD --match policy --pol ipsec --dir in --proto esp -s 192.168.0.0/16 -j ACCEPT
iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -d 192.168.0.0/16 -j ACCEPT
iptables -A FORWARD --match policy --pol ipsec --dir in --proto esp -s 10.0.0.0/8 -j ACCEPT
iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -d 10.0.0.0/8 -j ACCEPT

# Qué subredes enrutará el kernel
iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
iptables -t nat -A POSTROUTING -s 192.168.0.0/16 -o eth0 -j MASQUERADE
iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.0.0.0/8 -o eth0 -j MASQUERADE

# Definimos el tamaño de los paquetes
iptables -t mangle -A FORWARD --match policy --pol ipsec --dir in -s 192.168.0.0/16 -o eth0 -p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 -j TCPMSS --set-mss 1360

# Guardamos la configuración y la recargamos
netfilter-persistent save
netfilter-persistent reload

  

Una vez hecho todo lo anterior, se reinicia el servicio con “sudo ipsec restart”. Si queremos ver todos los registros que va anotando el servidor, podemos añadir otro comando al anterior, algo como “sudo ipsec restart && sudo tail -f /var/log/syslog | grep charon”. De entrada no veríamos mucha cosa interesante, más allá de los mensajes de inicio del servicio, de la carga de plugins, etc.

Llegados a este punto, tenemos un “router” en su propia subred con el servicio “strongSwan” funcionando, y dos máquinas en otra subred.

Y ya con esto era el momento de configurar el servidor on-premises.

CONFIGURANDO LA RASPBERRY

Como paso previo antes de configurar la Raspberry, hay que establecer una ruta en el “router” on-premises para que todo el tráfico hacia la subred 10.0.0.0/8 se redirija a la Raspberry.

En este caso, la configuración no difiere mucho de como lo hicimos en el servidor en Azure; a grandes rasgos, podríamos decir que se invierten los valores de algunos parámetros:

conn %default
authby=secret
auto=add
type=tunnel
keyexchange=ikev2
ike=aes256-sha1
esp=aes256-sha1
left=192.168.1.3
leftid=@tfe.intelequia.com
leftsubnet=192.168.0.0/16

conn TEST
right=<IP pública Azure>
rightid=@irl.intelequia.com
rightsubnet=10.0.0.0/8


Y lo mismo que pusimos en el archivo “/etc/ipsec.secrets” del servidor en Azure:

: PSK "insiemenoi"


En cuanto a las “iptables”, el script es el mismo, pero invirtiendo la última línea que hacía referencia a la table “mangle” (en concreto, se sustituye la subred 192.168.0.0/16 por 10.0.0.0/8).

Se reinicia el servicio (“sudo ipsec restart”), y entonces deberíamos ver que se ha levantado la conexión si solicitamos el estado del servicio con “sudo ipsec status”:


Tal y como le habíamos dicho, estableció una conexión de tipo túnel entre los dos extremos que habíamos indicado.

Podíamos ver que, efectivamente, había comunicación con los diferentes recursos de Azure. Primero habíamos probado con la máquina Linux con IP 10.0.18.5:


Y lo comprobaríamos también estableciendo una conexión de “Escritorio Remoto” con la máquina Windows Server 2016 con IP 10.0.18.4:


Con esto, ya teníamos configurada la conexión VPN entre Azure y el centro on-premises, empleando únicamente máquinas Linux con “strongSwan”.

UDR-TABLES

Anteriormente vimos que podíamos comunicarnos con los recursos de Azure desde el centro de trabajo, pero no era así cuando eran los recursos de Azure los que debían hacerlo con nosotros. Investigando un poco, para conseguir esto (lo cual es opcional), había que emplear un recurso de Azure llamado “UDR-tables” (“User Defined Routes-tables”, o “tablas de rutas definidas por el usuario”).

Funcionan prácticamente igual que cuando usamos el commando “ip route…” en Linux. En este caso, creábamos un recurso del tipo en cuestión con el nombre que quisiéramos, y para configurarlo, hacíamos lo siguiente:

  • Primero, iríamos a la sección “Routes” para añadir las rutas que seguirían los recursos de Azure (en este caso, le diríamos que todo el tráfico hacia la subred 192.168.0.0/16 fuera redirigido hacia el servidor “strongSwan” de Azure, osea, a la IP 10.0.9.4). Recalcar que el panel nos avisa de que hay que activar el reenvío de IP (sombreado en blanco) de la interfaz de red de la máquina afectada, además de que para poner la IP del servidor hay que seleccionar la opción “Virtual appliance” (los que tengan el panel en castellano, “Aplicación virtual”):


  • Y segundo, habríamos de ir a la sección “Subnets” para decirle a la tabla qué subredes se verán afectadas. Como son ambas, la de las máquinas Windows y Linux y la del servidor “strongSwan”, las ponemos:


Si queríamos comprobar si las máquinas hacían uso de las rutas, a la izquierda en el panel teníamos una sección llamada “Effective routes” (“Rutas efectivas”). Si la seleccionábamos, podíamos ver qué rutas usaban:


Vemos que aparece la ruta que habíamos definido antes, pero de no ser así, bastaría con reiniciar la máquina en cuestión para que importe la configuración. Y así, ya los recursos de Azure también podrían comunicarse con los recursos on-premises.

HOST-TO-SITE

Anteriormente dijimos que, además de hacer una conexión “site-to-site”, habíamos podido hacerla del tipo “host-to-site”. Esto es bastante útil por si, por ejemplo, queremos conectarnos desde cualquier lugar a los recursos de la infraestructura (sean de Azure o sean del centro de trabajo).

En este caso, la configuración resulta un poco más compleja, ya que requeriríamos del uso de certificados; la habíamos realizado en el servidor de Azure ya que pensamos que no solo permitiría conectar a los recursos de la plataforma, sino también a los recursos on-premises. Y así fue.

En el servidor, lo que hicimos fue editar de nuevo el archivo “/etc/ipsec.conf” para configurar otra conexión así:

config setup
uniqueids=no

conn HOST-TO-SITE
auto=add
type=tunnel
keyexchange=ikev2
ike=aes256-sha1-modp1024,3des-sha1-modp1024!
esp=aes256-sha1,3des-sha1!
leftid=@<IP pública Azure>
leftcert=/etc/ipsec.d/certs/vpn-server-cert.pem
leftsendcert=always
leftsubnet=0.0.0.0/0
right=%any
rightid=%any
rightauth=eap-mschapv2
rightdns=8.8.8.8,8.8.4.4
rightsourceip=172.16.0.0/24
rightsendcert=never
eap_identity=%identity


Aquí podemos ver que:

  • En los parámetros “right” y “righted” ponemos el valor “%any”, para indicarle al servidor que cualquiera puede acceder a la VPN.
  • En el identificador ponemos un valor que usaremos a la hora de crear un certificado (en este caso, pusimos la propia IP del servidor; más adelante lo detallamos en el proceso de creación del certificado).
  • Habíamos añadido los parámetros “leftid=@<IP pública Azure>”, “leftcert=/etc/ipsec.d/certs/vpn-server-cert.pem” (el lugar en el que almacenaríamos el certificado), “leftsendcert=always”, “rightauth=eap-mschapv2”, ”rightdns=8.8.8.8,8.8.4.4”, “rightsourceip=172.16.0.0/24”, “rightsendcert=never” y “eap_identity=%identity”, que son los que permitirán conectar a un host a la conexión VPN.

El parámetro “rightsourceip” es interesante, ya que es el que nos permitirá otorgar una dirección IP “virtual” a cada host que se conecte.

Luego, editamos el archivo “/etc/ipsec.secrets” para poner lo siguiente:

<IP pública Azure> : RSA "/etc/ipsec.d/private/vpn-server-key.pem"
javan %any% : EAP "1234"
javon %any% : EAP "1234"


Lo que hacemos aquí es indicarle al servidor dónde buscar la clave privada; y además, definimos una serie de usuarios con su correspondiente contraseña.

Otra cosa a tener en cuenta es que, al hacer uso de unas IPs virtuales, necesitamos que el servidor sea capaz también de enrutar sus paquetes adecuadamente; en este caso, de nuevo hacemos uso de los comandos “iptables”:

iptables -A FORWARD --match policy --pol ipsec --dir in --proto esp -s 172.0.0.0/11 -j ACCEPT
iptables -A FORWARD --match policy --pol ipsec --dir out --proto esp -s 172.0.0.0/11 -j ACCEPT
iptables -t nat -A POSTROUTING -s 172.0.0.0/11 -o eth0 -m policy --pol ipsec --dir out -j ACCEPT
iptables -t nat -A POSTROUTING -s 172.0.0.0/11 -o eth0 -j MASQUERADE


De esta manera, íbamos a ser capaces también de comunicar todos los dispositivos ‘host-to-site’ que estuvieran conectados.

Pero para que todo esto funcione, necesitábamos crear un certificado.

Para ello, podíamos ayudarnos de los siguientes comandos:

# Primero, generamos una clave privada para la Autoridad
# Certificadora (nosotros)
ipsec pki --gen --type rsa --size 4096 --outform pem > server-root-key.pem chmod 600 server-root-key.pem

# Luego, la propia Autoridad Certificadora usando la clave
# previa
ipsec pki --self --ca --lifetime 3650 \
--in server-root-key.pem \
--type rsa --dn "C=ES, O=Organización, CN=VPN Server Root CA" \
--outform pem > server-root-ca.pem

# Ahora, generamos una clave privada para el servidor
ipsec pki --gen --type rsa --size 4096 --outform pem > vpn-server-key.pem

# Firmamos un certificado para el mismo con la clave anterior
ipsec pki --pub --in vpn-server-key.pem \
--type rsa | ipsec pki --issue --lifetime 1825 \
--cacert server-root-ca.pem \
--cakey server-root-key.pem \
--dn "C=ES, O=Organización, CN=<IP pública Azure>" \
--san <IP pública Azure> \
--flag serverAuth --flag ikeIntermediate \
--outform pem > vpn-server-cert.pem

# Copiamos el certificado y la clave privada a sus directorios,
# y hacemos que solo el usuario 'root' sea capaz de acceder a
# la última
cp ./vpn-server-cert.pem /etc/ipsec.d/certs/
cp ./vpn-server-key.pem /etc/ipsec.d/private/
chown root:root /etc/ipsec.d/private/vpn-server-key.pem
chmod 600 /etc/ipsec.d/private/vpn-server-key.pem

# Finalmente, exportamos el contenido del archivo de la
# autoridad certificadora para convertirlo en un certificado
# para los dispositivos
cat ./server-root-ca.pem > certificate.pem


Así, se pueden crear los certificados tanto para el servidor como para un equipo cliente que queramos.

Si nos fijamos en la segunda línea marcada con muchos asteriscos, veríamos que el parámetro “CN” contiene una IP. Es precisamente el campo que define el identificador en el parámetro “leftid” del archivo “/etc/ipsec.conf” en el servidor. Lo que nosotros pongamos ahí, tendremos que ponerlo en el identificador. De hecho, los campos “C=”, “O=” y “CN=” habríamos de rellenarlos con los datos de nuestra organización.

Recomiendo poner la dirección IP para evitar problemas de DNS, pero vale igualmente el nombre de nuestra organización/empresa. Una cosa a tener en cuenta es que, si ponemos el nombre, podemos dejar de anteponer el símbolo “@” para que el servicio haga la autenticación por dominio.

Reiniciamos el servicio con “sudo ipsec restart”, y pasamos entonces a configurar un cliente para conectarlo a la VPN.

Por ejemplo, habíamos supuesto que estábamos con un equipo Windows 10, y que teníamos en el equipo un archivo con extension “.pem” conteniendo el certificado, tal y como nos indicaba la última línea de la captura anterior. Manteniendo pulsada la tecla Windows, pulsamos la tecla “r” para ejecutar el programa “mmc.exe”. En la nueva ventana, abrimos la pestaña “Archivo” para seleccionar la opción “Añadir complementos”. Seleccionaremos a la izquierda “Certificados”, y luego “Cuenta de equipo” y “Equipo local” para que nos quede así más o menos:


Le damos a “Ok”, y entonces podremos añadir el certificado expandiendo “Certificados > Entidades de certificación de confianza > Certificados”. Hacemos clic derecho en “Certificados”, y en “Todas las tareas” seleccionamos “Importar”. Buscamos el archivo, y nos cercioramos de que se almacena en “Entidades de certificación de confianza”:


Si la importación se realiza con éxito, veremos por abajo nuestro certificado. Entonces habrá llegado el momento de hacer la conexión.

Navegamos hasta el “Centro de redes y recursos compartidos” y configuramos una nueva conexión VPN a un centro de trabajo y usando nuestra conexión a Internet:


Si le damos a “Crear”, se crea la conexión, pero no se levanta ya que aún no hemos puesto las credenciales.

A la izquierda, seleccionamos “Cambiar configuración del adaptador”, y hacemos doble clic en la conexión recientemente creada. De nuevo hacemos clic en la conexión, luego en “Conectar”, y acto seguido nos pedirá las credenciales:


Y si todo salió bien, veríamos que estábamos conectados:


Y cómo no, pudimos acceder nuevamente por “Escritorio Remoto” al servidor Windows Server 2016 en Azure:


Arriba pudimos ver, en una sesión de terminal remoto con el servidor strongSwan, que nos otorgó la IP 172.16.0.2 para poder interactuar con los recursos de Azure. Y abajo habíamos ejecutado un comando “tracert 10.0.18.4” para poder ver la ruta que emplea el equipo para llegar hasta la máquina Windows Server.

VALORACIONES

Pues bien, aunque no fue posible hacer conexiones con dispositivos Android utilizando el cliente “strongSwan” para móviles, suponemos que deben ser plausibles. De todas formas, incluso en la documentación del programa admiten que suele haber errores en el cliente.

Esta técnica en particular supone el poder interconectar todas las sedes que una empresa/organización tenga, y además permitir conexiones incluso desde cualquier lugar. Todo ello cifrando las comunicaciones y dificultando cualquier intento de interceptación de las mismas.

El método ‘host-to-site’ fue tan potente en este caso, que incluso permitió acceder tanto a los recursos de Azure como a los recursos on-premises desde otro lugar diferente tanto del centro de trabajo como de Azure. Así, podíamos iniciar sesiones de “Escritorio Remoto” hacia cualquier host on-premises utilizando la conexión que hacíamos con Azure (porque, en efecto, aún teníamos activa la conexión “site-to-site” de antes).

Decir que también tuvimos la posibilidad de poder comunicar con los recursos ‘host-to-site’ que estaban conectados a varios kilómetros entre sí.

FUENTES CONSULTADAS