1 puntos por GN⁺ 2024-05-13 | 1 comentarios | Compartir por WhatsApp
  • Wag es un proyecto que agrega autenticación multifactor, restricción de rutas y registro de dispositivos a WireGuard, y permite distinguir entre rutas que requieren MFA y rutas públicas siempre accesibles
  • Ofrece una API para registrar clientes nuevos, alta disponibilidad, actualizaciones y notificaciones de usuarios en tiempo real, e integraciones de MFA como Security Key, SSO, PAM y TOTP
  • Para operar el servidor se requiere habilitar el reenvío de IP y, en ejecución manual, instalar iptables y libpam, además de ejecutarlo como root para gestionar iptables y dispositivos WireGuard
  • La administración puede hacerse desde la UI web y la CLI; la CLI incluye subcomandos start, registration, devices, users y webadmin para manejar tokens de registro, bloqueo de dispositivos, reinicio de MFA y cuentas de administrador web
  • Entre sus limitaciones, solo admite un AllowedIP por cliente y está orientado principalmente a Linux; Windows puede funcionar con algunos pasos adicionales

Funcionalidades que Wag agrega a WireGuard

  • Wag agrega MFA, restricción de rutas y registro de dispositivos a WireGuard
  • Las rutas pueden definirse separando las que requieren autenticación MFA de las rutas públicas que siempre están accesibles
  • Proporciona una API sencilla para registrar clientes nuevos
  • Soporta alta disponibilidad, actualizaciones y notificaciones de usuarios en tiempo real
  • Las integraciones de MFA incluyen los siguientes métodos
    • Security Key
    • SSO
    • PAM
    • TOTP
  • La documentación está disponible en Documentation

Requisitos de instalación y ejecución

  • Debe estar habilitado el reenvío en el servidor
    • Para IPv4 se usa la configuración net.ipv4.ip_forward=1
    • Para IPv6 se usan configuraciones sysctl relacionadas como net.ipv6.conf.all.forwarding=1
  • El ejemplo de ejecución con Docker Compose usa la imagen wagvpn/wag:latest
    • Un ejemplo de puerto para la página de administración es 4433/tcp
    • Un ejemplo de puerto para la página pública de registro es 8081/tcp
    • Un ejemplo de puerto de WireGuard es 53230/udp
    • El dispositivo /dev/net/tun se conecta al contenedor
  • La instalación manual requiere iptables y libpam
  • Wag debe ejecutarse como root para administrar iptables y los dispositivos WireGuard
  • Las versiones binarias requieren glibc 2.31+
  • Compilar desde el código fuente requiere go1.23.1 y npm

Formas de administración

  • Después de habilitar la UI de administración y configurar Wag, se crea el primer administrador y la contraseña se imprime en STDOUT
  • Luego se puede iniciar sesión en la UI web para administrar usuarios
  • El usuario root puede administrar el servidor Wag desde la CLI
  • El formato de la CLI es wag subcommand [-options]
  • Los subcomandos soportados son los siguientes
    • start: inicia el servidor Wag sin ejecutarlo como daemon
    • registration: maneja la creación, eliminación y listado de tokens de registro
    • devices: maneja el listado, eliminación, bloqueo, desbloqueo y consulta de sesiones MFA activas de dispositivos WireGuard
    • users: maneja la administración de MFA de usuarios, eliminación de usuarios, bloqueo de cuentas y reinicio de MFA
    • webadmin: maneja agregar, eliminar, listar, bloquear y desbloquear usuarios administradores de la UI web
    • version y firewall también están incluidos entre los comandos soportados

Flujo de tokens de registro y MFA

  • Para registrar un dispositivo nuevo, primero se genera un token de registro con un comando como wag registration -add -username tester
  • Si se entrega el token generado al endpoint público de registro, se puede obtener una respuesta con la configuración de WireGuard
  • La configuración devuelta incluye elementos como Interface, PrivateKey, Address, Peer, Endpoint, PublicKey, AllowedIPs y PersistentKeepAlive
  • El usuario se conecta a la dirección VPN del servidor e ingresa el código 2FA
  • El tiempo durante el cual la sesión permanece activa antes de expirar se define en el archivo de configuración

Consola de administración web

  • Para iniciar sesión en la consola de administración, Webserver.Management.Enabled debe estar configurado en true
  • En la consola se agrega una cuenta de administrador web con sudo ./wag webadmin -add -username <your_username> -password <your-password-here>
  • Después se accede a la dirección de escucha de administración e ingresan las credenciales
  • La propia interfaz web no puede agregar usuarios administradores
  • Se recomienda no exponer el portal de administración hacia el exterior; se recomienda configurar ListenAddress en 127.0.0.1 o localhost y exponerlo mediante reenvío SSH

Principales opciones de configuración

  • NumberProxies especifica cuántos proxies reversos confiables hay delante del cliente y permite que Wag interprete X-Forward-For para obtener la IP del cliente
  • Socket es el socket de control de Wag; al cambiarlo, se pueden ejecutar varias instancias de Wag en la misma máquina
  • NAT activa o desactiva el enmascaramiento; si está habilitado, todo el tráfico parecerá originarse en el servidor VPN
  • NATExcludeRanges especifica los rangos CIDR excluidos de NAT cuando NAT=true
  • ExposePorts expone puertos del servidor VPN a los clientes y agrega reglas de iptables
  • CheckUpdates está desactivado por defecto; si se activa, la UI de administración mostrará avisos de nuevas versiones de Wag y accederá a api.github.com
  • Acls define grupos y políticas, pero solo se aplica en la primera ejecución; durante la ejecución se edita desde la UI web
  • Webserver incluye la configuración del endpoint público de registro, el portal MFA del túnel y el portal de administración
  • Wireguard configura el nombre del dispositivo, el puerto de escucha, la clave privada, la subred a cargo de la VPN, el MTU y los servidores DNS
  • Clustering incluye la configuración del nombre del clúster, el estado del clúster etcd, el nivel de logs, nodos witness, ubicación de la base de datos y certificados del clúster

Comportamiento de las políticas ACL

  • Policies define las rutas que la VPN capturará y los puertos y protocolos que pasarán por Wag
  • La aplicación de reglas usa la longitud del prefijo de subred, y la coincidencia más específica determina el nivel de acceso a la ruta
  • Por ejemplo, si /16 se define como MFA y un /32 específico dentro de ese rango se define como Allow, ese /32 más específico tiene prioridad y permite acceso sin MFA
  • Este comportamiento cambió en v6.0.0; antes, las rutas MFA siempre tenían prioridad
  • Si se definen varias políticas para una misma ruta, las políticas se componen y las reglas MFA tienen prioridad
  • A partir de una versión que aún no ha sido liberada, las reglas Deny pueden bloquear el acceso a rutas
  • Como la regla más específica crea un nuevo “bucket” de reglas, si en el bucket /32 solo hay un deny, puede que tampoco se permita el acceso a otros puertos del mismo /32

Reglas de puertos y protocolos

  • El acceso a servicios puede definirse con reglas de puertos y protocolos
  • Hay 3 tipos de reglas soportadas
    • Any: si no hay una regla separada o se usa la palabra clave any, se permite cualquier combinación de servicio y puerto
    • Single Service: permite puertos TCP o UDP específicos de un host, como 192.168.1.1 22/tcp 53/udp
    • Ranges: permite definir rangos de puertos, como 192.168.1.1 22-1024/tcp 23-53/any
  • En los rangos de puertos, primero debe escribirse el puerto menor
  • ICMP no tiene puertos, así que puede especificarse sin puerto, como 1.1.1.1 icmp

Limitaciones y desarrollo

  • Wag solo soporta un AllowedIP por cliente
  • Esta limitación se adapta a una estructura de cliente a servidor
  • Está orientado principalmente a Linux y Windows puede funcionar con algunos pasos adicionales
  • En modo de desarrollo se puede usar una variable de entorno para establecer como IP del cliente la IP de las solicitudes que entran por el túnel
  • El ejemplo de prueba ejecuta sudo go test -v . en internal/router
  • Para contribuciones externas, se indica que al agregar funciones o corregir errores se escriban pruebas cuando sea posible y se abra un Pull Request

1 comentarios

 
GN⁺ 2024-05-13
Opiniones en Hacker News
  • Se ve bien, pero hay algunas cosas que me preocupan.
    Con el ejemplo curl [http://public.server.address:8080/register_device?key=e83253...](<http://public.server.address/register_device/…;) y la descripción de que “el servicio devuelve una respuesta completamente basada en plantillas”, parece que durante el proceso de registro el servidor genera la clave privada y se la envía al cliente, en vez de que el cliente cree la clave privada y envíe la clave pública al servidor.
    Además, como el ejemplo usa HTTP, sería bueno cambiar al menos esa parte para que la gente no piense que HTTP también es una opción aceptable.
    También me pregunto si el cliente tiene alguna forma de darse cuenta cuando la sesión expira. ¿O algo como una sesión SSH simplemente se queda colgado?
    A veces he buscado un cliente de WireGuard que funcione como la detección de portales cautivos en Wi-Fi. Idealmente sería algo como agregar una línea tipo persistentkeepalive al archivo de configuración para obtener una URL y revisarla periódicamente. Si llega OK, todo normal; si no hay respuesta, es un problema de red; si llega un encabezado Location, abrir el navegador en esa ubicación para reautenticar la sesión, etc.
    Todavía no he encontrado un cliente así.

    • La URL de registro también puede aceptar opcionalmente un parámetro pubkey, así que no es necesario depender de que el servidor genere la clave privada. La documentación es insuficiente, así que es comprensible que confunda.
      Para responder a la última pregunta: eBPF XDP, que es lo que uso, solo puede hacer PASS, DROP y REDIRECT. Así que lo manejo con el resultado más simple, PASS/DROP, y la conexión simplemente se queda colgada.
      Sin embargo, si agregas la página de detección de portal cautivo a la lista de MFA de wag, puedes configurar la detección por tu cuenta, y después el navegador se encargará del resto.
      No tengo intención de implementar en wag una funcionalidad que actúe como interceptación o proxy. Hacer eso facilitaría un poco manejar expiraciones de autenticación o cierres de sesión, pero no es la dirección que quiero tomar.
    • Una función así sería realmente genial, y ojalá el autor de este proyecto la considere.
    • Alguna vez hice un servidor parecido. Requería un certificado de cliente por dispositivo; con eso se entraba por mTLS a la página de login, luego se autenticaba al usuario con OIDC y se activaba el túnel. La parte difícil era el cliente.
      Escribí un cliente en Go para Mac y también manejaba la generación de claves usando el wg de línea de comandos de Brew, pero era tosco y requería sudo.
      Sería bueno tener una app nativa bien hecha que use permisos de red, pero eso está fuera de mis capacidades.
  • Me pregunto si ya han abordado, o planean abordar, el problema de la gestión de sesiones.
    En esencia, una clave de WireGuard es como una clave de sesión eterna.
    Si el software que implementa la capa de transporte de WireGuard pretende ser una solución de servidor VPN completa, creo que también debería implementar gestión de sesiones. Es decir: rotar periódicamente las claves de sesión mediante un segundo canal con el servidor, finalizar sesiones, cambiar direcciones IP, configurar nuevas rutas y repetir la autenticación cuando sea necesario.

    • Para ese caso usaría Firezone. Tiene una opción para obligar a los usuarios a iniciar sesión periódicamente en la plataforma y, combinado con un proveedor de identidad externo vía OIDC, se vuelve una solución muy robusta y simple para la gestión de sesiones.
    • No estoy seguro de qué significa exactamente “clave de sesión eterna” en el contexto de wag.
      La clave de WireGuard permite comunicarse con el servidor wag, pero la sesión real se mantiene en un mapa eBPF que contiene si el usuario está autenticado o no.
      Por eso, aunque alguien robe el material de la clave privada, no puede acceder a las rutas protegidas por MFA.
    • Si hiciera un cliente VPN tipo GlobalProtect con WireGuard, pondría una clave de autenticación permanente por cliente para crear un túnel inicial hasta el controlador VPN; dentro de ese túnel se realizaría la autenticación y se recibiría una clave de sesión separada. El primer túnel se cerraría en cuanto terminara la autenticación y se recibiera la clave de sesión real.
    • Un segundo canal para rotar periódicamente claves de sesión, terminar sesiones, cambiar direcciones IP, configurar nuevas rutas y reautenticar… ¿no es básicamente el protocolo IKE de IPsec? ¿No bastaría con usar IPsec?
  • Me pregunto si están impidiendo los ataques de fuerza bruta contra códigos TOTP. Por ejemplo, con límites de velocidad o límites de reintentos.
    Le di un vistazo rápido al código y no encontré nada de eso.
    El escenario que imagino es alguien que abre la UI de ingreso de TOTP en el navegador, abre las herramientas de desarrollo y prueba iterativamente todos los códigos TOTP posibles.

    • Sí hay defensa contra fuerza bruta de códigos TOTP. Cada autenticación tiene un límite de intentos que el usuario puede realizar; si se supera, la cuenta se bloquea y un administrador tiene que desbloquearla.
      En particular, la intención también es hacer que el usuario se pregunte por qué el dispositivo está intentando autenticarse a la fuerza. Una situación así podría indicar que el endpoint está comprometido.
    • Probablemente sea aquí: https://github.com/NHAS/wag/blob/cdbdbec3393fa86bf6c823117c8...
    • No conozco los detalles de esta implementación, pero normalmente, si ya tienes la información de inicio de sesión necesaria para llegar hasta la etapa TOTP —es decir, nombre de usuario y contraseña—, ese usuario ya está comprometido.
  • Suena muy parecido a Headscale o Tailscale. Es bueno ver alternativas para administrar redes WireGuard.
    Me pregunto si hay algún material comparativo para entender hasta dónde se superponen las funciones, qué se agregó, qué es distinto y qué cosas no se implementarán en el futuro.

    • Definitivamente son parecidos en que usan WireGuard.
      No incluí una comparación directa en la documentación, pero actualmente no es la dirección en la que quiero ir. Este proyecto se ajusta a mis necesidades y es bastante divertido.
      Wag encaja mejor con una estructura hub-and-spoke que busca límites firmes, más que con una malla al estilo Tailscale donde todo puede llegar a todo y las reglas definen el overlay.
      Tanto wag como Tailscale agregan integración SSO y, en la práctica, 2FA para proteger a los usuarios.
      Ambos tienen una UI web para métodos de registro y administración, pero como soy un desarrollador en solitario al que no le gusta el desarrollo web, Tailscale seguramente está mucho más pulido.
      Lo que definitivamente no voy a implementar es interceptación o proxy TLS para redirigir usuarios después de cerrar sesión. La razón principal es que hacerlo con eBPF ahora mismo se me hace un poco demasiado, y no quiero usar los componentes DNAT/SNAT que probablemente tendría que escribir para que funcione.
  • Que sea solo IPv4… pensaría que un sitio que elige WireGuard quizá tenga una configuración más moderna y use bastante ULA propia como servicio.

    • Planeo agregar soporte para IPv6 pronto, y también estoy considerando mapear las direcciones IPv4 de los usuarios a un espacio IPv6 privado para reducir el riesgo de conflicto con sus redes locales reales.
      Me pregunto si tenías algo específico en mente al mencionar ULA.
    • Me pregunto cuál es la ventaja de ULA que tienes en mente aquí.