--- weight: 4 title: "Apuntes de WireGuard" date: 2021-04-18T22:20:51+0200 draft: false summary: "Apuntes de WireGuard" categories: - notes tags: - wireguard - debian - alpine --- Apuntes sobre WireGuard. Una herramienta para implementar redes VPN {{< admonition type=warning title="Work in progress" open=true >}} Estos apuntes no están completos, (ni de lejos) {{< /admonition >}} # WireGuard {{< admonition type=abstract title="Referencias" open=false >}} - [WireGuard: Conceptual Overview](https://www.wireguard.com/#conceptual-overview) - [WireGuard on Ubuntu](https://www.xmodulo.com/wireguard-vpn-server-ubuntu.html) - [Set up WireGuard on Alpine Linux](https://www.cyberciti.biz/faq/how-to-set-up-wireguard-vpn-server-on-alpine-linux/) - [Firewall rules for WireGuard](https://www.cyberciti.biz/faq/how-to-set-up-wireguard-firewall-rules-in-linux/) - [Debian 10 set up WireGuard VPN server](https://www.cyberciti.biz/faq/debian-10-set-up-wireguard-vpn-server/) - [Setting up a private mesh VPN with WireGuard](https://www.scaleway.com/en/docs/wireguard-mesh-network/) - [Wireguard network managers](https://kaspars.net/blog/wireguard-managers) - [wg-gen-web](https://github.com/vx3r/wg-gen-web) {{< /admonition >}} Las implementaciones tradicionales de VPN (_**V**irtual **P**rivate **N**etwork_) más utilizadas en la industria (como _IPsec_ por ejemplo) se implementan dentro del kernel; insertando una capa extra, que se encarga del cifrado y el encapsulado, entre la pila IP y el interfaz de red. Esto se hace así por que se quiere que sea un proceso tan rápido como sea posible que no penalice la eficiencia de las comunicaciones. A cambio es necesario implementar por separado el _plano de tráfico_ (la capa que hemos mencionado dentro del kernel) y _el plano de control_ que se crea en el espacio de usuario. Esta arquitectura hace que tanto la implementación (el software) como la gestión de estas soluciones sean complejas. En las implementaciones de VPN en el espacio de usuario, como por ejemplo _OpenVPN_, todo el proceso de cifrado lo hace un demonio en el espacio de usuario. Con esta arquitectura inevitablemente se penaliza el rendimiento, por qué los paquetes tienen que pasar del espacio kernel al espacio de usuario y viceversa varias veces. Aunque esto no impide que sean soluciones muy populares debido a su facilidad de uso. _WireGuard_ es una alternativa relativamente reciente a _OpenVPN_ que puede ser muy interesante. Por un lado se implementa como un módulo del Kernel, y por otro mantiene la gestión y configuración a un nivel muy asequible por qué usa ficheros de configuración **muy sencillos** y se basa en la metáfora de _interfaz de red_, que se gestiona como cualquier otro interfaz del sistema con las herramientas estándar como `ip` o `ifconfig`. _WireGuard_ cifra y encapsula paquetes IP sobre UDP. Básicamente hay que añadir un interface _WireGuard_, configurarlo con tu clave privada y la clave pública de los pares (_peers_) con los que se va a comunicar y ya puedes enviar paquetes a través de ese túnel. En realidad es mucho más parecido a _SSH_ o _Mosh_ que a _OpenVPN_. Como hemos dicho, al usar _WireGuard_ se añadirán uno (o varios) interfaces de red de tipo _WireGuard_ (wg0, wg1, ....) a tu máquina. Estos interfaces de red se pueden configurar normalmente con las herramientas del sistema, igual que un `eth0` o `wlan0`. Los aspectos específicos del _WireGuard Interface_ se configuran con la herramienta `wg`. Los interfaces _WireGuard_ se comportan como un túnel cifrado. _WireGuard_ asocia las direcciones IP de los túneles con los extremos remotos y sus claves públicas. Cuando el interfaz envía un paquete a un _peer_ hace lo siguiente: 1. Veamos, este paquete es para `192.168.30.8`. ¿Qué _peer_ es este? Vale, es para el _peer_ `pepito`. Si no soy capaz de determinar a que _peer_ esta dirigido descartaré el paquete (_drop_) **¡OJO!** WireGuard determina el _peer_ a partir de la IP destino 2. Cifra el paquete con la clave pública del _peer_ `pepito` 3. ¿Qué extremo remoto corresponde a este _peer_? Vale, es el puerto UDP 53133 en 216.58.211.110 4. Procedo a enviar los bytes cifrados al extremo remoto via UDP En el extremo remoto, el _peer_ `pepito`que recibe el paquete: 1. Me ha llegado un paquete del puerto `7361` de `98.139.183.24`. Voy a descifrarlo. 2. Descifro y además compruebo que se garantiza la autenticidad para el peer `manolito`. Ya que estamos actualizo mi tabla de direcciones: Apunto que el destino más reciente para `manolito` es el `98.139.183.24:7361/UDP` 3. El paquete descifrado viene de `192.168.43.89` si el _peer_ `manolito` está autorizado a enviar paquetes con esa IP origen lo aceptamos, de lo contrario lo descartamos (_drop_) El concepto fundamental en el que se base _WireGuard_ es el ___Cryptokey Routing___, consiste en almacenar una lista de claves públicas junto con la lista de direcciones IP origen que permitimos, ambas asociadas al _peer_ al que corresponde esa clave. Así: - Cada interfaz de red tiene una clave privada - Cada interfaz de red tiene una lista de _peers_ - Cada _peer_ tiene una clave pública - Para cada _peer_ se especifican las direcciones IP origen autorizadas Un _servidor_ de _WireGuard_ podría tener una configuración como esta (no está completa es una configuración simplificada): ```toml [Interface] PrivateKey = yAnz5TF+lXXJte14tji3zlMNq+hd2rYUIgJBgB3fBmk= ListenPort = 51820 [Peer] PublicKey = xTIBA5rboUvnH4htodjb6e697QjLERt1NAB4mZqp8Dg= AllowedIPs = 10.192.122.3/32, 10.192.124.1/24 [Peer] PublicKey = TrMvSoP4jYQlY6RIzBgbssQqY3vxI2Pi+y71lOWWXX0= AllowedIPs = 10.192.122.4/32, 192.168.0.0/16 [Peer] PublicKey = gN65BkIKy1eCE9pP1wdc8ROUtkHLF2PfAqYdyYBz6EA= AllowedIPs = 10.10.10.230/32 ``` Un _cliente_ puede tener una configuración más simple (no está completa): ```toml [Interface] PrivateKey = gI6EdUSYvn8ugXOt8QQD6Yc+JyiZxIhp3GInSWRfWGE= ListenPort = 21841 [Peer] PublicKey = HIgo9xNzJMWLKASShiTqIybxZ0U3wGLiUeJ1PKf8ykw= Endpoint = 192.95.5.69:51820 AllowedIPs = 0.0.0.0/0 ``` Tanto el _"servidor"_ como el _"cliente"_ tienen un interfaz de red (al menos) con su clave privada asociada. Pero no te lies con los términos servidor y cliente, nada impide que configures varios interfaces de tu VPN como servidores. El _servidor_ tiene también una lista de _peer_; cada uno con su lista `AllowedIPs`: la lista de direcciones IP origen que están autorizadas para ese _peer_ La configuración mínima para un cliente solo necesita tener como _peer_ al servidor. Se almacena una dirección ___inicial___ del servidor. Deliberadamente no se mantienen direcciones estáticas para los _peer_, los _peer_ se identifican al descifrar un paquete recibido. Y se mantiene una tabla de direcciones más recientes para cada _peer_ que se actualiza con cada mensaje recibido. De esta forma los _peer_ podrían hacer _roaming_ por distintas redes. Cuando el servidor recibe un paquete comprueba, una vez descifrado, que el _peer_ puede enviar paquetes con ese origen, en caso que no sea así lo descarta. Cuando el servidor quiere enviar un paquete comprueba las redes asociadas a cada _peer_ para decidir a quien enviarlo, y lo envía a la dirección IP más reciente que tenga registrada para ese _peer_. En el cliente (con la configuración de ejemplo) se aceptan paquetes de cualquier origen si provienen del _servidor_. También se envian todos los paquetes, sea cual sea la dirección de destino hacia el _servidor_. Como hemos dicho, los paquetes se envían a la dirección registrada más recientemente para el servidor, si el cliente ya ha recibido algún paquete, o a la dirección inicial especificada en la configuración si aún no ha recibido nada desde el _servidor_. ## Instalación de _WireGuard_ ### Instalación de WireGuard en Debian Server 10 Añadimos `backports` a nuestros orígenes de software e instalamos _WireGuard_. ```bash echo 'deb http://ftp.debian.org/debian buster-backports main' | sudo tee /etc/apt/sources.list.d/buster-backports.list apt update apt install wireguard ``` _WireGuard_ se ejecuta como un **módulo del kernel** Genemos las claves en `/etc/wireguard` ```bash umask 077 wg genkey | sudo tee /etc/wireguard/privatekey | wg pubkey | sudo tee /etc/wireguard/publickey ``` Editamos el fichero `sudo nano /etc/wireguard/wg0.conf` con el contenido: ```toml [Interface] Address = 10.0.0.1/24 SaveConfig = true ListenPort = 51820 PrivateKey = SERVER_PRIVATE_KEY PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens3 -j MASQUERADE PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens3 -j MASQUERADE ``` El fichero de configuración no tiene por que ser accesible para nadie (`chmod 600 /etc/wireguard/wg0.conf`), al fin y al cabo contiene la clave privada. Este fichero de configuración correspondería a un "servidor" de VPN clásico para dar acceso a internet a los "clientes" de la VPN via NAT. Los campos del fichero de configuración: **Address** : La dirección IP **privada** (una dirección interna de la VPN) asignada al interfaz. Asignamos IP y máscara en un solo comando. **SaveConfig** : Si vale `true`la configuración del interfaz se salva en el fichero de configuración cada vez que hacemos un shutdown del interfaz **ListenPort** : El puerto UDP que escucha nuestro interfaz, los _peer_ (clientes) tienen que dirigirse a ese puerto **PrivateKey** : La clave de cifrado para este interfaz **PostUp** : Un comando o un script que se tiene que ejecutar **antes** de levantar el interfaz. En este ejemplo estamos habilitando _IP masquerading_. Esto permitirá que el tráfico salga del servidor dando a los clientes de la VPN acceso a internet (habría que sustituir `ens3` por el verdadero nombre del interfaz público del servidor) **PostDown** : Un comando o script que se tiene que ejecutar **antes** de bajar el interface. En nuestro ejemplo se borran la configuración de _IP masquerading_ de las IPtables. Es imprescindible sustituir `ens3` por el puerto real del servidor. Podemos arrancar ya el interfaz `wg0` con el comando `wg-quick up wg0` (en mi caso necesité reiniciar el servidor previamente, supongo que para completar la instalación del módulo wireguard en el kernel) ```bash root@vmi504132:~# wg-quick up wg0 [#] ip link add wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.0.0.1/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE root@vmi504132:~# wg show wg0 interface: wg0 public key: rgbUE5KCfrUWT/7Vhh7NTdosCyP9LGx7M5vnJU9EAxw= private key: (hidden) listening port: 51820 root@vmi504132:~# ip addr show wg0 24: wg0: mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000 link/none inet 10.0.0.1/24 scope global wg0 valid_lft forever preferred_lft forever ``` Hasta aquí hemos configurado nuestro nuevo interfaz `wg0` pero necesitamos completar un par de cosas más antes de terminar: #### Abrir el cortafuegos para WireGuard Tenemos que permitir tráfico entrante UDP en el puerto `51820`, en mi caso estoy usando UFW en el servidor: ```bash ufw allow 51820/udp ufw enable ufw status numbered ufw status verbose ``` #### Habilitar _ip forwarding_ Editamos el fichero `/etc/sysctl.conf` y descomentamos la linea: `net.ipv4.ip_forward=1` Recargamos el fichero ejecutando `sysctl -p /etc/sysctl.conf` Evidentemente si queremos usar IPV6 tenemos que descomentar la linea correspondiente. #### Configurar el arranque automático del interfaz cuando arranca el servidor Para dejar configurado el arranque automático del interfaz `wg0`: ```bash wg-quick down wg0 # Paramos el interfaz que hemos levantado manualmente systemctl enable wg-quick@wg0 # Dejamos el servicio habilitado systemctl status wg-quick@wg0 # Comprobamos el status del servicio # systemctl start wg-quick@wg0 # Comandos para arrancar o parar el servicio # systemctl stop wg-quick@wg0 # manualmente ``` ### Creación de una máquina virtual en Proxmox con Alpine Linux y WireGuard Creamos la máquina virtual igual que creamos la de Docker 1. Creamos la máquina virtual, basada en la ISO de _Alpine Linux_, asignamos un disco duro de 16Gb 2. Ejecutamos `alpine-setup` asignamos IP estática y el tipo de instalación `sys` 2. Instalamos `qemu-guest-agent` y configuramos adecuadamente 3. Instalamos `sudo` y habilitamos el grupo `wheel` (editando `/etc/sudoers`) 3. Añadimos usuario `wgadmin` (ya de paso lo metemos en `wheel`) 4. Instalamos UFW 5. Instalamos el WireGuard ```bash apk add sudo addgroup wgadmin adduser -G wgadmin wgadmin adduser wgadmin wheel apk add ufw ufw default deny incoming ufw default allow outgoing ufw allow ssh ufw enable apk add wireguard-tools ``` Ya tenemos todo listo para proceder con la configuración de _WireGuard_ ```bash cd /etc/wireguard umask 077; wg genkey | tee privatekey | wg pubkey > publickey ``` Creamos el fichero de configuración `wg0.conf` ```toml [Interface] Address = 10.0.0.2/24 ListenPort = 51820 PrivateKey = 2IHAoIOz+tWxRZ4oRlI1xHiD5FADVBTKOePnLSZNSkw= [Peer] PublicKey = d7TRyBXIX0I3sC/tPOqrIpNAGwEt2bW43QXKK4cEeGw= AllowedIPs = 0.0.0.0/0 Endpoint = 207.180.207.206:51820 PersistentKeepalive = 20 ``` Habilitamos el _IP Forwarding_: Por comando: `sysctl net.ipv4.ip_forward=1` O si lo queremos permanente añadimos la linea `net.ipv4.ip_forward=1` en el fichero `/etc/sysctl.conf` También tenemos que abrir el cortafuegos `ufw allow 51820/udp` Ya podemos levantar el interfaz con `wg-quick up wg0` Para que la cosa funcione tenemos que habilitar la redirección del puerto 51820/udp en nuestro router de internet para que apunte a la máquina virtual que hemos creado. Llegamos por ping a la maquina siriowg pero no más allá (faltan rutas de vuelta hacia el servidor)