31 KiB
weight | title | date | draft | summary | categories | tags | ||||
---|---|---|---|---|---|---|---|---|---|---|
4 | Apuntes de WireGuard | 2021-04-18T22:20:51+0200 | false | Apuntes de WireGuard |
|
|
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
- WireGuard on Ubuntu
- Set up WireGuard on Alpine Linux
- Firewall rules for WireGuard
- Debian 10 set up WireGuard VPN server
- Setting up a private mesh VPN with WireGuard
- Wireguard network managers
{{< /admonition >}}
Las implementaciones tradicionales de VPN (Virtual Private Network) 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:
- Veamos, este paquete es para
192.168.30.8
. ¿Qué peer es este? Vale, es para el peerpepito
. 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 - Cifra el paquete con la clave pública del peer
pepito
- ¿Qué extremo remoto corresponde a este peer? Vale, es el puerto UDP 53133 en 216.58.211.110
- Procedo a enviar los bytes cifrados al extremo remoto via UDP
En el extremo remoto, el peer pepito
que recibe el paquete:
- Me ha llegado un paquete del puerto
7361
de98.139.183.24
. Voy a descifrarlo. - 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 paramanolito
es el98.139.183.24:7361/UDP
- El paquete descifrado viene de
192.168.43.89
si el peermanolito
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):
[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):
[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.
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
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:
[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)
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: <POINTOPOINT,NOARP,UP,LOWER_UP> 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:
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
:
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
-
Creamos la máquina virtual, basada en la ISO de Alpine Linux, asignamos un disco duro de 16Gb
-
Ejecutamos
alpine-setup
asignamos IP estática y el tipo de instalaciónsys
-
Instalamos
qemu-guest-agent
y configuramos adecuadamente -
Instalamos
sudo
y habilitamos el grupowheel
(editando/etc/sudoers
) -
Añadimos usuario
wgadmin
(ya de paso lo metemos enwheel
) -
Instalamos UFW
-
Instalamos el WireGuard
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
cd /etc/wireguard
umask 077; wg genkey | tee privatekey | wg pubkey > publickey
Creamos el fichero de configuración wg0.conf
[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)