Acceso remoto seguro a agentes de IA · versión 2

El látigo de largo del radio de la Tierra, versión 2

En la versión 1 conté cómo manejo mis agentes de IA desde el celular con herdr y Collie, por SSH a través de Cloudflare. Funcionaba. Ahora dejé Cloudflare y el túnel SSH, y todo viaja por Tailscale: una red privada entre mis dispositivos, montada sobre internet. No publico nada, entro a Collie desde el celular y la tablet sin túneles, y de paso uso mi Pi-hole desde cualquier lugar.

Publicado el

Versión 1 La configuración anterior, con Cloudflare Access, Tunnel y SSH, sigue en agentes-ia-remoto-seguro.pages.dev. Esta página parte de ahí y explica qué cambié y por qué.
Publicado en internet
Nada: ni puertos ni hostname público
Dispositivos en mi red privada
4: notebook, Raspberry Pi, celular y tablet
Costo
0: plan Personal de Tailscale

Parte 1 · El resumen

Qué cambió

El objetivo es el mismo de la versión 1: ver y responder a mis agentes de IA cuando no estoy frente al computador, sin abrir la máquina a internet. Cambió el camino. Antes, el celular abría una sesión SSH que pasaba por Cloudflare, y Collie viajaba dentro de esa sesión. Ahora el celular, la tablet, el notebook y una Raspberry Pi están en la misma red privada, y Collie se abre directo, con HTTPS, solo dentro de esa red.

Aspectov1 · Cloudflare + SSHv2 · Tailscale
Qué hay en internetUn hostname público, protegido por Cloudflare AccessNada
Cómo llega el celularTermux: SSH dentro de un WebSocket hacia CloudflareLa app de Tailscale: red privada cifrada con WireGuard
Dónde abro Colliehttp://localhost:8787, mientras la sesión SSH esté abiertahttps://pc.tu-tailnet.ts.net, siempre que Tailscale esté activo
Sumar un dispositivoTermux, cloudflared, una llave SSH autorizada y login en AccessInstalar Tailscale, iniciar sesión y emparejar Collie
BarrerasAccess · llave SSH · emparejamientoRed privada · emparejamiento
Dominio propioSí, para el hostname del túnelNo: uso el nombre que da Tailscale (*.ts.net)
AdemásNada másPi-hole fuera de casa y salida a internet por mi casa

Parte 2 · El motivo

Por qué dejé Cloudflare y el túnel SSH

Lo que me molestaba de la v1

  • Un hostname público, aunque estuviera protegido. El nombre existía en internet y cualquiera podía llegar hasta el login de Access. La seguridad dependía de que Access protegiera exactamente ese nombre: en la v1 conté cómo un guion de diferencia dejó todo abierto.
  • Cada dispositivo nuevo era un proyecto. Termux, cloudflared, una llave SSH, ProxyCommand y LocalForward. Para la tablet no valía la pena.
  • Collie dependía de la sesión SSH. Antes de abrir la app tenía que abrir Termux y conectarme. Si se cortaba SSH, Collie quedaba sin conexión.
  • Quería mi Pi-hole fuera de casa. Bloquear anuncios y rastreadores también en 4G o 5G. Con un túnel SSH no había una forma simple.

Las mismas condiciones, cumplidas de otra forma

Condición de la v1En la v2
Ningún puerto entrante, ni en el router ni en el notebookSe cumple. Tailscale solo usa conexiones salientes, y Collie se publica solo dentro de la red privada.
Verificar mi identidad antes de que el tráfico toque el computadorSe cumple. Solo un dispositivo conectado con mi cuenta tiene una ruta hacia el notebook.
Una sola vía de entrada, SSH, también para la webCambió. La única vía es la red privada, y Collie ya no necesita SSH.
Usar la llave privada que ya tengo en el celularYa no hace falta. Cada dispositivo genera su propia llave WireGuard, que nunca sale del equipo.

Parte 3 · La idea

Qué es Tailscale, en simple

Celular y tablet

App de Tailscale

Collie instalado como app. Cada equipo tiene su propia llave.

Red privada

Mi tailnet

Solo mis dispositivos se ven entre sí. Desde internet, ninguno existe.

En casa

Notebook y Raspberry Pi

Collie y herdr en el notebook. Pi-hole en la Raspberry.

Tailscale arma una red privada, que llama tailnet, entre los dispositivos que conecto con mi cuenta. Cada uno recibe una dirección 100.x.y.z que solo existe dentro de esa red. Por debajo usa WireGuard: cada dispositivo genera su par de llaves, y la privada no sale del equipo.

Los servidores de Tailscale solo coordinan: reparten las llaves públicas y ayudan a que los equipos se encuentren. El tráfico va directo de un equipo a otro, aunque estén en redes distintas. Cuando una red no deja conectar directo, los paquetes pasan por un relay de Tailscale (DERP), que solo transporta datos ya cifrados.

Cloudflare ponía una puerta pública con un portero. Tailscale no pone puerta: desde internet, el notebook no existe.

Eso cambia el tipo de riesgo. En la v1, un error en la configuración de Access exponía el SSH a todo internet. En la v2, para llegar a Collie primero hay que ser un dispositivo de mi red.

Parte 4 · La arquitectura

El recorrido de una petición

Así llega un toque en la pantalla del celular hasta un agente de IA. Cada paso solo ocurre si el anterior dejó pasar la petición.

En el celular
  1. 1
    Collie instalado como apphttps://pc.tu-tailnet.ts.netHTTPS de verdad, con un certificado válido. Instalar la app, las notificaciones y el dictado funcionan sin el truco de localhost.
  2. 2
    App de TailscaleWireGuard · llave del celularReconoce que esa dirección es de mi red privada y cifra el tráfico. Si el celular no está en mi red, no hay a dónde ir.
    Barrera 1 · red privada
En la red privada
  1. 3
    Conexión directacelular ⇄ notebookLos dos equipos se encuentran con conexiones salientes, sin abrir puertos. Si no pueden, un relay de Tailscale pasa los paquetes ya cifrados.
En el notebook
  1. 4
    tailscale serve:443 → http://127.0.0.1:8787Termina el HTTPS y entrega la petición en loopback. Solo responde dentro de la red privada.
  2. 5
    Collie127.0.0.1:8787Comprueba que el dispositivo esté emparejado.
    Barrera 2 · dispositivo
  3. 6
    herdrsocket Unix ~/.config/herdr/herdr.sockCollie lee y escribe en los paneles donde trabajan los agentes de IA.

Parte 5 · Defensa en profundidad

Dos barreras independientes

La llave SSH, que era la barrera 2 de la versión 1, salió del camino: Collie ya no viaja por SSH. El servidor SSH del notebook sigue escuchando solo en loopback, y no lo publico ni siquiera dentro de la red privada. En Posibles mejoras hay formas de sumar más barreras.

1 · ¿Estás en mi red?

Tailscale

Solo mis dispositivos, conectados con mi cuenta, tienen una ruta hacia el notebook. Cada uno se identifica con su propia llave WireGuard.

Fuera de la red privada: no hay nada a qué conectarse.
2 · ¿Qué dispositivo eres?

Emparejamiento de Collie

Igual que en la v1: un código de un solo uso, generado en el notebook, deja un token en cada dispositivo.

Sin emparejar: la API responde "device not paired".
Base · redCollie sigue escuchando en 127.0.0.1. tailscale serve lo publica solo dentro de la red privada, y el router no tiene ningún puerto abierto.

Parte 6 · El resultado

Lo que gané

varios dispositivos
El celular y la tablet usan Collie igual. Sumar uno es instalar la app de Tailscale, iniciar sesión y emparejar.
sin túnel previo
Collie se abre como cualquier app. No hay una sesión SSH que levantar antes.
Pi-hole en todas partes
El DNS de todos mis dispositivos pasa por la Raspberry Pi, también en 4G o 5G: unos 430 mil dominios de anuncios y rastreadores bloqueados donde esté.
salida por mi casa
En un Wi-Fi público puedo hacer que todo el tráfico salga por mi casa, cifrado hasta allá.
nada publicado
No hay hostname, túnel ni login web que proteger. Lo que no está publicado no se puede escanear.
gratis
El plan Personal cubre hasta 6 usuarios y dispositivos ilimitados, y no cobra por tráfico: los datos van directo entre mis equipos.
DispositivoQué hace en la red privada
Notebook (Pop!_OS)Corre herdr y Collie. Publica Collie con tailscale serve.
Raspberry PiPi-hole con Unbound: el DNS de todos los dispositivos. También es la salida a internet cuando la activo.
CelularCollie instalado. Administra la Raspberry.
TabletCollie instalado.

Parte 7 · Paso a paso

La configuración

Los nombres de equipo, las direcciones y los identificadores son ejemplos. Reemplázalos por los tuyos.

1. Tailscale en el notebook

Se instala desde su repositorio oficial y se conecta a la cuenta con un enlace que abre el navegador.

El operador permite que mi usuario maneje Tailscale sin sudo. Collie lo necesita para publicar su entrada.

Notebook
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
sudo tailscale set --operator=$USER

2. HTTPS en la consola de Tailscale

Collie necesita HTTPS para instalarse como app y enviar notificaciones, y collie start se detiene con un error si los certificados están apagados.

  1. Consola de Tailscale → DNS.
  2. MagicDNS: activado. Viene así por defecto.
  3. HTTPS Certificates → Enable HTTPS.
Comprobación
tailscale status --json | grep -A2 CertDomains
# "pc.tu-tailnet.ts.net"

3. Collie publica su propia entrada

En la v1, COLLIE_SKIP_SERVE=1 le decía a Collie que no publicara nada, porque la entrada era SSH. Basta con comentarlo: collie restart ejecuta tailscale serve y deja Collie en HTTPS, solo dentro de la red privada.

Collie sigue escuchando en 127.0.0.1:8787. Lo que cambia es quién lo trae hasta ahí.

~/.config/collie/.env (permisos 600)
COLLIE_HOST=127.0.0.1
COLLIE_PORT=8787
COLLIE_MUX=herdr
# COLLIE_SKIP_SERVE=1   # activo en la v1; ahora Collie publica con tailscale serve
Aplicar y comprobar
collie restart
collie status            # tailnet  https://pc.tu-tailnet.ts.net
tailscale serve status
# https://pc.tu-tailnet.ts.net (tailnet only)
# |-- / proxy http://127.0.0.1:8787

4. Celular y tablet

  1. Instalar la app de Tailscale e iniciar sesión con la misma cuenta.
  2. Abrir https://pc.tu-tailnet.ts.net en el navegador.
  3. En el notebook, ejecutar collie pair y escribir el código en Settings → Paired devices.
  4. Instalar la app desde el menú del navegador.

En el celular borré el acceso viejo a localhost:8787 y revoqué su emparejamiento. También saqué el ProxyCommand y el LocalForward de la configuración SSH de Termux.

Notebook
collie pair              # código de 8 caracteres, válido por 10 minutos
collie devices list
collie devices revoke "<dispositivo-viejo>"

5. Pi-hole para todos los dispositivos

La Raspberry Pi entra a la red privada sin usar el DNS de Tailscale, para no consultarse a sí misma en un bucle.

Pi-hole, por defecto, solo responde a redes locales, y las direcciones 100.x de Tailscale no cuentan como locales. Lo pasé a ALL. Es seguro porque la Raspberry no tiene puertos abiertos a internet.

  1. Consola → DNS → Nameservers → Add nameserver → Custom: la IP de Tailscale de la Raspberry.
  2. Activar Override DNS servers, para que los equipos lo usen en cualquier red.
  3. Machines → la Raspberry → Disable key expiry, para que no salga de la red cada 180 días.
Raspberry Pi
sudo tailscale up --accept-dns=false
sudo pihole-FTL --config dns.listeningMode ALL
Comprobación en el notebook
tailscale dns status | grep -A1 Resolvers
# - <ip-tailscale-de-la-raspberry>

6. Salir a internet por mi casa (opcional)

La Raspberry se ofrece como nodo de salida. En el celular elijo cuándo usarla: todo el tráfico va cifrado hasta mi casa y sale con la IP de mi conexión.

  1. Consola → Machines → la Raspberry → Edit route settings → Use as exit node.
  2. En el celular: Exit node → la Raspberry, con Allow LAN access activado.
Raspberry Pi
printf 'net.ipv4.ip_forward = 1\nnet.ipv6.conf.all.forwarding = 1\n' \
  | sudo tee /etc/sysctl.d/99-tailscale.conf
sudo sysctl -p /etc/sysctl.d/99-tailscale.conf
sudo tailscale set --advertise-exit-node

7. Desarmar la versión 1

Detuve cloudflared sin desinstalarlo, porque lo uso para otras cosas, y saqué la regla de SSH de su configuración. Así, si un día lo vuelvo a encender, no publica el SSH otra vez.

En Cloudflare borré el túnel y la aplicación de Access. Sin túnel detrás, el hostname viejo responde un error 530.

Notebook
sudo systemctl disable --now cloudflared
sudo cp /etc/cloudflared/config.yml /etc/cloudflared/config.yml.bak
# quitar del ingress la regla hostname → ssh://localhost:22
systemctl is-active cloudflared    # inactive

Parte 8 · Más cosas a considerar

Lo que aprendí en el cambio

Tailscale SSH no es SSH por Tailscale

Tailscale trae su propio servidor SSH, que autentica con la identidad de Tailscale y no con llaves. Lo probé y la Raspberry entró a mi notebook sin tener ninguna llave autorizada: le bastó con ser un equipo de mi cuenta.

Regla: no actives tailscale set --ssh si no quieres que todos tus equipos entren sin llave.

Por defecto, todos ven todo

La política inicial de Tailscale deja que cualquier dispositivo de la red llegue a cualquier puerto de otro. Desde la Raspberry podía alcanzar la entrada de Collie y un puerto de Docker del notebook.

Regla: para cerrarlo, una política con lo mínimo. Hay un ejemplo en Posibles mejoras.

HTTPS va antes que Collie

Si quitas COLLIE_SKIP_SERVE con los certificados apagados, Collie no arranca. Al activarlos, el nombre del equipo queda en los registros públicos de certificados. Se publica solo el nombre, no el acceso.

Regla: activa HTTPS en la consola y después cambia Collie.

Tu propia IP de Tailscale no pasa por Tailscale

Una prueba desde el notebook contra su propia dirección 100.x daba Connection refused, aunque todo estaba bien. Esas conexiones se quedan dentro del equipo y nunca llegan a Tailscale.

Regla: prueba siempre desde otro equipo de la red.

Docker se salta todo, otra vez

Igual que con UFW en la v1, un contenedor publicado con -p 3100:80 escucha en todas las interfaces, y eso incluye la red privada.

Regla: publica los contenedores en 127.0.0.1:puerto.

Si la Raspberry se apaga, no hay DNS

Con Override DNS, los dispositivos con Tailscale activo le preguntan todo a la Raspberry. Si se cae, se quedan sin nombres, estén donde estén.

Regla: si cae la Raspberry, apaga Tailscale en el dispositivo hasta que vuelva.

Parte 9 · Confiar, pero comprobar

Cómo comprobarlo

PruebaResultado esperado
tailscale serve status en el notebookhttps://pc.tu-tailnet.ts.net (tailnet only) hacia 127.0.0.1:8787
ss -tln en el notebookCollie sigue en 127.0.0.1:8787; nada nuevo en 0.0.0.0
Abrir la página desde otro equipo de la red200, con un certificado válido de Let's Encrypt
La API desde un equipo sin emparejardevice not paired
Abrir la página con Tailscale apagadoNo conecta
tailscale statusLas conexiones aparecen como direct
tailscale dns status en el notebookResolvers: la IP de Tailscale de la Raspberry
systemctl is-active cloudflaredinactive

Parte 10 · Para reforzar

Posibles mejoras

La solución funciona tal como está. Estas son formas de sumar barreras, por si quiero endurecerla más, de mayor a menor impacto.

Política de acceso en Tailscale

La política por defecto deja que cualquier dispositivo de la red llegue a cualquier puerto de otro. Una política con lo mínimo agrega una barrera de red: celular y tablet llegan a Collie, notebook y celular administran la Raspberry, y todos usan su DNS. Lo demás se descarta antes de llegar al equipo.

Las pruebas del final hacen que la consola rechace el cambio si algo no se cumple.

Consola → Access controls (JSON)
{
  "hosts": {
    "notebook":  "<ip-del-notebook>",
    "raspberry": "<ip-de-la-raspberry>",
    "celular":   "<ip-del-celular>",
    "tablet":    "<ip-de-la-tablet>",
  },
  "grants": [
    // Todos: DNS de Pi-hole y salida por la Raspberry
    {"src": ["autogroup:member"], "dst": ["raspberry"], "ip": ["tcp:53", "udp:53"]},
    {"src": ["autogroup:member"], "dst": ["autogroup:internet"], "ip": ["*"]},
    // Collie en el notebook: celular y tablet
    {"src": ["celular", "tablet"], "dst": ["notebook"], "ip": ["tcp:443"]},
    // Administrar la Raspberry: notebook y celular
    {"src": ["notebook", "celular"], "dst": ["raspberry"],
     "ip": ["tcp:22", "tcp:80", "tcp:443", "tcp:445", "tcp:9090"]},
  ],
  "ssh": [],
  "tests": [
    {"src": "raspberry", "deny": ["notebook:22", "notebook:443"]},
    {"src": "tablet", "accept": ["notebook:443", "raspberry:53"],
     "deny": ["notebook:22", "raspberry:22"]},
  ],
}
Cuenta

Proteger la cuenta de Tailscale

Quien controla la cuenta puede sumar dispositivos a la red. Verificación en dos pasos o passkey en el proveedor con que inicio sesión, y Device approval para aprobar a mano cada equipo nuevo.

Red

Tailnet Lock

Cada equipo nuevo necesita la firma de uno de mis equipos de confianza. Así, ni siquiera un servidor de Tailscale comprometido podría meter un equipo en mi red.

Collie

Emparejamientos con vencimiento

Por defecto, un dispositivo emparejado no vence. Con un plazo, un dispositivo olvidado pierde el acceso solo: collie devices set-expiry <dispositivo> 30d.

Collie

Collie solo para mi usuario

tailscale serve le dice a Collie qué usuario de Tailscale hace cada petición. Con COLLIE_TRUSTED_USER, Collie rechaza las que no vengan de mi cuenta. Sirve si algún día comparto un equipo o sumo otro usuario a la red.

Parte 11 · Sin vender humo

Límites

Lo que esto no resuelve

  • Confío en el servidor de coordinación de Tailscale. Sin Tailnet Lock, podría agregar un equipo a mi red.
  • Mi cuenta de Tailscale es la llave maestra: quien la controle puede sumar dispositivos.
  • Un celular o una tablet desbloqueados y emparejados equivalen a una terminal en mi notebook, igual que en la v1.
  • La Raspberry es un punto único para el DNS de todos mis dispositivos.

Si pierdo el celular

  • Quitarlo en la consola de Tailscale: Machines → el celular → Remove.
  • collie devices revoke <dispositivo>

Cada paso cierra una barrera distinta, así que conviene hacer los dos.

Parte 12 · Para seguir leyendo

Referencias

Red privada

Agentes

DNS