El artículo de hoy trata de otro escenario que conlleva cambios en lo visto hasta ahora, y es cuando queremos acceder a través de VPN a la web app que llevamos viendo en los últimos dos artículos pero no directamente, que sería conectándonos a la red donde está la web app, sino desde la red del escenario anterior, other-vnet:
Este artículo pertenece a una serie donde ya hemos visto:
- Acceder a un App Service con Private Endpoint dentro de la misma Vnet
- Acceder a un App Service con Private Endpoint desde otra Vnet
Si quieres montar el escenario completo, deberás seguir primero los otros dos.
Para lo que nos toca hoy, vamos a introducir dos conceptos, quizás nuevos para ti, que son VPN Gateway y Gateway transit.
VPN Gateway
Lo primero que debemos conseguir es que otras máquinas en on-prem puedan conectarse a nuestra red en Azure, de tal forma que consigamos el mismo efecto que con el emparejamiento de redes, esto es: que todas las máquinas/servicios se vean como si estuvieran en la misma red. En el caso de las conexiones que están en otro sitio que no sea Azure, necesitamos de un componente llamado VPN Gateway que va a ser la puerta que nos ayudará a comunicarnos con otros sitios más allá de nuestra nube. En este ejemplo he comentado además que, para complicarnos un poco más ;-), no vamos a conectarnos directamente a la red donde se encuentra la web app, esto es webapp-vnet, sino que vamos a conectarnos a other-vnet en su lugar. Para hacer esto, debemos seguir los siguientes pasos:
###### Scenario 3: Access a web app from a VPN using transit routing ######
# 17. Create a VPN Gateway to connect wit my machine
VPN_GATEWAY_NAME="gateway"
VPN_GATEWAY_CIDR=10.20.3.0/24
# 17.1. Create a subnet for the VPN Gateway
az network vnet subnet create \
--vnet-name $OTHER_VNET_NAME \
--name GatewaySubnet \
--resource-group $OTHER_RESOURCE_GROUP \
--address-prefix $VPN_GATEWAY_CIDR
# 18. Create a public IP for the VPN Gateway
az network public-ip create \
--name "${VPN_GATEWAY_NAME}-ip" \
--resource-group $OTHER_RESOURCE_GROUP \
--allocation-method Dynamic
# 19. Define CIDR block for the VPN clients
ADDRESS_POOL_FOR_VPN_CLIENTS=10.30.0.0/16
# Azure Active Directory info
#https://login.microsoftonline.com/<YOUR_TENANT_ID>
TENANT_ID="<YOU_TENANT_ID>"
AZURE_VPN_CLIENT_ID="41b23e61-6c1e-4545-b367-cd054e0ed4b4"
#You have to consent Azure VPN application in your tenant first:
https://login.microsoftonline.com/common/oauth2/authorize?client_id=41b23e61-6c1e-4545-b367-cd054e0ed4b4&response_type=code&redirect_uri=https://portal.azure.com&nonce=1234&prompt=admin_consent
# 20. Create a VPN Gateway
az network vnet-gateway create \
--name $VPN_GATEWAY_NAME \
--location $LOCATION \
--public-ip-address "${VPN_GATEWAY_NAME}-ip" \
--resource-group $OTHER_RESOURCE_GROUP \
--vnet $OTHER_VNET_NAME \
--gateway-type Vpn \
--sku VpnGw2 \
--vpn-type RouteBased \
--address-prefixes $ADDRESS_POOL_FOR_VPN_CLIENTS \
--client-protocol OpenVPN \
--vpn-auth-type AAD \
--aad-tenant "https://login.microsoftonline.com/${TENANT_ID}" \
--aad-audience $AZURE_VPN_CLIENT_ID \
--aad-issuer "https://sts.windows.net/${TENANT_ID}/"
Lo primero que hago es crear una nueva subnet, que debe tener el nombre GatewaySubnet, donde vivirá el VPN Gateway, creo una IP pública para el servicio y defino el CIDR para los clientes que accedan a través de la VPN para obtener una IP válida dentro de la red y que no se solape con otras. En este ejemplo uso Azure Active Directory para la autenticación durante la conexión a la VPN, por lo que tengo que hacer un paso adicional: necesito autorizar a la aplicación Azure VPN sobre mi tenant para poder validar los usuarios que se autentican. Esto se hace utilizando la siguiente URL:https://login.microsoftonline.com/common/oauth2/authorize?client_id=41b23e61-6c1e-4545-b367-cd054e0ed4b4&response_type=code&redirect_uri=https://portal.azure.com&nonce=1234&prompt=admin_consent. Te aparecerá un mensaje como el siguiente que deberás aceptar:
Además, necesito recuperar el Id del Azure Active Directory que quiero utilizar para esa autenticación y autorización. Este lo puedes conseguir en el apartado Overview de este servicio:

El client Id de la aplicación Azure VPN es principalmente el que te he dejado en el código, pero puede variar. El que aparece en mi ejemplo es para Azure Public.
Una vez que tienes estos datos guardados en las variables de entorno del shell, el último paso es generar el gateway con toda la información anterior. Cuando termine el proceso tendrás un nuevo recurso llamado gateway en el grupo de recursos y la foto de tu arquitectura quedaría así:
Este recurso, con los parámetros opcionales que he añadido en su creación, ya debería tener la configuración para las conexiones del tipo Point to Site:
Lo último que debes hacer relativo a la conexión a través de VPN es configurar algún cliente. Para ello puedes hacer uso del botón que ves en la imagen anterior llamado Download VPN client o bien utilizar este comando que generará una URL donde poder descargarte el mismo contenido:
# Get VPN client configuration
az network vnet-gateway vpn-client generate \
--resource-group $OTHER_RESOURCE_GROUP \
--name $VPN_GATEWAY_NAME
De cualquiera de las maneras descarga el archivo comprimido. Verás que dentro de él tienes tres archivos a tu disposición. Ahora necesitas descargar el cliente de Azure VPN, el cual está disponible tanto para Mac como para Windows, que te ayudará de una forma sencilla con la configuración de tu máquina. Una vez instalado, haz clic en el + y selecciona la opción Import.
Dentro de los archivos descargados selecciona el llamado AzureVPN\azurevpnconfig.xml. Automáticamente se rellenarán los campos del cliente. Haz clic en el botón Save y después en Connect.
Añade si es necesario la cuenta con la que quieres logarte y permite a la aplicación añadir esta configuración a la configuración de tu equipo.
Si la autenticación ha ido bien, y has autorizado añadir la nueva conexión, debería de aparecer como Connected.
Ahora ya estás conectado a la red other-vnet desde tu máquina 🙂 Es más, si intentas hacer un ping a la máquina virtual other-vnet-vm, a través de su IP privada 10.20.1.4, el resultado debería de ser satisfactorio.
Sin embargo, si intento acceder a internalweb a través de su IP (10.10.1.4), aunque me de como resultado bad request (por intentar acceder sin usar su hostname), en su lugar me da un timeout.
¿Qué ha ocurrido? Si ya me he conectado a other-vnet y esta ya tiene un peering con webapp-vnet. Aquí es donde entra en juego el Gateway transit.
Gateway transit
Pues en este caso, además de tener un peering necesitas habilitar la opción transit Gateway que ahora tienes deshabilitada:

Esto lo que permite es que cuando nos conectamos a través del Gateway VPN este anuncie además la red con la que hace peering, para que si desde mi cliente conectado a la VPN hace referencia a una IP de ese rango sepa que tiene que ir a través de este, y que pueda ir. Para actualizar el peering puedes utilizar este comando:
# 22. Update peering to use transit routing
az network vnet peering update \
--name "peering-with-webapp-vnet" \
--resource-group $OTHER_RESOURCE_GROUP \
--vnet-name $OTHER_VNET_NAME \
--set allowGatewayTransit=true
az network vnet peering update \
--name "peering-with-other-vnet" \
--resource-group $WEB_APP_RESOURCE_GROUP \
--vnet-name $WEB_APP_VNET_NAME \
--set useRemoteGateways=true
Con ello nuestra foto se actualizaría de la siguiente forma:
Si desconectas el cliente VPN y lo vuelves a conectar verás el siguiente cambio respecto a la conexión anterior:

Ahora mi máquina sabe que si se intento acceder a alguna IP dentro de ese rango debe ir a través de nuestra VPN. Si ahora intentas acceder a la web a través de la IP, para comprobar que el acceso es posible, ya la alcanzamos «sin problemas»:
Sin embargo, en este escenario Private DNS Zone no puede ayudarnos, al no estar soportada la resolución de nombre a través de esta conexión P2S. Para este caso, sí que necesitaríamos un servidor DNS propio que hiciera el mismo trabajo. Para este laboratorio lo que podemos hacer, para confirmar que funciona, es modificar el archivo hosts (c:/windows/system32/drivers/etc/hosts en Windows y /etc/hosts en Mac) y añadir la última línea:
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost
# Added by Docker Desktop
# To allow the same kube context to work on the host and the container:
127.0.0.1 kubernetes.docker.internal
# End of section
10.10.1.4 internalweb.azurewebsites.net
Con ello conseguimos la resolución del nombre localmente y ahora si que podemos acceder sin problemas.

Los comandos los tienes en mi GitHub.
¡Saludos!

Bootcamp Backend
Si tienes ganas de ponerte al día con tecnologías de backend, formo parte del
equipo de docentes del Bootcamp Backend de Lemoncode, ¿Te animas a
aprender con nosotros?