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.Acceso remoto seguro a agentes de IA · 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
Parte 1 · El resumen
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.
| Aspecto | v1 · Cloudflare + SSH | v2 · Tailscale |
|---|---|---|
| Qué hay en internet | Un hostname público, protegido por Cloudflare Access | Nada |
| Cómo llega el celular | Termux: SSH dentro de un WebSocket hacia Cloudflare | La app de Tailscale: red privada cifrada con WireGuard |
| Dónde abro Collie | http://localhost:8787, mientras la sesión SSH esté abierta | https://pc.tu-tailnet.ts.net, siempre que Tailscale esté activo |
| Sumar un dispositivo | Termux, cloudflared, una llave SSH autorizada y login en Access | Instalar Tailscale, iniciar sesión y emparejar Collie |
| Barreras | Access · llave SSH · emparejamiento | Red privada · emparejamiento |
| Dominio propio | Sí, para el hostname del túnel | No: uso el nombre que da Tailscale (*.ts.net) |
| Además | Nada más | Pi-hole fuera de casa y salida a internet por mi casa |
Parte 2 · El motivo
ProxyCommand y LocalForward. Para la tablet no valía la pena.| Condición de la v1 | En la v2 |
|---|---|
| Ningún puerto entrante, ni en el router ni en el notebook | Se 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 computador | Se 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 web | Cambió. La única vía es la red privada, y Collie ya no necesita SSH. |
| Usar la llave privada que ya tengo en el celular | Ya no hace falta. Cada dispositivo genera su propia llave WireGuard, que nunca sale del equipo. |
Parte 3 · La idea
Collie instalado como app. Cada equipo tiene su propia llave.
Solo mis dispositivos se ven entre sí. Desde internet, ninguno existe.
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
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.
localhost.Parte 5 · Defensa en profundidad
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.
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.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".Parte 6 · El resultado
| Dispositivo | Qué hace en la red privada |
|---|---|
| Notebook (Pop!_OS) | Corre herdr y Collie. Publica Collie con tailscale serve. |
| Raspberry Pi | Pi-hole con Unbound: el DNS de todos los dispositivos. También es la salida a internet cuando la activo. |
| Celular | Collie instalado. Administra la Raspberry. |
| Tablet | Collie instalado. |
Parte 7 · Paso a paso
Los nombres de equipo, las direcciones y los identificadores son ejemplos. Reemplázalos por los tuyos.
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.
curl -fsSL https://tailscale.com/install.sh | sh
sudo tailscale up
sudo tailscale set --operator=$USERCollie necesita HTTPS para instalarse como app y enviar notificaciones, y collie start se detiene con un error si los certificados están apagados.
tailscale status --json | grep -A2 CertDomains
# "pc.tu-tailnet.ts.net"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í.
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 servecollie 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:8787https://pc.tu-tailnet.ts.net en el navegador.collie pair y escribir el código en Settings → Paired devices.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.
collie pair # código de 8 caracteres, válido por 10 minutos
collie devices list
collie devices revoke "<dispositivo-viejo>"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.
sudo tailscale up --accept-dns=false
sudo pihole-FTL --config dns.listeningMode ALLtailscale dns status | grep -A1 Resolvers
# - <ip-tailscale-de-la-raspberry>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.
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-nodeDetuve 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.
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 # inactiveParte 8 · Más cosas a considerar
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.
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.
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.
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.
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.
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
| Prueba | Resultado esperado |
|---|---|
tailscale serve status en el notebook | https://pc.tu-tailnet.ts.net (tailnet only) hacia 127.0.0.1:8787 |
ss -tln en el notebook | Collie sigue en 127.0.0.1:8787; nada nuevo en 0.0.0.0 |
| Abrir la página desde otro equipo de la red | 200, con un certificado válido de Let's Encrypt |
| La API desde un equipo sin emparejar | device not paired |
| Abrir la página con Tailscale apagado | No conecta |
tailscale status | Las conexiones aparecen como direct |
tailscale dns status en el notebook | Resolvers: la IP de Tailscale de la Raspberry |
systemctl is-active cloudflared | inactive |
Parte 10 · Para reforzar
La solución funciona tal como está. Estas son formas de sumar barreras, por si quiero endurecerla más, de mayor a menor impacto.
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.
{
"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"]},
],
}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.
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.
Por defecto, un dispositivo emparejado no vence. Con un plazo, un dispositivo olvidado pierde el acceso solo: collie devices set-expiry <dispositivo> 30d.
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
collie devices revoke <dispositivo>Cada paso cierra una barrera distinta, así que conviene hacer los dos.
Parte 12 · Para seguir leyendo