VPN con WireGuard

VPN con WireGuard
Ruto Neón - Sana Sana

Hola de nuevo, antes de pasar a comentar la generación de la infraestructura que tengo montada en la red local de casa mediante Ansible, voy a comentar algo que tenía en mente desde hace tiempo, la utilización de WireGuard para acceder a esa red desde cualquier sitio.

La portada creo que no dejará indiferente a nadie, pero como explicaré al final, voy a intentar renovar un poco las recomendaciones musicales y estos murcianos, sobre todo esta versión, me ha sorprendido bastante, para bien, por supuesto.

Volviendo al tema que nos ocupa, tengo que recordar que durante mi etapa en Mirai descubrí Softether, un software de VPN de origen japonés que para su época estaba muy avanzado, tenía uno de los mejores rendimientos y era muy versátil. En casa tengo un contenedor montado con él pero más por el DNS con IP dinámica que me proporciona que como VPN, y como hace bastantes versiones de kernel que WireGuard ya está disponible he decidido darle una oportunidad a esta nueva tecnología.

Como suele ser habitual en estos proyectos de software libre tenemos abundante documentación. Aparte de la oficial, también he encontrado algún que otro post con información interesante. En éste, por ejemplo, tenemos un resumen de las arquitecturas posibles que se pueden configurar. A nosotros nos interesa la que llaman road warrior, un cliente, o clientes, que se conectan a un servidor de WireGuard dentro de una red local y tienen acceso directo a todos sus recursos. Prácticamente nos hemos basado en este post para generar esta guía.

La idea es poder acceder a la red local de casa mediante un cliente en un ordenador portátil o incluso móvil. Para ello se necesita configurar un servidor en la red local que sirva como gateway.

WireGuard: Road Warrior

Primero debemos permitir que el puerto 51820, pero sólo el protocolo UDP, pueda ser accedido desde internet en dirección al contenedor que albergará el gateway. Si estamos usando contenedores LXD debemos permitir el forwarding de ipv4 en el propio contenedor. Para ello debemos tocar su configuración:

$ lxc config set wireguard linux.sysctl.net.ipv4.ip_forward 1
$ lxc config get wireguard linux.sysctl.net.ipv4.ip_forward
1

Si obtenemos ese 1 como resultado es que el cambio ha tenido efecto, pero debemos reiniciar el contenedor y desde dentro del mismo podremos comprobar que el cambio permanece tras los reinicios:

$ sysctl -a | grep ip_forward
[...]
net.ipv4.ip_forward = 1
[...]

Para generar las claves de encriptación debemos tener instalados un par de paquetes, wireguard y wireguard-tools (en Debian y derivados):

# apt install wireguard wireguard-tools

Y la generación se realiza mediante los siguientes comandos:

$ umask 077
$ wg genkey | tee server-private.key | wg pubkey > server-public.key
$ sudo chown root:root server-p*
$ sudo mv server-p* /etc/wireguard/
$ sudo ls -lkp /etc/wireguard/
total 1
-rw------- 1 root root 45 Jul 31 08:27 server-private.key
-rw-—— 1 root root 45 Jul 31 08:27 server-public.key

El contenido de los certificados son simplemente unas cadenas de texto que luego utilizaremos en los ficheros de configuración. También debemos realizar la misma operación en los clientes que queramos configurar:

$ umask 077
$ wg genkey | tee client-private.key | wg pubkey > client-public.key
$ sudo chown root:root server-p*
$ sudo mv server-p* /etc/wireguard/

El fichero de configuración del servidor sería éste:

[Interface]
Address = 10.23.5.1/24
ListenPort = 51820
PrivateKey = [To-fill with content of server-private.key]
PreUp = iptables -t nat -A POSTROUTING -s 10.23.5.0/24 -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -s 10.23.5.0/24 -o eth0 -j MASQUERADE

# Client
[Peer]
PublicKey = [To-fill with content of client-public.key]
AllowedIPs = 10.23.5.2/32

# Other client
[Peer]
PublicKey = [To-fill with content of other-client-public.key]
AllowedIPs = 10.23.5.3/32

Este archivo tiene dos partes bien diferenciadas. En la primera, Interface, definimos la correspondiente a la configuración de red del propio servidor. La primera línea define la ip del servidor y la segunda el puerto en el que escucha, el mismo que hemos abierto en el router y apuntado a la ip del servidor que estamos configurando. Los comandos de iptables nos sirven para poder redirigir el tráfico desde la VPN a la red local y así no tenemos que ejecutar las reglas cada vez que levantemos el servicio de WireGuard. Nos quedaría pendiente actualizar este comando a nftables, que ya va siendo hora de familiarizarse con su sintaxis.

La parte de los clientes es más sencilla. Lo primero es definir el bloque y consignar el contenido de la parte pública del certificado generado en el cliente. La parte de la ip tiene su miga aunque no se aprecie a simple vista. Según aclaran en el post del que hemos generado este procedimiento, aquí debemos definir la ip que queremos que tenga el cliente en la VPN. Evidentemente debe ser única por cada cliente. En caso de estar configurando escenarios de conexión de redes mediante esta VPN deberíamos definir la red de la otra parte, pero eso se escapa del alcance de este post.

Veamos la configuración del cliente. En nuestro caso vamos a definir un split tunnel, es decir, el tráfico del cliente seguirá funcionando igual pero las conexiones con la red local de casa se enrutarán a través de la VPN. Para conseguir esto debemos definir un fichero de configuración como éste:

[Interface]
PrivateKey = [To-fill with content of client-private.key]
Address = 10.23.5.2/24
DNS = [DNS red local]

[Peer]
PublicKey = [To-fill with content of server-public.key]
Endpoint = home-server.org:51820
AllowedIPs = 10.23.5.0/24, 192.168.1.0/24

La parte de Interface es similar a la del servidor con la diferencia de que se define un DNS que en este caso debe ser el que tenemos configurado en la red local. He probado con DNSs públicos y la navegación del cliente fallaba con split tunnel. Después simplemente hay que añadir los datos de conexión al servidor en la parte Peer. La public key y el endpoint especifican el método de conexión con el servidor y la variable AllowedIPs le dice al cliente cómo tienen que comportarse las conexiones a través de la VPN. Aquí es donde se define el split tunnel, ya que únicamente las conexiones a la red de la VPN y a las de la red local remota se dirigirán por este canal de datos. En caso de que quisiéramos enrutar todo el tráfico por la VPN, por si estuviéramos en una WiFi pública, deberíamos cambiar el valor de esta variable a 0.0.0.0/0.

Con las configuraciones preparadas pasamos a activar el servicio en el servidor mediante systemd:

$ sudo systemctl status wg-quick@wg0
○ [email protected] - WireGuard via wg-quick(8) for wg0
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; preset: enabled)
Active: inactive (dead)
Docs: man:wg-quick(8)
man:wg(8)
[...]

Como vemos, la unidad que nos define el paquete wg-quick se completa mediante el nombre del fichero de configuración que hemos definido. Lo lanzamos y vemos si todo funciona correctamente, incluida la interfaz que debe crearse, wg0:

$ sudo systemctl start wg-quick@wg0
$ sudo systemctl status wg-quick@wg0
● [email protected] - WireGuard via wg-quick(8) for wg0
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; preset: enabled)
Active: active (exited) since Thu 2024-08-01 08:03:09 CEST; 15s ago
Docs: man:wg-quick(8)
man:wg(8)
https://www.wireguard.com/
https://www.wireguard.com/quickstart/
https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
Process: 766 ExecStart=/usr/bin/wg-quick up wg0 (code=exited, status=0/SUCCESS)
Main PID: 766 (code=exited, status=0/SUCCESS)
CPU: 59ms
Aug 01 08:03:09 wireguard systemd[1]: Starting [email protected] - WireGuard via wg-quick(8) for wg0...
Aug 01 08:03:09 wireguard wg-quick[766]: [#] iptables -t nat -A POSTROUTING -s 10.23.5.0/24 -o eth0 -j MASQUERADE
Aug 01 08:03:09 wireguard wg-quick[766]: [#] ip link add wg0 type wireguard
Aug 01 08:03:09 wireguard wg-quick[766]: [#] wg setconf wg0 /dev/fd/63
Aug 01 08:03:09 wireguard wg-quick[766]: [#] ip -4 address add 10.23.5.1/24 dev wg0
Aug 01 08:03:09 wireguard wg-quick[766]: [#] ip link set mtu 1420 up dev wg0
Aug 01 08:03:09 wireguard systemd[1]: Finished [email protected] - WireGuard via wg-quick(8) for wg0.
$ ip a
[...]
2: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.23.5.1/24 scope global wg0
valid_lft forever preferred_lft forever

También podemos comprobar el estado de las rutas y utilizar el comando propio de WireGuard para mostrar los datos actuales de la VPN:

$ sudo ip r
default via 192.168.1.1 dev eth0 proto dhcp src 192.168.1.57 metric 100
10.23.5.0/24 dev wg0 proto kernel scope link src 10.23.5.1
192.168.1.0/24 dev eth0 proto kernel scope link src 192.168.1.57 metric 100
$ sudo wg show
interface: wg0
public key: [Public Server Key]
private key: (hidden)
listening port: 51820
peer: [Public Client Key
allowed ips: 10.23.5.2/32

Si vemos que no hay ningún problema con la configuración podemos activar el servicio para que siempre arranque junto con el inicio del sistema:

$ sudo systemctl enable wg-quick@wg0
Created symlink /etc/systemd/system/multi-user.target.wants/[email protected] → /usr/lib/systemd/system/[email protected].

Para el cliente nos hemos encontrado con un problema al querer definir la resolución de nombres, parece ser que eliminándola no hay ningún problema pero en entornos como una WiFi pública no es deseable, por lo que necesitaríamos poder configurar nuestro DNS de la red local. La solución pasa por instalar el paquete systemd-resolved:

# apt install systemd-resolved

Y ya podemos lanzar la VPN sin problemas:

systemctl start wg-quick@client
systemctl status wg-quick@client
● [email protected] - WireGuard via wg-quick(8) for client
Loaded: loaded (/usr/lib/systemd/system/[email protected]; disabled; preset: enabled)
Active: active (exited) since Thu 2024-08-01 08:18:53 CEST; 3min 21s ago
Invocation: c8711db0d8e44132a63ce6dbc06fc0e1
Docs: man:wg-quick(8)
man:wg(8)
https://www.wireguard.com/
https://www.wireguard.com/quickstart/
https://git.zx2c4.com/wireguard-tools/about/src/man/wg-quick.8
https://git.zx2c4.com/wireguard-tools/about/src/man/wg.8
Process: 63209 ExecStart=/usr/bin/wg-quick up client (code=exited, status=0/SUCCESS)
Main PID: 63209 (code=exited, status=0/SUCCESS)
Mem peak: 5.6M
CPU: 79ms
ago 01 08:18:52 erika systemd[1]: Starting [email protected] - WireGuard via wg-quick(8) for client...
ago 01 08:18:52 erika wg-quick[63209]: [#] ip link add client type wireguard
ago 01 08:18:52 erika wg-quick[63209]: [#] wg setconf client /dev/fd/63
ago 01 08:18:52 erika wg-quick[63209]: [#] ip -4 address add 10.23.5.2/24 dev client
ago 01 08:18:52 erika wg-quick[63209]: [#] ip link set mtu 1420 up dev client
ago 01 08:18:52 erika wg-quick[63241]: [#] resolvconf -a client -m 0 -x

El servicio no lo activamos por defecto ya que no siempre vamos a trabajar con la VPN, siempre que queramos utilizarla debemos invocar el comando de inicio y podremos pararlo mediante systemd o cuando apaguemos el ordenador.

Otro método aún más sencillo de manejar pasa por utilizar NetworkManager, y en su defecto, cualquier entorno de escritorio de linux moderno. Para disponer de la configuración podemos importarla directamente con el CLI de NetworkManager:

$ nmcli connection import type wireguard file wg0.conf
Conexión «wg0» (50ea40f2-417c-4766-93bb-b1379a9b5daa) añadida con éxito.
$ nmcli connection modify 'wg0' connection.autoconnect no

Si no renombramos el fichero de configuración original con el nombre de la interfaz el comando nos dará error, pero si todo ha funcionado correctamente, a partir de ese momento podremos activar la VPN cómodamente desde nuestro escritorio favorito, como ya sabréis yo siempre he preferido Gnome, pero para gustos los colores. El segundo comando es para evitar que la VPN se lance al hacer login en el sistema porque también nos ha dado algún problema. La configuración importada sería algo así:

GNOME NetworkManager Configuration WireGuard

Esta última configuración así como una explicación más extensa de las posibilidades de WireGuard en NetworkManager la tenéis en este post.

Cuando suba mi infraestructura local a Git, generada con Ansible, podréis tener vuestro propio DNS en casa también para no depender de DNSs en ubicaciones WiFi/Públicas sospechosas. Para activar la VPN, si habéis seguido todo el post al pie de la letra, tendréis dos posibilidades, vía systemd o configuraciones VPN en vuestro escritorio. Si no queréis una de las dos simplemente podréis borrar una, yo voy a dejar las dos por la comodidad de consultar la configuración original en texto plano, pero visto lo sencillo que es configurar la VPN desde el escritorio no creo que mantenga la vía systemd por mucho tiempo.

Y como os he adelantado al principio, para este nuevo post he elegido una canción muy actual, aún siendo una parodia/homenaje de otra no mucho más antigua, con ustedes otros murcianos llamados Ruto Neón y su versión del Sana Sana de Naty Peluso. Creo que voy a intentar mezclar mis temas clásicos con la recuperación que intento hacer de Radio 3 y de la música en general porque he tenido épocas en las que no tenía en cuenta la actualidad musical y se están haciendo muy buenas cosas mezclándolo todo, géneros, cantantes y productores intercambiándose los roles, gente consagrada con músicos emergentes, etc.

Por cierto, respecto a la música que recomiendo, como habréis notado, no pongo ningún enlace ya que no quiero obligar a pinchar en una plataforma de streaming que a mí me parezca mejor que otra, copiando los datos de la canción en vuestro servicio favorito seguro que las encontráis fácilmente, y si no es el caso, seguro que en cualquier otro que os indique vuestro buscador, ése que seguro respeta vuestra privacidad, podréis escucharla perfectamente.

Disfrutad!