Infraestructura · Sistemas Distribuidos

Clúster proxy multi-nodo
con autenticación híbrida

WaterfallBungeeCordRedisPostgreSQLLuckPermsTOTPMojang APIJavaPterodactylProxmox

El objetivo era montar una infraestructura de red de juego con comportamiento de proxy de capa de aplicación: un nodo de entrada por región que enruta tráfico hacia múltiples backends segregados por función, con control centralizado de sesiones, autenticación propia y capa de datos compartida entre regiones.

La red operó en dos regiones simultáneas — Europa y Latinoamérica — cada una con su propio proxy Waterfall y conjunto de nodos backend independientes, pero compartiendo la misma base de datos PostgreSQL y el mismo estado de permisos LuckPerms. Un jugador podía cambiar de región manteniendo su rango, inventario y sesión intactos.

Cada región corre en su propio hardware gestionado desde Pterodactyl sobre Proxmox, con contenedores Docker aislados por nodo. La capa de datos (PostgreSQL y Redis) es compartida entre regiones, actuando como fuente de verdad única para cuentas, permisos y tienda.

REGIÓN EU · Europa

PROXY Waterfall EU :25565 · auth · routing
A Lobby
B Skywars
C Minekoro
D FFA
E Eventos
↕ datos compartidos

REGIÓN LATAM · Latinoamérica

PROXY Waterfall LATAM :25565 · auth · routing
A Lobby
B Skywars
C Minekoro
D FFA
E Eventos
↕ estado compartido entre regiones
CACHÉ Redis sesiones autenticadas · cache cross-node
PERSISTENCIA PostgreSQL cuentas · permisos LuckPerms · tienda

El servidor corre en modo offline a nivel de Waterfall para poder interceptar la autenticación antes de delegarla. El plugin propio en Java implementa tres ramas:

Premium Mojang Session API
  1. Cliente inicia sesión contra los servidores de Mojang (online mode local)
  2. El plugin consulta sessionserver.mojang.com/session/minecraft/hasJoined
  3. Respuesta positiva → UUID canónico extraído del perfil JSON
  4. Sesión creada en Redis con TTL de 30 min y UUID como clave primaria
  5. Cliente redirigido al hub sin pantalla de login
Offline Credenciales propias
  1. Cliente detectado como offline (UUID v3 basado en nombre)
  2. Plugin bloquea al hub y fuerza pantalla de login
  3. Credenciales introducidas en chat cifrado (canal privado de plugin)
  4. Contraseña verificada contra hash BCrypt almacenado en base de datos
  5. Sesión creada en Redis; cliente desbloqueado hacia el hub
2FA TOTP — RFC 6238
  1. Segundo factor activable por usuario desde panel de comandos
  2. Generación de secreto TOTP (Base32) vinculado a la cuenta
  3. Compatible con cualquier autenticador estándar (Google Authenticator, Aegis…)
  4. Código de 6 dígitos requerido tras la contraseña en cada sesión
  5. Ventana de validación de ±30 s con tolerancia de drift de un intervalo

Redis actúa como capa de estado compartido entre el proxy y todos los nodos backend. Cada sesión autenticada escribe una clave con el UUID del jugador y metadatos: estado de auth, tipo de cuenta, timestamp, nodo actual.

Estructura de clave en Redis

session:<uuid> → Hash
  ├── authenticated  : "true"
  ├── account_type   : "premium" | "offline"
  ├── 2fa_verified   : "true" | "false"
  ├── current_node   : "lobby" | "skywars" | "minekoro" | "ffa" | "eventos"
  ├── login_ts       : <unix timestamp>
  └── ip             : <ip de origen>

TTL: 1800s (renovado on activity)
Revocación: DEL session:<uuid> en disconnect

Al cambiar de nodo, el backend destino consulta Redis antes de cargar al jugador, evitando que un cliente no autenticado pueda acceder directamente al puerto del nodo aunque lo conozca por otros medios.

El sistema de permisos se gestiona con LuckPerms configurado en modo de almacenamiento PostgreSQL, lo que permite que todos los nodos compartan el mismo estado de permisos en tiempo real sin necesidad de sincronización manual.

Cada nodo arranca con LuckPerms apuntando a la misma base de datos central. Un cambio de rango aplicado desde cualquier nodo, o desde la consola del proxy, se refleja de forma inmediata en el resto del clúster.

Jerarquía de grupos (extracto)

default          → permisos base (todos los jugadores)
  vip            → acceso a comandos cosméticos, prioridad de cola
    vip+         → slots reservados, kits adicionales
      staff      → moderación, /kick, /mute, /ban (con log)
        admin    → acceso total + gestión de nodos

Los rangos de la tienda (VIP, VIP+) se asignan automáticamente vía la integración con el sistema de compras, sin intervención manual del equipo de administración.

La tienda externa se integra directamente con el clúster mediante un sistema de entrega automática en juego: el jugador completa la compra en la web y recibe los ítems o el rango sin intervención del equipo.

Flujo Proceso de compra → entrega
  1. Jugador realiza la compra en la tienda web (Tebex / panel propio)
  2. Tienda dispara un webhook HTTP hacia el listener del proxy
  3. Listener valida la firma del webhook y extrae UUID + producto comprado
  4. Si el jugador está conectado: entrega inmediata vía comando en el nodo actual
  5. Si está desconectado: entrega encolada en PostgreSQL, se ejecuta al siguiente login

Los productos se mapean a acciones configurables: ejecución de comandos con privilegios, asignación de grupos en LuckPerms, entrega de ítems en inventario o modificación de metadatos en la base de datos del jugador. La cola de entregas pendientes garantiza que ninguna compra se pierde ante desconexiones.

Tabla de entregas pendientes (PostgreSQL)

pending_deliveries
  ├── id          SERIAL PRIMARY KEY
  ├── player_uuid UUID NOT NULL
  ├── product_id  VARCHAR(64) NOT NULL
  ├── commands    JSONB          -- lista de comandos a ejecutar
  ├── purchased_at TIMESTAMPTZ
  └── delivered_at TIMESTAMPTZ  -- NULL hasta ejecución

Toda la infraestructura de procesos corre sobre Pterodactyl Panel, desplegado en una VM dedicada en Proxmox. Pterodactyl gestiona:

  • Arranque, parada y reinicio de cada nodo de forma independiente
  • Límites de recursos por contenedor (CPU, RAM, swap, disco)
  • Acceso a consola en tiempo real con logs por nodo
  • Gestión de backups programados del mundo y la base de datos
  • Control de usuarios con permisos granulares por servidor

El acceso al panel está restringido por IP y protegido con Caddy como reverse proxy HTTPS, con certificado emitido por Let's Encrypt.