Articulo

Controlar tu PC sin exponerlo: modelo pull con agent + cola (patron seguro y pragmatico)

En vez de abrir puertos en tu PC, montas un control plane en tu servidor y haces que el PC sea quien llame: poll a una cola, lease de trabajos y autenticacion por scopes.

Muchas veces quieres que tu PC haga algo bajo demanda (ejecutar una tarea, generar un fichero, arrancar un proceso, etc.), pero no quieres exponer tu PC a Internet abriendo puertos, VPNs mal configuradas o inventos con reenvio de puertos.

La alternativa mas simple y segura a nivel operativo es cambiar el sentido del trafico: en lugar de que el servidor llame a tu PC (push), haces que el PC llame al servidor (pull) y se quede esperando trabajo.

Este post describe el patron completo que monte en `d3v.es`: cola en el servidor, agent en Windows que hace polling, y una UI web para disparar tareas y ver historico. No hace falta saber el caso de uso concreto; lo importante es la arquitectura.

1) El problema real: por que no quieres abrir tu PC

Exponer un servicio en un PC domestico suele ser mala idea: cambia la IP, hay NAT/CGNAT, el router mete su propia complejidad, y cualquier endpoint publico se convierte en superficie de ataque.

Incluso si lo haces bien, el mantenimiento operativo es pesado: certificados, hardening, logs, rate limits, rotacion de credenciales, y el riesgo de dejar algo abierto sin darte cuenta.

El modelo pull reduce esto drasticamente: tu PC hace solo conexiones salientes hacia un servidor que ya tienes endurecido y monitoreado.

2) Push vs Pull (la idea clave)

Push (lo tipico): tu servidor intenta conectar con tu PC. Necesitas el PC accesible desde fuera (puerto, VPN, tunnel, reverse proxy, etc.).

Pull (este patron): tu PC hace polling a un endpoint en tu servidor. Si hay trabajo, lo reclama, ejecuta y reporta estado.

En pull, el PC puede estar detras de NAT y seguir funcionando igual, porque todo es outbound.

# Pull: el PC pregunta si hay trabajo
GET  /api/agent/jobs/next

# Si hay job: el PC lo reclama (lease) y pide payload
GET  /api/agent/jobs/{id}/payload

# El PC reporta progreso/final
POST /api/agent/jobs/{id}/ack  { status, message, result }

3) Componentes del sistema

  • Control plane (servidor): UI para humanos + API para crear trabajos.
  • Job store (servidor): cola persistente (en mi caso, un JSON) con retencion y estados.
  • Agent (PC): proceso en segundo plano que hace poll, ejecuta y reporta.
  • Observabilidad: pagina de cola/historico, contadores (queued/active/done/failed) y mensajes de error.

4) Diseno de la cola: estados, lease y reintentos

Una cola no es solo 'una lista'. Si quieres que sea robusta, necesitas al menos: estados, lease (para evitar que el mismo job lo cojan dos veces), reintentos y retencion de historico.

En `d3v.es` el ciclo de vida es: `queued` -> `assigned` -> `downloading`/`running` -> `done` o `failed`. El nombre exacto da igual; la idea es que el servidor pueda distinguir entre trabajos pendientes, en curso y finalizados.

El lease es clave: si el agent se cae en mitad, el job no queda bloqueado para siempre. Cuando expira el lease, otro poll puede retomarlo.

Tambien hay deduplicacion basica: si intentas encolar el mismo trabajo mientras sigue activo, el sistema devuelve el existente en lugar de crear duplicados.

# Parametros tipicos (en variables de entorno)
AGENT_LEASE_SECONDS=180
JOBS_RETENTION_DAYS=21
JOBS_MAX_LIST=400

# Comportamiento
- queued: disponible
- assigned: reservado por agent X hasta leaseUntil
- running: progreso reportado por agent
- done/failed/cancelled: terminales, se prunan por retencion

5) Seguridad por capas: humanos vs maquinas (scopes)

El error comun es usar una sola credencial para todo. Aqui separo claramente dos superficies: lo que hacen humanos desde navegador, y lo que hace el agent desde el PC.

Humano (UI): autenticacion fuerte via OAuth y allowlist. Esto protege la UI y las APIs administrativas (crear trabajos, listar, limpiar historico).

Maquina (agent): autenticacion por bearer token largo (secreto compartido) y cabecera de agent-id. Este token solo sirve para endpoints de agent; no da acceso a admin ni a otras rutas.

Importante: el endpoint del agent se deja fuera del middleware de OAuth, pero valida su propia autenticacion en el handler. Esto evita mezclar mecanismos y simplifica el agent.

  • OAuth + allowlist para UI (cero secretos en el navegador).
  • Bearer key para agent (secreto guardado en disco con ACL).
  • Separacion de listas: `/4dm1n` puede ser mas estricto que una seccion tipo `/plex`.
  • Rate limit / backoff en el agent para no martillear el server cuando no hay trabajo.

6) Operacion en Windows: autoarranque y configuracion segura

El agent tiene que sobrevivir reinicios. La forma mas pragmatica (y compatible con unidades por letra) es que arranque al iniciar sesion del usuario.

La configuracion se guarda en un fichero (por ejemplo `agent.env`) y se protege con ACL para que solo el usuario (y administradores) puedan leerlo.

El agent hace poll con dos ritmos: lento cuando no hay trabajo (idle) y rapido cuando esta procesando (busy), con jitter para evitar sincronizacion entre multiples agentes.

# Ejemplo de config local del agent (conceptual)
BASE_URL=https://d3v.es
AGENT_KEY=<secreto_largo>
AGENT_ID=pc-main
POLL_IDLE_MS=45000
POLL_BUSY_MS=4000
POLL_ERROR_MS=30000

7) Fallos esperables y como se recupera el sistema

  • PC apagado: jobs quedan `queued` y se procesan cuando vuelva.
  • PC se cae a mitad: lease expira y el job se reintenta.
  • Servidor reinicia: cola persiste (store en disco) y el agent reanuda por polling.
  • Credencial comprometida: rotas `AGENT_KEY` (nuevo secreto) y reinstalas/actualizas el agent.
  • Crecimiento de historico: prune por dias o por max lista.

8) Checklist rapido (si quieres montarlo tu)

  • 1. Decide donde vive el control plane (tu servidor publico).
  • 2. Define endpoints de agent separados de endpoints admin.
  • 3. Implementa store persistente + lease + estados.
  • 4. Autentica humanos con OAuth/allowlist; autentica agent con key independiente.
  • 5. En el PC: agent en autoarranque, config con ACL, backoff/jitter.
  • 6. Crea UI/endpoint para ver cola e historico (te ahorra entrar por SSH).

Este patron es simple, pero extremadamente efectivo: reduces superficie de ataque, eliminas dependencias raras de red domestica y mantienes control y trazabilidad desde tu servidor.

Lo mas importante es la separacion de responsabilidades (humanos vs agent) y el lease de trabajos. Con eso, el sistema aguanta reinicios y caidas sin inventos.

Si te interesa, el siguiente paso natural es soportar multiples tipos de jobs, multiples agentes y firmas de payload para endurecer aun mas la integridad de los trabajos.