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

Tabla de Contenidos

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, disponer de una IP estática, conectarte desde redes inseguras o tener conectividad IPv6.

Una alternativa es usar el VPN en un dominio de enrutamiento alternativo.

El servidor VPN correrá en un VPS OpenBSD 7.0 con 1GB de RAM, aunque también se puede instalar en un servidor de 512MB de RAM desactivando KARL.

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

  • Abrir el puerto 51820 UDP en el servidor
  • 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 del cliente será 0.0.0.0/0 para conectar desde cualquier IPv4
  • La IP del túnel VPN del cliente será 10.0.0.2

Configuración inicial de Wireguard en el cliente

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 y genero la clave privada:

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

Como estoy detrás de un NAT uso 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 el directorio y genero la clave pública:

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

Este es el archivo /etc/hostname.wg0 donde he incluido la clave pública del cliente:

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).

Redirección del tráfico del cliente en el servidor

Activo el reenvío de IP en el servidor:

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

Esta es la línea correspondiente al archivo /etc/pf.conf que debe de ir arriba antes de block:

pass out quick on egress from wg0:network to any nat-to (egress)

Cargo la configuración de Packet Filter:

# pfctl -f /etc/pf.conf

Configurar el VPN como la ruta por defecto

Para conseguir esto tengo que modificar los archivos /etc/hostname.wg0 y /etc/hostname.em0 en el cliente, 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.

Resolución DNS y dominios de enrutado

Ahora ya no será accesible el servidor DNS de la red local ya que la conexión sale por defecto a través del VPN. Ahora habrá dos dominios de enrutado:

  • El dominio 0 se conecta a través de la VPN
  • El dominios 1 se conecta sin usar la VPN

Esto se puede hacer de varias formas:

Mediante resolv.conf

Simplemente pongo los dos servidores de red local y VPN en este archivo:

nameserver 192.168.10.1
nameserver 10.0.0.1

Con un par de instancias unwind

Para que funcione la resolución DNS para cada dominio de enrutado correré dos instancias del servidor de DNS local unwind(8).

Configuro lo1 para 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"

Levanto la interfaz:

# sh /etc/netstart lo1

Configuro el reenvío de consultas DNS usando 10.0.0.1 para el dominio de enrutado del VPN y 192.168.1.1 para el dominio de enrutado de la red local. Ambas IPs tienen un servidor DNS que responde a las consultas:

/etc/unwind.conf
forwarder { 10.0.0.1 }
/etc/unwind1.conf
forwarder { 192.168.1.1 }

Activo y arranco las dos instancias de unwind(8), una para lo0 y otra para lo1, y las activo y arranco usando rcctl(8):

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

Compruebo que funciona para ambos dominios:

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

La instancia de unwind correspondiente al dominio 1 reenvía las peticiones al servidor DNS de la red local 192.168.1.1 y la instancia correspondiente al dominio 0 reenvía las peticiones a 10.0.0.1, el VPN funcionando con unbound-adblock.

Si prefieres usar los servidores asignados al servidor se puede ejecutar en local un script awk(1) que extraiga dichos servidores del servidor y los incorpore al archivo de configuración de unwind. En este ejemplo también añado la línea correspondiente a unbound-adblock, que corre en local configurado para unwind:

#!/bin/sh
scp vpn:/etc/resolv.conf /tmp/vpn-resolv.conf
echo 'block list "/var/db/unwind-adblock.db" log' > /etc/unwind.conf
cat /tmp/vpn-resolv.conf | awk 'NR<=3 {printf "fwd"; printf ++counter ; printf " = \""; printf $2; printf "\"\n"}' >> /etc/unwind.conf
echo 'forwarder { $fwd1 $fwd2 $fwd3 }' >> /etc/unwind.conf
doas /usr/sbin/unwindctl reload

Esta sería la línea correspondiente al final del archivo doas.conf(5):

permit nopass :wheel cmd /usr/sbin/unwindctl args reload

Una alternativa más sencilla sin unwind

Si el router local con dirección IP 192.168.1.1 resuelve DNS y su firewall está redirigiendo las peticiones DNS a sí mismo de esta forma y tal y como explico en el artículo unbound-adblock es el firewall DNS definitivo:

adblock = "192.168.1.1"
pass in quick proto { tcp, udp } to any port { domain, domain-s } rdr-to $adblock

En ese caso, partiendo de que el servidor VPN tiene también configurado unbound-adblock, sería posible usar la dirección 10.0.0.1 en el archivo /etc/resolv.conf de forma que en el dominio de enrutado 0 (a través del VPN) la resolución DNS la haría el servidor VPN, y en el dominio de enrutado 1 esta petición sería redirigida al router local.

Añadir 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

Más información en la sección wgpsk de ifconfig(8).

Finalmente las configuraciones de wg0 en ambos puntos usando wgpsk quedarían así:

/etc/hostname.wg0 en el servidor
wgkey `cat /etc/wireguard/$(hostname).key` wgport 51820
wgpeer <clave-publica-cliente> wgaip 10.0.0.0/8 wgpsk <clave-compartida-psk>
inet 10.0.0.1/8
up
/etc/hostname.wg0 en el cliente
wgkey `cat /etc/wireguard/$(hostname).key`
wgpeer <clave-publica-servidor> wgendpoint 203.0.113.1 51820 wgaip 0.0.0.0/0 wgpka 20 wgpsk <clave-compartida-psk>
inet 10.0.0.2/8
wgrtable 1
up
!route add -net default 10.0.0.1

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 ungoogled-chromium

Soporte IPv6

Destacado

Contacto

Si has encontrado algún error o quieres comentarme algo mándame un correo a webmaster@adminlibre.org