SSH read-only + Tailscale: dejé que mi IA vigile el servidor sin ponerlo en riesgo
Quería que mi agente local me avisara cuando el servidor empezara a portarse raro — pero darle credenciales sudo a un proceso de IA es una mala idea. La solución: un usuario SSH llamado monitoring con permisos solo de lectura, accesible únicamente por Tailscale. Si te interesa este patrón en tu infraestructura, te lo configuramos.
Quería que mi agente local — el que tengo corriendo con Claude — me avisara cuando el servidor empezara a portarse raro: cuando un container se reiniciara solo, cuando RabbitMQ se desconectara, cuando los logs de Nginx empezaran a llenarse de 502.
El primer impulso fue obvio: dejar la sesión SSH abierta y que el agente ejecute lo que necesite. Pésima idea. Mi sesión SSH tiene sudo, puede tirar containers, modificar configs, leer credenciales. Un proceso de IA con esas llaves no es una herramienta — es una bomba de tiempo bien intencionada.
El patrón que terminé adoptando es el que cuento abajo. Es simple, es viejo, y resuelve el problema sin agregar superficie de ataque: un usuario SSH dedicado con permisos solo de lectura, accesible únicamente por Tailscale. Si quieres algo así en tu servidor, te lo configuramos.
El problema con dejar que la IA tenga acceso "normal"
Cuando un agente automatizado se conecta a tu servidor con tu propio usuario, hereda todos tus permisos. Ese agente puede:
- Leer credenciales en
/etc/y en variables de entorno de containers. - Detener servicios, borrar volúmenes, mover archivos.
- Si el agente es comprometido, alguien con acceso al proceso del agente tiene acceso a tu servidor entero.
Y si el agente "alucina" (le pasa a las IA), puede ejecutar un comando que no le pediste. No por malicia, por ruido del modelo.
El principio que aplica acá es viejo y se llama menor privilegio: el sujeto recibe los mínimos permisos para hacer su trabajo, ni uno más. Si el agente solo tiene que mirar el servidor, no tiene que poder modificarlo. Si solo tiene que ejecutar algunos comandos seguros, el resto debe estar prohibido.
El patrón: un usuario monitoring que solo mira
En el servidor, creé un usuario nuevo llamado monitoring. Esquema en una línea: shell mínima, sin grupo sudo, sin grupo docker (en su lugar, comandos específicos vía sudoers), home aislado, login solo por clave SSH.
# En el servidor, como root o con sudo
sudo useradd -m -s /bin/bash monitoring
# Carpeta para la clave pública del agente
sudo mkdir -p /home/monitoring/.ssh
sudo chmod 700 /home/monitoring/.ssh
# Pegar la clave pública del agente local
sudo nano /home/monitoring/.ssh/authorized_keys
# Permisos correctos
sudo chown -R monitoring:monitoring /home/monitoring/.ssh
sudo chmod 600 /home/monitoring/.ssh/authorized_keys
Hasta acá tengo un usuario que entra por SSH pero no tiene acceso a nada útil todavía. No está en docker, no está en sudo. Si intenta docker ps se va a comer un "permission denied".
El truco: sudoers con solo los comandos que necesitas
El usuario monitoring necesita ejecutar algunos comandos privilegiados — leer logs de Docker, ver el estado de servicios systemd, verificar uso de disco. La forma correcta no es darle el grupo sudo ni el grupo docker, sino crear un archivo en /etc/sudoers.d/ con la lista exacta de comandos permitidos.
sudo visudo -f /etc/sudoers.d/monitoring
Contenido:
# Comandos read-only que monitoring puede ejecutar sin password
monitoring ALL=(root) NOPASSWD: /usr/bin/docker ps
monitoring ALL=(root) NOPASSWD: /usr/bin/docker ps -a
monitoring ALL=(root) NOPASSWD: /usr/bin/docker logs *
monitoring ALL=(root) NOPASSWD: /usr/bin/docker stats --no-stream
monitoring ALL=(root) NOPASSWD: /usr/bin/docker inspect *
monitoring ALL=(root) NOPASSWD: /usr/bin/systemctl status *
monitoring ALL=(root) NOPASSWD: /usr/bin/journalctl --no-pager -n *
monitoring ALL=(root) NOPASSWD: /usr/bin/df -h
monitoring ALL=(root) NOPASSWD: /usr/bin/free -h
monitoring ALL=(root) NOPASSWD: /usr/bin/uptime
monitoring ALL=(root) NOPASSWD: /usr/bin/fail2ban-client status *
# Bloqueos explícitos por las dudas
monitoring ALL=(root) !/usr/bin/docker exec *
monitoring ALL=(root) !/usr/bin/docker restart *
monitoring ALL=(root) !/usr/bin/docker stop *
monitoring ALL=(root) !/usr/bin/docker rm *
monitoring ALL=(root) !/bin/su, !/usr/bin/sudo
Eso es todo lo que el usuario puede ejecutar con privilegios. Cualquier otra cosa devuelve "Sorry, user monitoring is not allowed to execute …".
Importante:
docker logspermite leer logs de cualquier container, incluyendo posibles secretos que se hayan loggeado por error. Es responsabilidad de tu equipo no loggear credenciales — pero si quieres cerrar todavía más, puedes restringir el comando solo a los containers que quieres monitorear.
La capa de red: que el SSH de monitoring no exista para internet
El usuario read-only ya es una buena defensa. Pero si lo dejo accesible en el puerto 22 público, igual estoy invitando a que cada bot del este de Europa intente fuerza bruta contra él.
Solución: que monitoring solo escuche por Tailscale. Tailscale arma una red privada entre tus máquinas (un mesh WireGuard) que no existe en el internet público. Mi laptop y el servidor son dos nodos de esa red; nadie más los ve.
El servidor sigue teniendo SSH público para mi usuario admin (también con clave + Fail2ban + IP whitelisteada), pero al usuario monitoring lo restringimos:
# /etc/ssh/sshd_config.d/monitoring.conf
Match User monitoring
PasswordAuthentication no
PubkeyAuthentication yes
AllowAgentForwarding no
AllowTcpForwarding no
X11Forwarding no
PermitTTY yes
# Solo aceptar conexiones desde rango Tailscale (100.64.0.0/10 es el CGNAT de Tailscale)
Match User monitoring Address 100.64.0.0/10
AllowUsers monitoring
Reinicio sshd y a partir de ese momento, intentar entrar como monitoring desde el internet público devuelve "Permission denied" sin siquiera mirar la clave.
El agente local: pregunta, detecta, avisa
Del lado de mi laptop tengo un script que corre cada 5 minutos como cron, se conecta vía Tailscale al servidor con el usuario monitoring, y ejecuta una pequeña lista de comandos read-only:
ssh monitoring@<tailscale-ip-del-servidor> << 'EOF'
sudo docker ps --format "{{.Names}}\t{{.Status}}"
sudo df -h /
sudo journalctl -u docker --no-pager -n 50
sudo fail2ban-client status sshd
EOF
El output va a un archivo que el agente local lee. Si detecta algo raro — un container marcado como unhealthy, una partición con más de 85% de uso, una cantidad anormal de bans nuevos — me dispara una notificación.
¿Por qué no usar Prometheus + Grafana directo? Porque ya los uso para métricas continuas. Esto es complementario: una vigilancia narrativa, conversacional, que me dice cosas como "el container forms-api se reinició dos veces en la última hora" en vez de gráficas de CPU. Cuando algo de la vigilancia narrativa se prende en rojo, voy a Grafana a profundizar.
Por qué este patrón gana frente a otras opciones
| Alternativa | Por qué la descarté |
|---|---|
| Agente con SSH sudo full | Si lo comprometen, pierdes el servidor. |
| Agente con Docker socket montado | Cualquier "container puede convertirse en root del host" — peor que sudo. |
| Webhook entrante al servidor | Abre un puerto público nuevo. Más superficie. |
| Cloud monitoring SaaS (Datadog, etc.) | Excelente para empresas. Para PyME suele ser overkill, costoso y manda tus logs a terceros. |
| Usuario monitoring read-only + Tailscale | Mínima superficie, costo ~0, agente tiene exactamente lo que necesita. |
El usuario read-only no es una idea nueva — es lo que un equipo SRE con disciplina haría desde el día uno. Lo nuevo es el caso de uso: que el consumidor de esos permisos sea un agente de IA que te ayuda a operar.
Lo que no resuelve este patrón
Para ser honesto: este setup no reemplaza un sistema serio de monitoreo. No reemplaza alertas de uptime externas (si el servidor se cae completo, Tailscale tampoco va a llegar). No reemplaza logs centralizados. No reemplaza un APM.
Lo que sí hace: te da un compañero que te avisa de los detalles que ningún dashboard te grita en la cara, y lo hace por una puerta que es prácticamente imposible de comprometer desde fuera.
Si quieres este setup en tu servidor
Si tienes una VPS y quieres:
- Un usuario
monitoringcon permisos quirúrgicamente acotados. - Tailscale instalado y configurado para que ese usuario solo sea accesible por la red privada.
- Un script de vigilancia local (con tu agente o sin él) que consume ese acceso.
- Notificaciones a Telegram, Discord, email o lo que prefieras cuando algo se desvíe.
Lo configuramos en una sesión. Hablemos. Es la diferencia entre tener un servidor que solo te avisa cuando se cae (vía monitoreo externo) y uno que te dice por qué empezó a temblar antes de caerse.
Relacionado: Cloudflare Tunnel: cero puertos abiertos · SSH cerrado, deploys abiertos · Auditamos nuestro propio servidor · IAM de menor privilegio.
Preguntas frecuentes
¿Tailscale es gratis? ¿Es seguro?
Tailscale tiene un plan gratuito que cubre hasta 100 dispositivos y 3 usuarios — más que suficiente para una PyME. Es seguro: la conexión entre nodos usa WireGuard cifrado punto a punto, las claves se rotan automáticamente y la coordinación pasa por servidores de Tailscale pero el tráfico no. Para casos donde no quieres depender de Tailscale como compañía, está su versión open source Headscale que puedes autohostear.
¿Qué pasa si el agente intenta ejecutar un comando que no está permitido?
sudo simplemente lo rechaza con "Sorry, user monitoring is not allowed to execute …". No hay forma de "saltarse" el sudoers — ese archivo es la lista blanca completa. Si el agente alucina y manda "sudo docker stop forms-api", el comando muere ahí mismo, queda en los logs de auth y tú te enteras en el siguiente reporte.
¿Por qué no usar simplemente un agente como Datadog, New Relic o Better Stack?
Funcionan muy bien y los recomendamos para equipos que pueden pagarlos. Pero tienen costos mensuales por host, agentes pesados que corren con privilegios altos, y mandan tus métricas y logs a terceros. Para una PyME con uno o dos servidores, este patrón cubre el 80% del valor por costo cero — y sin enviar nada fuera de tu infraestructura. Los grandes ganan en profundidad de análisis, paneles y alertas; este patrón gana en simplicidad y privacidad.
¿Qué tan seguido conviene que el agente consulte el servidor?
Para vigilancia narrativa, cada 5 a 15 minutos es razonable. Más frecuente que eso es ruido y consume conexiones SSH innecesariamente. Si necesitas detección casi en tiempo real (ej: un servicio crítico que no puede estar caído más de 1 minuto), esto no es la herramienta — para ese caso usas health checks externos con Uptime Kuma, Better Stack o similar, que sondean cada 30 segundos desde fuera.
¿Sirve si mi servidor no tiene Docker?
Sí, completamente. El patrón es independiente de Docker — el sudoers permite los comandos que tu servidor necesita: nginx, postgresql, mysql, systemctl para servicios, lo que sea. Lo único que cambia es la lista de comandos en /etc/sudoers.d/monitoring. La estructura (usuario read-only + sudoers acotado + acceso solo por Tailscale) aplica a cualquier servidor Linux.