Admin Libre - Administración de sistemas y redes

VPN road warrior con WireGuard en OpenBSD
Por Francisco Gaitán el 2 de Abril de 2022

Con esta configuración todo el tráfico del cliente, que se conecta desde cualquier IP, sale a internet a través de un servidor VPS con OpenBSD y WireGuard. Esta configuración es denominada road warrior y es especialmente útil si eres administrador de sistemas o si simplemente quieres librarte del secuestro de DNS por parte de tu ISP, tener una IP fija, conectarte tranquilamente desde conexiones inseguras o tener conectividad IPv6 desde un proveedor IPv4.

Usaré OpenBSD 7.0 para el servidor y OpenBSD 7.1-current para el cliente, aunque debe funcionar en cualquiera de estas versiones. Procuro mostrar las configuraciones todo lo sencillas y similares a la documentación oficial que me resulta posible, de forma que los cambios futuros que haya que hacer sean mínimos.

El servidor VPN correrá en un VPS OpenBSD 7.0 con 1GB de RAM. El consumo típico de RAM está entre 350MB y 1GB con un usuario.

Lectura recomendada

He seguido la guía de Solène Full WireGuard setup with OpenBSD por lo que no repetiré todo lo que dice, aunque alguna cosa la hago de forma distinta y he completado información que falta. La idea de guardar la clave privada en un archivo la vi en OpenBSD + Wireguard.

Pasos a dar

  • Crear un túnel VPN entre en cliente y el servidor
  • Configurar el servidor para que redirija el tráfico del cliente hacia internet
  • Hacer que la ruta por defecto en el cliente sea dicho túnel
  • Configurar el DNS para los dos dominios de enrutado

Crear un túnel VPN entre el cliente y el servidor

La red VPN tendrá las siguientes características:

  • La red VPN tendrá la dirección de red 10.0.0.0/8
  • La IP pública del servidor es 203.0.113.1
  • La IP del túnel VPN del servidor será 10.0.0.1
  • La IP pública del cliente no está definida pues es dinámica
  • La IP del túnel VPN del cliente será 10.0.0.2

Crearé el directorio /etc/wireguard tanto en el cliente como en el servidor donde irán las claves privadas de forma que no se vean al abrir el archivo hostname.wg0.

Configuración inicial de Wireguard en el cliente

Genero la clave privada y la guardo:

# install -m 0700 -o root -g wheel -d /etc/wireguard
# openssl rand -base64 32 > /etc/wireguard/$(hostname).key

Como estoy detrás de un NAT añadiré la opción wgpka 25 para establecer un intervalo de 25 segundos en el envío de paquetes keepalive. Este es el archivo /etc/hostname.wg0, aún sin la clave pública del servidor:

wgkey `cat /etc/wireguard/$(hostname).key`
wgpeer wgendpoint 203.0.113.1 51820 wgaip 0.0.0.0/0 wgpka 25  
inet 10.0.0.2/8
up

Ejecuto el comando # sh /etc/netstart wg0 y, aunque dará un error al no tener la clave pública del otro punto, ya puedo obtener la clave pública del cliente, que la usaré más tarde para configurar Wireguard en el servidor:

# sh /etc/netstart wg0
# ifconfig wg0 | grep wgpubkey
        wgpubkey <clave-publica-cliente>

Configuración de Wireguard en el servidor

Al igual que antes, creo la clave pública para el servidor:

# install -m 0700 -o root -g wheel -d /etc/wireguard
# openssl rand -base64 32 > /etc/wireguard/$(hostname).key

Este es el archivo /etc/hostname.wg0 donde he incluído la clave pública del cliente obtenida anteriormente:

wgkey `cat /etc/wireguard/$(hostname).key` wgport 51820
wgpeer <clave-publica-cliente> wgaip 10.0.0.0/8 
inet 10.0.0.1/8
up

Arranco la interfaz del túnel y obtengo la clave pública:

# sh /etc/netstart wg0 
# ifconfig wg0 | grep wgpubkey
        wgpubkey <clave-publica-servidor>

Configuración de Wireguard en el cliente

Ahora pongo esa clave pública del servidor en el archivo /etc/hostname.wg0 del cliente, de forma que queda así:

wgkey `cat /etc/wireguard/$(hostname).key`
wgpeer <clave-publica-servidor> wgendpoint 203.0.113.1 51820 wgaip 0.0.0.0/0 wgpka 20
inet 10.0.0.2/8
up

Cargo de nuevo la configuración:

# sh /etc/netstart wg0

En este punto se debe poder hacer ping desde el cliente a la IP del túnel del servidor (10.0.0.1). Si no funciona debe ser que hay algún firewall bloqueando el puerto 51820 (UDP). Si estás detrás de un router que hace NAT como es mi caso necesitarás una regla de redirección hacia la IP de tu ordenador, lo que queda fuera del alcance de este tutorial.

Configurar el servidor para que redirija el tráfico del cliente hacia internet

Activo el reenvío de IP en el servidor:

# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
# sysctl net.inet.ip.forwarding=1

Este es el archivo /etc/pf.conf con las modificaciones mínimas a la configuración por defecto. Aunque funciona, recomiendo usar en su lugar una configuración con denegación por defecto.

set skip on lo
pass out quick on egress from wg0:network to any nat-to (egress)
block return    # block stateless traffic
pass            # establish keep-state

Sin olvidar cargarlo:

# pfctl -f /etc/pf.conf

Hacer que la ruta por defecto en el cliente sea el VPN

Para conseguir esto tengo que modificar los archivos (del cliente) /etc/hostname.wg0 y /etc/hostname.em0, siendo em0 la interfaz con la que me conecto a Internet.

/etc/hostname.wg0
wgkey `cat /etc/wireguard/$(hostname).key`
wgpeer <clave-publica-servidor> wgendpoint 203.0.113.1 51820 wgaip 0.0.0.0/0 wgpka 20
inet 10.0.0.2/8
wgrtable 1
up
!route add -net default 10.0.0.1
/etc/hostname.em0
rdomain 1
up
autoconf

Tras reiniciar la red con # sh /etc/netstart el dominio de enrutado por defecto será el 0 que corresponde al VPN.

DNS local con unwind

Ahora ya no será accesible el servidor DNS local de nuestra red; esto se puede solucionar usando un servidor DNS local como unwind(8) (opcionalmente con unbound-adblock):

# rcctl enable unwind
# rcctl start unwind

Como hacer que funcione el DNS para ambos dominios de enrutado

Configura lo1 en el dominio de enrutado 1 (visto en daemonforums):

/etc/hostname.lo1
rdomain 1
inet 127.0.0.1 255.0.0.0
description "rdomain 1 loopback address"

Ahora necesitas dos instancias de unwind: una para el dominio 0 y otra para el dominio 1.

# ln -s /etc/rc.d/unwind /etc/rc.d/unwind1
# rcctl enable unwind unwind1
# rcctl set unwind1 rtable 1
# rcctl set unwind1 flags -s /var/run/unwind1.sock
# rcctl start unwind unwind1

Compruebo que funciona para ambos dominios:

$ route -T 0 exec dig +short example.com
93.184.216.34
$ route -T 1 exec dig +short example.com 
93.184.216.34

Para rematar he dado una configuración diferente a cada instancia de unwind: el correspondiente al dominio 1 reenvía las peticiones al servidor DNS de la red local, y el del dominio 0 está configurado con unbound-adblock y DoT (DNS sobre TLS).

Añadiendo una clave compartida psk

En la página de manual recomenda establecer una clave pre-compartida entre los pares para fortalecer el cifrado. Se trata de una clave de 32 bytes codificada en base64 que se puede generar con el siguiente comando:

$ openssl rand -base64 32

Como usar un programa fuera del VPN

Si quieres usar un programa sin que pase por el túnel puedes usar la función exec de route, de forma que el proceso que se ejecuta y todos sus hijos funcionan en el dominio de enrutado que se especifique. En este caso:

$ route -T 1 exec firefox 

Información adicional

Soporte IPv6

Si quieres añadir conectividad IPv6 a una conexión IPv4 echa un ojo a la entrada Conectividad IPv6 desde IPv4 con un VPS y NAT66.

Soporte para teléfonos

El artículo wireguard all the things explica como usar esta configuración para múltiples dispositivos, incluyendo teléfonos móviles.

Destacado

Misión

Admin Libre promueve el uso de software libre y de calidad tanto en los servidores como en el hardware de red con el objetivo de hacer los sistemas más seguros y auditables.