Tailscale ACL

Segmentando la red de Tailscale con ACL y etiquetas: Cómo aislé mi VPS de los dispositivos domésticos

Llevo años usando Tailscale como mi VPN personal: increíblemente cómodo, configuración cero, funciona en todas partes. Pero nunca lo mantenía siempre activo — lo encendía cuando lo necesitaba y lo apagaba después. Un poco rudimentario como precaución, lo sé, pero había una razón: un servidor VPS expuesto a internet sentado en la misma red que mis dispositivos domésticos nunca me convenció. En algún momento decidí hacer las cosas correctamente, en lugar de depender de la disciplina manual.

1. El problema: la red de Tailscale es plana por defecto

Tailscale crea una red mesh privada entre todos tus dispositivos. Por defecto, cada nodo puede alcanzar a cualquier otro nodo en cualquier puerto. Es cómodo, pero es una red plana — exactamente como una LAN doméstica donde todos pueden verse entre sí.

Mi configuración: un servidor VPS expuesto a internet (Nginx, algunos puertos abiertos, servicios públicos) en la misma red Tailscale que mi PC doméstico, smartphone y Raspberry Pis. El riesgo es obvio — por eso no mantenía Tailscale siempre activo. Pero “lo apago cuando no lo necesito” no es una política: es una excusa. Basta con olvidarlo una vez, o querer revisar el monitoreo a las 2am, para tener la red plana activa sin haberlo pensado.

La solución correcta no es activar y desactivar — es segmentar: asegurarse de que el VPS, aunque se vea comprometido, no pueda alcanzar nada al otro lado de la red.

2. La solución: ACL y etiquetas de Tailscale

Tailscale permite definir políticas de acceso mediante un archivo de configuración en formato HuJSON (JSON con comentarios), gestionado desde la consola de administración. El mecanismo se basa en dos conceptos:

  • Etiquetas: etiquetas asignadas a dispositivos (p. ej. tag:server, tag:monitoring). Las etiquetas permiten agrupar dispositivos por rol, independientemente de quién los posea.
  • ACL (Lista de Control de Acceso): reglas que definen quién puede comunicarse con quién, en qué puertos y protocolos.

El sistema funciona como una lista blanca: todo lo que no está explícitamente permitido queda bloqueado. Esta es la diferencia clave respecto a la configuración predeterminada.

Las etiquetas se definen en la sección tagOwners del archivo de política, que especifica quién tiene derecho a asignar esa etiqueta a un dispositivo. Las etiquetas se asignan luego a los dispositivos desde la consola de administración o mediante tailscale set --advertise-tags en el propio dispositivo.

3. Diseño de la red

Estructuré la red con cinco dispositivos, cada uno con un rol bien definido:

DispositivoEtiquetaRol
PC de escritoriotag:personalMáquina de trabajo / uso diario
Smartphonetag:personalDispositivo móvil personal
Raspberry Pi (genérico)tag:homelabServicios internos, experimentos
Raspberry Pi (monitoreo)tag:monitoringPrometheus, Grafana, alertas
Servidor VPStag:serverServicios públicos expuestos a internet

La lógica de acceso que quería implementar:

  • Los dispositivos personal pueden alcanzar todo (soy yo quien administra)
  • El nodo monitoring puede hacer scraping de todos los nodos (puerto 9100 para node_exporter)
  • El server VPS puede ser alcanzado desde personal y monitoring, pero no puede iniciar conexiones hacia otros
  • homelab está aislado: no puede ser alcanzado desde el servidor

4. Las políticas implementadas

Aquí está la matriz de visibilidad resultante:

De Apersonalhomelabmonitoringserver
personal
homelab
monitoring✓ (9100)✓ (9100)
server

Y el archivo ACL HuJSON correspondiente:

{
  // Who can assign tags
  "tagOwners": {
    "tag:personal":    ["autogroup:admin"],
    "tag:homelab":     ["autogroup:admin"],
    "tag:monitoring":  ["autogroup:admin"],
    "tag:server":      ["autogroup:admin"]
  },

  "acls": [
    // Personal devices can reach everything
    {
      "action": "accept",
      "src":    ["tag:personal"],
      "dst":    ["*:*"]
    },

    // Monitoring can scrape homelab and server (node_exporter)
    {
      "action": "accept",
      "src":    ["tag:monitoring"],
      "dst":    ["tag:homelab:9100", "tag:server:9100"]
    },

    // Monitoring can reach itself (e.g. Prometheus → Grafana)
    {
      "action": "accept",
      "src":    ["tag:monitoring"],
      "dst":    ["tag:monitoring:*"]
    },

    // Homelab can only reach itself
    {
      "action": "accept",
      "src":    ["tag:homelab"],
      "dst":    ["tag:homelab:*"]
    }

    // Everything else is implicitly blocked (whitelist)
  ]
}

Con esta configuración, el VPS no puede abrir conexiones hacia ningún otro nodo. Aunque se vea comprometido, queda aislado del resto de la red.

5. Un error común: etiquetas no asignadas a los dispositivos

Este es el error que cometí la primera vez, y probablemente algo con lo que se encontrará cualquiera que se acerque a esta configuración.

Escribí la política, la guardé en la consola de Tailscale y me conecté por SSH al servidor. Todo funcionó. Esperé unos minutos, lo intenté de nuevo — y SSH se quedó bloqueado. Sin timeout explícito, solo silencio.

El problema: había definido las etiquetas en la política pero no las había asignado a los dispositivos. Un dispositivo sin etiqueta no coincide con ninguna regla ACL, por lo que en modo lista blanca no puede enviar ni recibir nada — excepto las conexiones ya activas cuando se aplicó la política (que eventualmente expiran).

Diagnostica esto con:

tailscale status

En la columna de etiquetas, los dispositivos sin etiquetas aparecen vacíos o muestran solo el nombre de usuario. La solución es asignar etiquetas desde el propio dispositivo:

# On the VPS server
sudo tailscale set --advertise-tags=tag:server

# On the monitoring Raspberry Pi
sudo tailscale set --advertise-tags=tag:monitoring

# On the generic Raspberry Pi
sudo tailscale set --advertise-tags=tag:homelab

O desde la consola de administración de Tailscale, en la sección Machines, haciendo clic en el dispositivo y editando sus etiquetas. Tras la asignación, las reglas ACL entran en vigor en pocos segundos.

Nota: para asignar una etiqueta a un dispositivo, ese usuario (o el propio dispositivo, si usa una auth key con preautorización) debe figurar en la sección tagOwners de la política.

6. Conclusión

El principio de mínimo privilegio no es solo para aplicaciones empresariales. Aplicarlo a tu red doméstica — incluso con herramientas de consumo como Tailscale — marca una diferencia real.

El resultado práctico para mí fue poder mantener Tailscale siempre activo sin tener que pensar cada vez en qué está conectado a qué. La segmentación transformó una precaución conductual frágil (“recuerda apagarlo”) en una garantía estructural: el VPS está aislado por diseño, no por disciplina.

Unas pocas docenas de líneas de HuJSON, etiquetas asignadas a los dispositivos, y la topología de red finalmente refleja lo que realmente quería — no lo que era conveniente dejar por defecto.

Si gestionas una red Tailscale con dispositivos de distintos niveles de confianza (y un servidor expuesto a internet definitivamente cuenta como de menor confianza que tu portátil), vale la pena dedicar una tarde a hacer este trabajo correctamente.

¿Quieres hacerlo en cinco minutos? Delégaselo a Claude

Si tu red tiene más que un puñado de dispositivos, diseñar etiquetas y reglas ACL a mano se vuelve tedioso — especialmente si aún no conoces bien la sintaxis HuJSON. Hay una forma mucho más rápida: exporta la lista de hosts desde Tailscale y deja que Claude la procese directamente.

Ve a la consola de administración de Tailscale → Machines, copia la lista de máquinas con el hostname y el rol de cada una, luego pégalo todo en Claude con un prompt como:

Here is the list of machines on my Tailscale network:

- pc-home           → personal PC, daily use
- personal-phone    → personal smartphone
- rpi-monitor       → Raspberry Pi with Prometheus and Grafana
- rpi-homelab       → Raspberry Pi for experiments
- vps-prod          → VPS server exposed to the internet

Create a complete HuJSON file for Tailscale ACLs:
- assign appropriate tags to each device based on its role
- define a sensible isolation policy (the server should not be able
  to reach personal devices or the homelab)
- the monitoring node must be able to scrape all nodes (port 9100)
- add explanatory comments for each rule
- include the tailscale set --advertise-tags commands for each device

Como respuesta obtendrás un archivo HuJSON completo y comentado, la lista de comandos a ejecutar en cada nodo, y una matriz de visibilidad lista para usar — todo listo para pegar en la sección Access Controls de la consola de Tailscale. Si algo no parece correcto, basta con pedir ajustes: en pocos intercambios llegarás a una política personalizada sin tener que estudiar la sintaxis desde cero ni releer la documentación cada cinco minutos.