4. Acceso privado con Tailscale¶
TL;DR: Instalar Tailscale de forma segura, configurar ACLs obligatorias para zero-trust, cerrar SSH público y opcionalmente habilitar Tailnet Lock.
Tiempo estimado: 20-30 minutos
Nivel requerido: Intermedio
Prerrequisitos¶
- [ ] Sección 3 (Seguridad del sistema) completada
- [ ] Usuario
openclawfuncionando - [ ] Cuenta Tailscale creada (con MFA en identity provider)
Objetivos¶
Al terminar esta sección tendrás:
- Tailscale instalado y verificado en el VPS
- ACLs configuradas (zero-trust, no permit-all)
- SSH público eliminado (solo acceso por Tailscale)
- Opcionalmente: Tailnet Lock y webhooks de alertas
¿Qué es Tailscale?¶
- VPN mesh peer-to-peer basada en WireGuard
- Free tier: hasta 100 dispositivos, 3 usuarios
- Cada dispositivo recibe una IP privada tipo
100.x.x.x - Cifrado de extremo a extremo
┌─────────────────────────────────────────────┐
│ INTERNET │
│ ❌ │
│ Sin acceso directo al VPS │
└─────────────────────────────────────────────┘
▲
│ Tailscale VPN (WireGuard)
│ Cifrado E2E
▼
┌─────────────────────────────────────────────┐
│ TU DISPOSITIVO │
│ IP Tailscale: 100.y.y.y │
└─────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ VPS │
│ IP Tailscale: 100.x.x.x (única vía) │
│ IP pública: cerrada │
└─────────────────────────────────────────────┘
Placeholders en esta sección
En los ejemplos de esta sección:
<TU_TAILSCALE_IP>o100.x.x.x= IP de Tailscale de tu VPS (obtenida contailscale ip -4)100.y.y.y= IP de Tailscale de tu dispositivo local<TU_IP_PUBLICA>= IP pública del VPS (la que te dio el proveedor)
Reemplaza estos valores con tus IPs reales en cada comando.
Instalar Tailscale en el VPS¶
Conéctate al VPS (todavía por IP pública):
Opción A: Instalación desde repositorio APT (RECOMENDADO)¶
Esta opción es más segura porque verifica firmas de paquetes.
# Añadir clave GPG de Tailscale
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
# Añadir repositorio
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
# Instalar
sudo apt update
sudo apt install -y tailscale
# Verificar que se instaló desde el repo oficial
apt-cache policy tailscale
Salida esperada:
tailscale:
Installed: 1.XX.X
Candidate: 1.XX.X
Version table:
*** 1.XX.X 500
500 https://pkgs.tailscale.com/stable/ubuntu noble/main amd64 Packages
Opción B: Script de instalación (con verificación)¶
Si prefieres el script oficial, verifícalo antes de ejecutar:
# 1. Descargar script
curl -fsSL https://tailscale.com/install.sh -o /tmp/tailscale-install.sh
# 2. Verificar contenido (buscar comandos sospechosos)
# Revisa que solo descarga de dominios tailscale.com/pkgs.tailscale.com
less /tmp/tailscale-install.sh
# 3. Si todo parece correcto, ejecutar
sudo bash /tmp/tailscale-install.sh
# 4. Limpiar
rm /tmp/tailscale-install.sh
Iniciar Tailscale¶
Te dará una URL para autenticar. Ábrela en tu navegador e inicia sesión con tu cuenta Tailscale.
Verificar conexión¶
Salida esperada:
Anota la IP de Tailscale del VPS (algo como 100.x.x.x). Esta será tu nueva forma de acceder.
Instalar Tailscale en tu dispositivo¶
macOS¶
O descarga desde tailscale.com/download
Linux¶
# Usando repositorio APT (Ubuntu/Debian)
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.noarmor.gpg | sudo tee /usr/share/keyrings/tailscale-archive-keyring.gpg >/dev/null
curl -fsSL https://pkgs.tailscale.com/stable/ubuntu/noble.tailscale-keyring.list | sudo tee /etc/apt/sources.list.d/tailscale.list
sudo apt update && sudo apt install -y tailscale
sudo tailscale up
Windows / iOS / Android¶
Descarga la app desde la tienda correspondiente o tailscale.com/download
Probar conexión por Tailscale¶
Desde tu dispositivo local (con Tailscale activado):
Si funciona, ya tienes acceso privado
A partir de ahora, usa esta IP para todo.
Configurar ACLs (OBLIGATORIO)¶
No saltes este paso
Sin ACLs, cualquier dispositivo en tu Tailnet puede acceder a cualquier otro. Esto viola el principio de zero-trust y es un riesgo de seguridad.
Por defecto, Tailscale permite que todos los dispositivos se comuniquen entre sí ("*": ["*:*"]). Esto NO es seguro para un servidor con OpenClaw.
Configurar ACL restrictiva¶
- Ve a login.tailscale.com/admin/acls
- Reemplaza el contenido completo con:
{
// === ACLs para OpenClaw VPS ===
// Referencia: https://tailscale.com/kb/1196/security-hardening
"acls": [
// Solo el owner puede acceder al VPS
{
"action": "accept",
"src": ["autogroup:owner"],
"dst": ["tag:vps:*"]
},
// El VPS puede acceder a Internet (para APIs de LLM)
{
"action": "accept",
"src": ["tag:vps"],
"dst": ["autogroup:internet:*"]
}
// NOTA: El VPS NO puede iniciar conexiones a otros dispositivos
],
// Tags y sus propietarios
"tagOwners": {
"tag:vps": ["autogroup:owner"]
},
// Configuración de SSH por Tailscale
"ssh": [
{
"action": "accept",
"src": ["autogroup:owner"],
"dst": ["tag:vps"],
"users": ["openclaw"]
}
],
// Tests para validar las ACLs
"tests": [
// Verificar que owner puede acceder al VPS
{
"src": "autogroup:owner",
"accept": ["tag:vps:22", "tag:vps:3000"]
},
// Verificar que VPS puede acceder a Internet
{
"src": "tag:vps",
"accept": ["8.8.8.8:443"]
},
// Verificar que VPS NO puede acceder a dispositivos del owner
{
"src": "tag:vps",
"deny": ["autogroup:owner:22", "autogroup:owner:80"]
}
]
}
- Haz clic en Save y verifica que los tests pasan.
Aplicar tag al VPS¶
NUNCA ejecutes tailscale down una vez cerrado el SSH público
Si Tailscale es tu única forma de acceder al servidor y ejecutas tailscale down, perderás todo el acceso. La única recuperación es via el modo rescue del proveedor (ver Troubleshooting más abajo). Usa siempre --reset para reconfigurar sin cortar la conexión.
En el VPS, ejecuta:
Te mostrará una URL para re-autenticar. Ábrela en el navegador y autoriza. NO uses tailscale down + tailscale up — eso cortará tu conexión.
Verificar tags¶
Salida esperada:
El VPS debe aparecer como tagged-devices en lugar de tu email.
Eliminar acceso SSH público¶
Este es el paso crítico. Una vez verificado Tailscale, cierra el puerto 22 público.
Paso 1: Configurar SSH para escuchar solo en Tailscale¶
Añade la directiva ListenAddress automáticamente (evita errores de copia manual):
# Añadir ListenAddress con tu IP real de Tailscale
echo "# === Escuchar SOLO en Tailscale ===" | sudo tee -a /etc/ssh/sshd_config.d/99-openclaw-hardening.conf
echo "ListenAddress $(tailscale ip -4)" | sudo tee -a /etc/ssh/sshd_config.d/99-openclaw-hardening.conf
# Verificar que se añadió correctamente
tail -2 /etc/ssh/sshd_config.d/99-openclaw-hardening.conf
Salida esperada:
IP de ejemplo
Tu IP será diferente (algo como 100.x.x.x). El comando usa automáticamente tu IP real.
Paso 2: Deshabilitar ssh.socket (Ubuntu 24.04)¶
Crítico: Ubuntu 24.04 usa activación por socket por defecto
Ubuntu 24.04 inicia SSH mediante ssh.socket (activación por socket de systemd), que escucha en 0.0.0.0:22 e ignora la directiva ListenAddress de sshd_config. Debes deshabilitarlo y cambiar al servicio tradicional ssh.service para que ListenAddress funcione.
# Deshabilitar activación por socket (ignora ListenAddress)
sudo systemctl disable --now ssh.socket
# Habilitar servicio SSH tradicional (respeta ListenAddress)
sudo systemctl enable ssh.service
# Crear directorio de separación de privilegios (normalmente lo crea ssh.socket)
sudo mkdir -p /run/sshd
# Matar procesos sshd residuales de la activación por socket
sudo kill $(cat /run/sshd.pid 2>/dev/null) 2>/dev/null || true
Paso 3: Asegurar que SSH arranque después de Tailscale¶
Crítico: sin esto, SSH fallará en cada reinicio
Como SSH está configurado para escuchar solo en la IP de Tailscale (ListenAddress),
debe esperar a que Tailscale esté listo antes de arrancar. Sin este drop-in, SSH
intenta hacer bind a la IP de Tailscale antes de que exista, falla con "Cannot assign
requested address" y pierdes acceso SSH hasta reiniciarlo manualmente desde la consola VNC.
# Crear drop-in de systemd para que SSH espere a Tailscale
sudo mkdir -p /etc/systemd/system/ssh.service.d
sudo tee /etc/systemd/system/ssh.service.d/after-tailscale.conf > /dev/null << 'EOF'
[Unit]
After=tailscaled.service
Wants=tailscaled.service
[Service]
RestartSec=5
Restart=on-failure
EOF
sudo systemctl daemon-reload
Esto asegura:
- SSH espera a que
tailscaledarranque antes de hacer bind - SSH se reinicia automáticamente si falla (ej: Tailscale tardó en asignar la IP)
Paso 4: Verificar sintaxis¶
Si hay errores, NO reinicies SSH. Corrige primero.
Paso 5: Reiniciar SSH¶
Paso 6: Eliminar regla de firewall¶
# Eliminar regla que permite SSH desde cualquier lugar
sudo ufw delete allow ssh
# Verificar estado
sudo ufw status
Salida esperada:
Verificación final¶
Prueba ANTES de cerrar tu sesión actual
En una NUEVA terminal:¶
# 1. Conectar por Tailscale (DEBE funcionar)
ssh openclaw@<TU_TAILSCALE_IP>
# 2. Verificar que SSH público NO funciona
ssh openclaw@<TU_IP_PUBLICA>
# Esperado: Connection timed out o Connection refused
Verificar que SSH solo escucha en Tailscale¶
Salida esperada:
Verifica el acceso por consola antes de cerrar tu sesión
Tu contraseña local es la única vía de recuperación si Tailscale no arranca tras un reinicio.
Abre la consola VNC de Hetzner (icono Console en el panel) y verifica que puedes hacer
login con tu usuario openclaw. Si no pusiste contraseña al crear el usuario,
hazlo ahora con sudo passwd openclaw — la autenticación SSH por contraseña sigue
deshabilitada independientemente.
Si ves solo tu IP de Tailscale, el hardening está completo
Tailnet Lock (Seguridad avanzada)¶
Recomendado para máxima seguridad
Tailnet Lock asegura que incluso si Tailscale (la empresa) fuera comprometida, no podrían inyectar dispositivos maliciosos en tu red.
¿Qué es Tailnet Lock?¶
- Requiere firmas criptográficas para añadir dispositivos
- Los dispositivos de confianza firman nuevos dispositivos
- Incluso Tailscale (la empresa) no puede añadir dispositivos sin tu firma
Activar Tailnet Lock¶
Desde tu dispositivo principal (NO el VPS):
Te mostrará una clave de firma. Guárdala en lugar seguro offline.
# 3. Ver el node key del VPS (necesitas esto para firmarlo)
tailscale status
# Busca la línea del VPS y copia el node key
Verificar Tailnet Lock¶
Salida esperada:
Tailnet lock is ENABLED
...
Trusted signing keys:
- nlpub:XXXX (this node)
Filtered nodes:
(ninguno, si todos están firmados)
Guarda las claves de recuperación
Si pierdes acceso a todos los dispositivos firmantes, perderás acceso a la red. Guarda las claves en un gestor de contraseñas o lugar seguro offline.
Webhooks de alertas (Opcional)¶
Recibe notificaciones cuando algo cambie en tu Tailnet.
Configurar webhooks¶
-
Añade un webhook:
- Slack: URL del incoming webhook
- Discord: URL del webhook de Discord
-
Custom: Tu endpoint HTTP/HTTPS
-
Selecciona eventos a monitorear:
| Evento | Descripción | Recomendación |
|---|---|---|
nodeCreated |
Nuevo dispositivo añadido | ✅ Activar |
nodeDeleted |
Dispositivo eliminado | ✅ Activar |
nodeApproved |
Dispositivo aprobado | ✅ Activar |
nodeKeyExpiring |
Clave a punto de expirar | ✅ Activar |
userCreated |
Nuevo usuario | ✅ Activar |
userDeleted |
Usuario eliminado | ✅ Activar |
Ejemplo de payload de webhook¶
{
"timestamp": "2026-02-01T10:30:00Z",
"event": "nodeCreated",
"tailnet": "tu-tailnet.ts.net",
"node": {
"name": "nuevo-dispositivo",
"addresses": ["100.x.x.x"]
}
}
MagicDNS Hardening¶
MagicDNS permite resolver nombres de dispositivos dentro de tu Tailnet. Para mayor seguridad:
Verificar configuración de MagicDNS¶
- Ve a login.tailscale.com/admin/dns
- Configura:
- MagicDNS: Habilitado (para resolver nombres internos)
- Override local DNS: Habilitado (para evitar DNS leaks)
- Global nameservers: Configura servidores DNS seguros (ej:
1.1.1.1,9.9.9.9)
Añadir Split DNS (opcional)¶
Si necesitas resolver dominios internos específicos:
Ventaja de Override local DNS
Con overrideLocalDNS: true, todo el tráfico DNS del VPS pasa por los servidores configurados en Tailscale, evitando posibles DNS leaks a través del proveedor VPS.
Expiración de claves de nodo¶
Por defecto, las claves de nodo de Tailscale expiran cada 180 días. Debes renovarlas o desactivar la expiración para el VPS.
Opción A: Desactivar expiración (recomendado para servidores)¶
- Ve a login.tailscale.com/admin/machines
- Encuentra tu VPS en la lista
- Haz clic en el menú ... → Disable key expiry
Implicación de seguridad
Desactivar la expiración significa que el nodo permanecerá autorizado indefinidamente. Esto es aceptable para servidores que no cambian de propietario. Para dispositivos personales, mantén la expiración activa.
Opción B: Renovar manualmente¶
Si prefieres mantener la expiración activa, renueva antes de que expire:
# Ver cuándo expira la clave actual
tailscale status --json | jq '.Self.KeyExpiry'
# Renovar (requiere re-autenticación)
sudo tailscale up --reset
Configurar alerta de expiración¶
Añade al cron una verificación semanal:
# Verificar expiración de clave Tailscale cada lunes
0 9 * * 1 tailscale status --json | jq -r '.Self.KeyExpiry // "no-expiry"' | logger -t tailscale-expiry
Estado actual del sistema¶
┌─────────────────────────────────────────────┐
│ INTERNET │
│ ❌ │
│ Puerto 22 cerrado / no accesible │
│ Sin puertos públicos │
└─────────────────────────────────────────────┘
┌─────────────────────────────────────────────┐
│ VPS (Hetzner/otro) │
│ IP pública: xxx.xxx.xxx.xxx (NO usada) │
│ IP Tailscale: 100.x.x.x (única vía) │
│ Tag: tag:vps │
│ ┌─────────────────────────────────────┐ │
│ │ SSH escuchando en 100.x.x.x:22 │ │
│ │ Firewall: deny all incoming │ │
│ │ ACLs: solo owner puede acceder │ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘
▲
│ Tailscale VPN (cifrado WireGuard)
│ ACLs verificadas
▼
┌─────────────────────────────────────────────┐
│ TU DISPOSITIVO (owner) │
│ IP Tailscale: 100.y.y.y │
│ Puede: SSH a VPS, acceder a puerto 3000 │
└─────────────────────────────────────────────┘
Comandos útiles de Tailscale¶
# Ver todos los dispositivos en tu red
tailscale status
# Ver tu IP de Tailscale
tailscale ip -4
# Ping a otro dispositivo por nombre
tailscale ping nombre-vps
# Ver logs de Tailscale
sudo journalctl -u tailscaled -f
# Desconectar temporalmente
tailscale down
# Reconectar
tailscale up
# Ver configuración actual
tailscale debug prefs
Subir archivos al VPS¶
Ahora que solo tienes acceso por Tailscale, usa estos comandos para transferir archivos:
# Subir un archivo
scp archivo.txt openclaw@<TU_TAILSCALE_IP>:~/workspace/
# Subir una carpeta
scp -r mi-carpeta/ openclaw@<TU_TAILSCALE_IP>:~/workspace/
# Sincronizar carpeta (más eficiente para actualizaciones)
rsync -avz mi-carpeta/ openclaw@<TU_TAILSCALE_IP>:~/workspace/mi-carpeta/
# Descargar archivo del VPS
scp openclaw@<TU_TAILSCALE_IP>:~/workspace/resultado.txt ./
Troubleshooting¶
Error: "Tailscale not running"¶
Causa: El servicio tailscaled no está activo.
Solución:
Error: "Not authorized" al usar tags¶
Causa: Las ACLs no permiten el tag o no eres owner del tag.
Solución:
1. Verifica que tagOwners incluye tu usuario o autogroup:owner
2. Verifica que guardaste las ACLs en el panel de Tailscale
Error: "Connection timed out" por Tailscale¶
Causa: Firewall local o de red bloqueando WireGuard.
Solución:
# Verificar que tailscaled está escuchando
sudo ss -ulnp | grep tailscale
# Verificar conectividad
tailscale netcheck
SSH sigue accesible en la IP pública tras configurar ListenAddress¶
Causa: Ubuntu 24.04 usa ssh.socket (activación por socket de systemd) que escucha en 0.0.0.0:22 e ignora ListenAddress.
Solución:
# Verificar si ssh.socket está activo
systemctl is-active ssh.socket
# Si está activo, deshabilitarlo y cambiar al servicio tradicional
sudo systemctl disable --now ssh.socket
sudo systemctl enable ssh.service
sudo mkdir -p /run/sshd
sudo kill $(cat /run/sshd.pid 2>/dev/null) 2>/dev/null || true
sudo systemctl restart ssh
# Verificar que solo escucha en la IP de Tailscale
ss -tln | grep :22
SSH falla tras reinicio: "Cannot assign requested address"¶
Causa: SSH intentó hacer bind a la IP de Tailscale antes de que tailscaled la asignara. Ocurre cuando falta el drop-in de systemd (/etc/systemd/system/ssh.service.d/after-tailscale.conf) o Tailscale tardó en arrancar.
Arreglo rápido (desde consola VNC o sesión existente):
Arreglo permanente — instala el drop-in si no existe:
sudo mkdir -p /etc/systemd/system/ssh.service.d
sudo tee /etc/systemd/system/ssh.service.d/after-tailscale.conf > /dev/null << 'EOF'
[Unit]
After=tailscaled.service
Wants=tailscaled.service
[Service]
RestartSec=5
Restart=on-failure
EOF
sudo systemctl daemon-reload
sudo systemctl restart ssh
Checklist post-reinicio
Después de cada reinicio del VPS, si no puedes conectar por SSH, entra por la consola VNC de Hetzner y ejecuta:
Con el drop-in instalado, esto debería ser automático — pero si algo falla, estos dos comandos lo solucionan.Error: "Cannot bind any address" o "Address already in use"¶
Causa: Tras deshabilitar ssh.socket, quedan procesos sshd residuales de la activación por socket que siguen ocupando el puerto.
Solución:
# Matar proceso sshd residual
sudo kill $(cat /run/sshd.pid 2>/dev/null) 2>/dev/null || true
sudo systemctl restart ssh
Error: "Missing privilege separation directory: /run/sshd"¶
Causa: El directorio /run/sshd normalmente lo crea ssh.socket. Tras deshabilitarlo, el directorio no existe.
Solución:
Perdí el acceso: SSH público cerrado y Tailscale caído¶
Causa: Ejecutaste tailscale down, Tailscale se cayó, o tailscaled no estaba habilitado y no arrancó tras un reinicio. La consola del VPS tampoco funciona si tu usuario no tiene contraseña.
Solución — Modo Rescue de Hetzner:
- En el panel de Hetzner Cloud → tu servidor → pestaña Rescue → Enable Rescue & Power Cycle
- Copia la contraseña de root que muestra el panel
- Ve a la pestaña Power → Power cycle el servidor (arrancará en rescue)
- Accede al sistema rescue por una de estas vías:
- Consola VNC (recomendado): Haz clic en el icono Console (
>_) en el panel de Hetzner. Login conrooty la contraseña del rescue. No necesita red ni firewall. - SSH (si tu Cloud Firewall permite puerto 22): Usa la contraseña de root del rescue del paso 2.
- Consola VNC (recomendado): Haz clic en el icono Console (
- Monta tu disco y entra en chroot:
- Corrige el problema — pon contraseñas y asegura que Tailscale arranque al inicio:
- Sal del chroot y reinicia en modo normal:
- En el panel de Hetzner → pestaña Rescue → Disable Rescue para que el siguiente reinicio sea normal.
- Entra por la consola VNC con la contraseña que acabas de poner y verifica:
Teclado no-US en la consola VNC
El sistema rescue usa teclado US por defecto. Si tienes teclado español, ejecuta loadkeys es primero. Alternativamente, usa el botón de pegar en la barra de la consola VNC para evitar problemas de mapeo de teclado.
Cloud Firewall de Hetzner
El modo rescue arranca un sistema diferente que ignora UFW, pero sí respeta el Cloud Firewall de Hetzner. Si SSH al rescue es rechazado, usa la consola VNC en su lugar, o ve a la pestaña Firewalls y añade temporalmente una regla de entrada permitiendo TCP puerto 22.
Resumen¶
| Configuración | Estado |
|---|---|
| Tailscale instalado | ✅ |
| Verificado desde repo oficial | ✅ |
| ACLs configuradas (no permit-all) | ✅ |
Tag vps asignado |
✅ |
| SSH solo en interfaz Tailscale | ✅ |
| Puerto 22 público cerrado | ✅ |
| Tailnet Lock (opcional) | ⬜ |
| Webhooks configurados (opcional) | ⬜ |
Siguiente: 5. Instalar OpenClaw