Podría tratarse de dos sucursales en una misma ciudad o en lugares diferentes del mundo. En este caso en particular se van a tratar de dos redes en dos regiones diferentes de AWS. Una en eu-west-1 (Dublin) y otra en us-east-1 (North Virginia). A continuación una imagen que posiblemente aclare un poco mas el entorno (estrenando pizarra jajaj):
Qué hace falta?
Dado que se va a tratar de un entorno virtual, los requerimientos son sencillos:
- Dos redes privadas diferentes separadas geográficamente de tal forma que la hostil Internet sea el único medio viable para comunicarlas.
- Dos endpoints uno en cada región:
- En este caso dos instancias EC2 en VPCs con redes diferentes.
- Cada endpoint debe contar con una EIP asociada.
- Ubuntu 14.04 LTS con OpenSwan instalado en ambos endpoints (apt-get install -y openswan)
- El tráfico a los puertos UDP 500 y 4500 debe estar permitido entre los endpoints. Estos puertos se utilizaran para establecer las Asociaciones de Seguridad y transportar los paquetes ESP cifrados (respectivamente).
Configuración del kernel
Dado que los endpoints oficiaran de routers en este caso, es necesario setear algunos parámetros extras en el kernel de los mismos. OpenSwan provee los mismos en el archivo /etc/ipsec.d/examples/sysctl.conf, por lo tanto podemos copiarlos a /etc/sysctl.d (para garantizarnos que se cargan luego en caso de reinicio) y ejecutarlos manualmente:
root@ip-172-31-16-163:/etc/ipsec.d# cp /etc/ipsec.d/examples/sysctl.conf /etc/sysctl.d/11-openswan.conf
root@ip-172-31-16-163:/etc/ipsec.d# sysctl -p /etc/sysctl.d/11-openswan.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.default.log_martians = 0
net.ipv4.conf.all.log_martians = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.neigh.default.gc_thresh1 = 1024
net.ipv4.neigh.default.gc_thresh2 = 2048
net.ipv4.neigh.default.gc_thresh3 = 4096
root@ip-172-31-16-163:/etc/ipsec.d#
Con el kernel ya listo procedemos a configurar OpenSwan
Configuración de OpenSwan:
Por defecto OpenSwan viene casi listo para arrancar y con el soporte para NAT-Traversal activado así que no nos tenemos que preocupar por esa parte. Para corroborar que la instalación fue exitosa podemos ver los puertos en estado LISTEN en el sistema:
ubuntu@ip-10-0-61-63:~$ sudo netstat -ntpul
sudo: unable to resolve host ip-10-0-61-63
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1071/sshd
tcp6 0 0 :::22 :::* LISTEN 1071/sshd
udp 0 0 0.0.0.0:11854 0.0.0.0:* 589/dhclient
udp 0 0 0.0.0.0:68 0.0.0.0:* 589/dhclient
udp 0 0 127.0.0.1:4500 0.0.0.0:* 2372/pluto
udp 0 0 10.0.61.63:4500 0.0.0.0:* 2372/pluto
udp 0 0 127.0.0.1:500 0.0.0.0:* 2372/pluto
udp 0 0 10.0.61.63:500 0.0.0.0:* 2372/pluto
udp6 0 0 ::1:500 :::* 2372/plutoudp6 0 0 :::1255 :::* 589/dhclient
ubuntu@ip-10-0-61-63:~$
en ambos endpoints deberíamos ver los puertos UDP 500 y 4500.
Como primer paso debemos editar el archivo /etc/ipsec.conf para indicarle donde pondremos las configuraciones extra de nuestros túneles, agregamos la siguiente linea al final:
include /etc/ipsec.d/*.conf
Ahora creamos el archivo /etc/ipsec.d/eu-west-1_us-east-1.conf con el siguiente contenido:
En el endpoint de eu-west-1:
conn tunnel
authby=secret
type=tunnel
left=172.31.16.163
leftsubnet=172.31.0.0/16
right=52.23.178.X
rightid=10.0.61.63
rightsubnet=10.0.0.0/16
En el endpoint de us-east-1:
conn tunnel
type=tunnel
auto=start
authby=secret
left=54.77.119.X
leftid=172.31.16.163
leftsubnet=172.31.0.0/16
right=10.0.61.63
rightsubnet=10.0.0.0/16
Como pueden ver las configuraciones son similares, pero a la vez espejadas en algunos campos como los left/right y las subnets. El modo de la VPN sera Tunel, se iniciará automáticamente y se autenticará con pre-shared key (el método mas sencillo de autenticación).
El último paso consiste en definir la autenticación en el archivo /etc/ipsec.secrets de la siguiente manera:
En el endpoint de eu-west-1 agregar:
10.0.61.63 172.31.16.163: PSK "bjimQ0jc"
En el endpoint the us-east-1 agregar:
172.31.16.163 10.0.61.63: PSK "bjimQ0jc"
Ahora solo basta reiniciar el servicio ipsec en ambos endpoints:
root@ip-172-31-16-163:/home/ubuntu# service ipsec restart
ipsec_setup: Stopping Openswan IPsec...
ipsec_setup: Starting Openswan IPsec 2.6.38...
ipsec_setup: No KLIPS support found while requested, desperately falling back to netkey
ipsec_setup: NETKEY support found. Use protostack=netkey in /etc/ipsec.conf to avoid attempts to use KLIPS. Attempting to continue with NETKEY
root@ip-172-31-16-163:/home/ubuntu#
root@ip-10-0-61-63:/home/ubuntu# service ipsec restart
ipsec_setup: Stopping Openswan IPsec...
ipsec_setup: Starting Openswan IPsec 2.6.38...
ipsec_setup: No KLIPS support found while requested, desperately falling back to netkey
ipsec_setup: NETKEY support found. Use protostack=netkey in /etc/ipsec.conf to avoid attempts to use KLIPS. Attempting to continue with NETKEY
root@ip-10-0-61-63:/home/ubuntu#
Y si todo salio bien, deberíamos poder ver los túneles de la siguiente manera:
root@ip-172-31-16-163:/home/ubuntu# service ipsec status
IPsec running - pluto pid: 17635
pluto pid 17635
2 tunnels up
some eroutes exist
root@ip-172-31-16-163:/home/ubuntu#
root@ip-10-0-61-63:/home/ubuntu# service ipsec status
IPsec running - pluto pid: 11655
pluto pid 11655
2 tunnels up
some eroutes exist
root@ip-10-0-61-63:/home/ubuntu#
Para mas detalles puede ejecutar ipsec auto --status.
Probando el túnel
Dado que el túnel se encuentra listo, ahora probemos hacer ping desde un endpoint al otro pero mediante las IPs privadas:
root@ip-172-31-16-163:/etc/ipsec.d# ping -c 5 10.0.61.63
PING 10.0.61.63 (10.0.61.63) 56(84) bytes of data.
64 bytes from 10.0.61.63: icmp_seq=1 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=2 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=3 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=4 ttl=64 time=81.3 ms
64 bytes from 10.0.61.63: icmp_seq=5 ttl=64 time=81.1 ms
--- 10.0.61.63 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 81.161/81.321/81.378/0.267 ms
root@ip-172-31-16-163:/etc/ipsec.d#
Vemos que todo funciona perfectamente, y con una latencia de unos 81ms, mas que razonable si consideramos que los endpoints están separadas por un buen pedazo de mundo. Con tcpdump en otra ventana capturé el tráfico que sale del endpoint en eu-west-1 con destino al otro endpoint :
root@ip-172-31-16-163:/home/ubuntu# tcpdump -n host 52.23.178.X
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
22:52:38.065238 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x1), length 132
22:52:38.146578 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x1), length 132
22:52:39.066729 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x2), length 132
22:52:39.148078 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x2), length 132
22:52:40.068222 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x3), length 132
22:52:40.149553 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x3), length 132
22:52:41.069703 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x4), length 132
22:52:41.151020 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x4), length 132
22:52:42.071171 IP 172.31.16.163.4500 > 52.23.178.X.4500: UDP-encap: ESP(spi=0x71e9743c,seq=0x5), length 132
22:52:42.152306 IP 52.23.178.X.4500 > 172.31.16.163.4500: UDP-encap: ESP(spi=0x8261d38d,seq=0x5), length 132
^C
10 packets captured
10 packets received by filter
0 packets dropped by kernel
root@ip-172-31-16-163:/home/ubuntu#
Cada uno de estos paquetes UDP 4500 lleva los mensajes ICMP enviados anteriormente dentro de un paquete ESP Algo como (((((ICMP)IP)ESP)UDP)IP). Por eso vemos 5 mensajes que salen del endpoint y 5 que vuelven.
En este punto ya tenemos la conectividad resuelta de endpoint a endpoint perfectamente, ahora sólo nos queda por resolver la conectividad con las otras instancias en las redes en cuestión. Esto tiene dos aristas:
- Deshabilitar el src/dst check en las instancias que ofician de endpoints
- Configurar las tablas de ruteo en las VPCs para que el tráfico hacia las redes en cuestión sea enrutado por los endpoints.
Basicamente hay que identificar las tablas de ruteo en ambas regiones y modificarlas de la siguiente manera:
- Tabla en eu-west-1, dirigimos todo el tráfico con destino a 10.0.0.0/16 hacia la instancia endpoint de la región:
- Tabla en us-east-1, dirigimos todo el tráfico destinado a 172.31.0.0/16 hacia la instancia endpoint de la región:
y ahora hacemos ping entre las instancias internas libremente:
[ec2-user@ip-172-31-17-235 ~]$ ping -c 5 10.0.207.203
PING 10.0.207.203 (10.0.207.203) 56(84) bytes of data.
64 bytes from 10.0.207.203: icmp_seq=1 ttl=62 time=82.1 ms
64 bytes from 10.0.207.203: icmp_seq=2 ttl=62 time=82.0 ms
64 bytes from 10.0.207.203: icmp_seq=3 ttl=62 time=82.2 ms
64 bytes from 10.0.207.203: icmp_seq=4 ttl=62 time=82.1 ms
64 bytes from 10.0.207.203: icmp_seq=5 ttl=62 time=82.2 ms
--- 10.0.207.203 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4087ms
rtt min/avg/max/mdev = 82.089/82.190/82.286/0.192 ms
[ec2-user@ip-172-31-17-235 ~]$
Reiniciar uno de los endpoints
Para corroborar que la configuración sea correcta y todo inicie normalmente mandé a reiniciar uno de los endpoints mientras hacía ping entre las instancias internas. Eh aquí van los resultados:
[ec2-user@ip-172-31-17-235 ~]$ ping 10.0.207.203
PING 10.0.207.203 (10.0.207.203) 56(84) bytes of data.
64 bytes from 10.0.207.203: icmp_seq=1 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=2 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=3 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=4 ttl=62 time=77.6 ms
64 bytes from 10.0.207.203: icmp_seq=5 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=6 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=7 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=8 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=9 ttl=62 time=77.4 ms
64 bytes from 10.0.207.203: icmp_seq=10 ttl=62 time=77.5 ms
64 bytes from 10.0.207.203: icmp_seq=11 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=13 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=14 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=15 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=69 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=70 ttl=62 time=77.2 ms
64 bytes from 10.0.207.203: icmp_seq=71 ttl=62 time=76.9 ms
64 bytes from 10.0.207.203: icmp_seq=72 ttl=62 time=77.6 ms
64 bytes from 10.0.207.203: icmp_seq=73 ttl=62 time=77.3 ms
64 bytes from 10.0.207.203: icmp_seq=74 ttl=62 time=77.0 ms
64 bytes from 10.0.207.203: icmp_seq=75 ttl=62 time=77.1 ms
64 bytes from 10.0.207.203: icmp_seq=76 ttl=62 time=76.9 ms
^C
--- 10.0.207.203 ping statistics ---
76 packets transmitted, 22 received, 71% packet loss, time 76043ms
rtt min/avg/max/mdev = 76.929/77.259/77.671/0.255 ms
[ec2-user@ip-172-31-17-235 ~]$
podemos ver que los paquetes entre el 15 y el 69 se perdieron por la caída del endpoint. Pero a su vez sin intervención alguna la conexión se reestableció exitosamente!!!
Overhead de la VPN
Nada es perfecto ni gratis en este mundo, y las VPNs no son la excepci'on. Por lo tanto, es esperable que el uso de la VPN venga con un costo asociado.
En pequeñas transferencias no para ser perceptible:
- Ping entre los endpoints sin utilizar el tunel:
PING 52.23.178.X (52.23.178.X) 56(84) bytes of data.
64 bytes from 52.23.178.X: icmp_seq=1 ttl=52 time=76.0 ms
64 bytes from 52.23.178.X: icmp_seq=2 ttl=52 time=76.3 ms
64 bytes from 52.23.178.X: icmp_seq=3 ttl=52 time=76.1 ms
64 bytes from 52.23.178.X: icmp_seq=4 ttl=52 time=76.1 ms
64 bytes from 52.23.178.X: icmp_seq=5 ttl=52 time=76.0 ms
--- 52.23.178.X ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 76.021/76.164/76.349/0.206 ms
root@ip-172-31-16-163:/etc/ipsec.d#
- Ping entre los endpoints utilizando el tunel:
PING 10.0.61.63 (10.0.61.63) 56(84) bytes of data.
64 bytes from 10.0.61.63: icmp_seq=1 ttl=64 time=76.1 ms
64 bytes from 10.0.61.63: icmp_seq=2 ttl=64 time=76.1 ms
64 bytes from 10.0.61.63: icmp_seq=3 ttl=64 time=76.2 ms
64 bytes from 10.0.61.63: icmp_seq=4 ttl=64 time=76.0 ms
64 bytes from 10.0.61.63: icmp_seq=5 ttl=64 time=76.1 ms
--- 10.0.61.63 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4005ms
rtt min/avg/max/mdev = 76.086/76.166/76.245/0.053 ms
root@ip-172-31-16-163:/etc/ipsec.d#
Con tráfico un poco mas denso la diferencia comienza a notarse, en este caso hay 6 segundos de diferencia en una transferencia de 500 MBytes:
root@ip-172-31-16-163:/home/ubuntu# time nc 10.0.61.63 80 < 500MB_file
real 0m44.957s
user 0m0.013s
sys 0m1.591s
root@ip-172-31-16-163:/home/ubuntu# time nc 52.23.178.X 80 < 500MB_file
real 0m38.693s
user 0m0.004s
sys 0m0.429s
root@ip-172-31-16-163:/home/ubuntu#
Conclusión
Hoy por hoy con soluciones Open Source como OpenSwan no hay motivo alguno para que no utilicemos VPNs para asegurar nuestra información en tránsito. Es cierto que en algunos escenarios las VPNs con SSL pueden ser mas sencillas de desplegar, pero IPSec tarde o temprano las va a reemplazar, así que gente... ponganse a probar OpenSwan!!!