Fallbacks de sincronización de tiempo — cuando el lab no puede alcanzar una fuente NTP pública¶
Lee en tu idioma: English · Português · Español
Estado del alcance (post-congelación de alcance 2026-05-10) — Ver ARCHITECTURE.md para los 37 MÓDULOs canónicos + 7 Test Kinds + arquitectura de safety DOM/CPOS/PIE-PA. ADRs 0014, 0019-0025 cubren adiciones post-Freeze. Este documento complementa
TIME_SYNC.es.md. Lee primero el documento principal — eso es lo que todo operador necesita. Este archivo cubre los casos más difíciles:
- El lab está air-gapped a nivel UCS / Nexus / NGFW
- Pero UN elemento (típicamente el Cloner) tiene un camino hacia internet pública mediante un enlace ISP o proxy
- O ningún elemento tiene internet, y el operador quiere usar el laptop que accede al Dashboard como fuente de tiempo
Tres opciones, ordenadas por confiabilidad¶
| # | Opción | ¿Confiable? | Cuándo usar |
|---|---|---|---|
| 1 | Appliance stratum-1 GPS-disciplined + lab stratum-2 | ✅ forensic-grade | Instalaciones clasificadas, engagements de larga duración, cumplimiento regulatorio |
| 2 | Relay NTP TLSStress.Art corriendo en un nodo con internet (p. ej. host del Cloner) | ✅ bueno para la mayoría de labs | Air-gap a nivel UCS, pero al menos un nodo puede alcanzar NTP público vía un enlace ISP |
| 3 | Fallback de reloj del navegador (laptop del operador) | 🟡 NOT forensic | Último recurso. Solo timing relativo a la ejecución; no cites los timestamps resultantes en informes |
La Opción 1 es el estándar de oro y está documentada en TIME_SYNC.es.md. Este archivo se enfoca en las Opciones 2 y 3.
Opción 2 — Relay NTP TLSStress.Art¶
Un pequeño deployment Kubernetes que ejecuta chronyd en un nodo elegido, sincroniza con NTP público mediante el camino de internet de ese nodo y sirve tiempo en UDP/123 a todos los demás elementos del lab (hosts UCS, Nexus 9000, NGFW DUT) sobre la red de gestión.
Cuándo encaja¶
- El Cloner está en un nodo con camino ISP (porque el Cloner necesita clonar sitios públicos reales)
- Los demás hosts UCS, Nexus y NGFW están todos aislados de internet pública
- Quieres que hereden un reloj razonable sin comprar hardware GPS
Arquitectura¶
Public NTP servers
(time.cloudflare.com,
time.google.com,
pool.ntp.org)
│
▼
[ Node X — has ISP path ]
chronyd (relay)
Pod hostNetwork=true
Listens on UDP/123 of Node X's host IP
│
▼
┌────────────────────────────┐
│ Lab management network │
│ 192.168.90.0/24 │
└────────────────────────────┘
│ │ │
▼ ▼ ▼
UCS host(s) Nexus 9000 NGFW DUT
(chrony) (NTP client) (NTP client)
El nodo del relay es el "ancla stratum 2" del lab. Todos los demás elementos apuntan a él.
Configuración¶
Paso 1 — elige y etiqueta el nodo del relay.
# The node must already have a route to public NTP via its primary interface.
# Verify before applying:
ssh <relay-node>
chronyc -h time.cloudflare.com sources # if chrony is preinstalled
# or just:
nc -uv time.cloudflare.com 123 < /dev/null
# expect "succeeded" / "open"
# Then label the node:
kubectl label node <relay-node-name> tlsstress.art/ntp-relay=true
Paso 2 — aplica el manifiesto del relay.
kubectl apply -f k8s/optional/ntp-relay.yaml
Esto crea:
- Un namespace tlsstress-ntp-relay (PSA: privileged — requerido para la capability SYS_TIME)
- Un ConfigMap con chrony.conf
- Un Deployment que corre en el nodo etiquetado con hostNetwork: true y expone UDP/123 en la IP primaria del host
- Un Service headless para discovery dentro del cluster
Paso 3 — apunta los elementos del lab al relay.
En cada uno de los demás hosts UCS (/etc/chrony/chrony.conf):
server <relay-node-host-ip> iburst prefer minpoll 4 maxpoll 6
makestep 1.0 3
rtcsync
sudo systemctl restart chronyd
sudo chronyc waitsync 30 0.05
En el Cisco Nexus 9000:
configure terminal
ntp server <relay-node-host-ip> prefer
ntp source-interface mgmt0
end
write memory
show ntp peer-status
En el NGFW DUT — específico del fabricante. Para Cisco FTD/ASA:
configure terminal
ntp server <relay-node-host-ip>
write memory
show ntp associations
Para Palo Alto:
set deviceconfig system ntp-servers primary-ntp-server-address <relay-node-host-ip>
commit
Para Fortinet:
config system ntp
set ntpsync enable
set type custom
config ntpserver
edit 1
set server <relay-node-host-ip>
end
end
end
Paso 4 — verifica.
# In Grafana, open: TLSStress.Art — Time Sync Status
# Confirm all lab hosts show green (drift < 100 ms)
# From any UCS:
./scripts/check-time-sync.sh --strict
# expect exit 0
Trade-offs¶
✅ Pros: - Sin hardware adicional - Reusa un nodo ya conectado a internet (el host del Cloner) — sin superficie de ataque extra - Recuperación automática: si el nodo del relay reinicia, resincroniza y el lab sigue - Defendible forensemente — la fuente NTP de cada host queda registrada, el audit log muestra la cadena
⚠️ Contras:
- Single point of failure — si el nodo del relay pierde internet, los relojes del lab van lentamente a drift (chrony mantiene el offset previo por horas, no días)
- Requiere claves de autenticación NTP para confianza production-grade (sin claves, cualquiera en la red de gestión puede responder consultas NTP; es un ataque de bajo esfuerzo)
- El nodo del relay ahora tiene un DaemonSet privilegiado (capability SYS_TIME) — riesgo ligeramente elevado
Hardening — añade autenticación NTP (opcional pero recomendado):
Edita el ConfigMap chrony.conf para añadir una clave compartida que los clientes del lab también usen:
keyfile /etc/chrony/keys
authselectmode require
Luego monta el archivo de clave vía un Secret. Documentación sobre esto está en docs/TIME_SYNC.es.md#authentication (mejora futura).
Opción 3 — Fallback de reloj del navegador (NOT forensic)¶
⚠️ Lee con atención. Esta opción está documentada porque los operadores la piden, pero acarrea reservas serias. El Dashboard no se entrega con esto habilitado por defecto.
Qué haría¶
El operador abre el Dashboard desde su laptop. El Dashboard lee Date.now() del navegador, lo postea a un endpoint backend privilegiado, que entonces ejecuta un DaemonSet privilegiado en todos los nodos para ajustar (step) sus relojes al tiempo del navegador. Esto hace que todo el lab quede "sincronizado con el laptop del operador".
Por qué esto es riesgoso¶
| Riesgo | Explicación |
|---|---|
| Los relojes del navegador son poco confiables | El laptop del operador puede estar sincronizado vía NTP, o puede estar desfasado por minutos (proxy corporativo interceptando NTP, VM con reloj virtual pausado, hotspot móvil, hardware con jet lag). El Dashboard no puede saberlo. |
| Operadores distintos fijan tiempos distintos | Si dos ingenieros acceden al lab en días distintos desde laptops distintos, el reloj del lab camina. La comparación cross-engagement se rompe silenciosamente. |
| Superficie de ataque privilegiada | Un endpoint POST que fija el tiempo del sistema en cada nodo es un blanco de alto valor. Sesión de navegador comprometida = tiempo comprometido en todos los hosts. |
| Sin valor forense | Los timestamps escritos bajo este esquema no pueden citarse en procedimientos legales, registros regulatorios o comparaciones de benchmark de fabricantes. Son "tiempo relativo solamente". |
| Esconde el problema real | Si el lab no puede alcanzar NTP, el operador debería arreglar esa causa raíz (Opción 2 o GPS), no encubrirla. |
Cuándo es aceptable¶
Solo cuando TODO lo siguiente sea verdadero:
1. Ningún nodo del lab tiene camino alguno hacia una fuente NTP pública o privada
2. El engagement es estrictamente interno — no de cara al cliente, no para ningún informe que vaya a salir de la sala
3. El operador acepta explícitamente la reserva "non-forensic" y firma el audit log
4. Los resultados de esta ejecución se marcarán en la base de datos como time_source = browser_fallback y se filtrarán de las consultas de comparación cross-engagement
Lo que construimos (y lo que NO construimos)¶
Construimos una versión con guarda del lado de petición de este flujo:
- El Dashboard expone
POST /api/time-sync/set-from-browser(solo admin). Acepta{ browserTimestampMs, browserTimezone, browserOffsetMinutes, acknowledgement: "NOT_FORENSIC" }y rechaza si el tiempo declarado por el navegador está más de ±24 h fuera del tiempo actual estimado por el servidor. - NO ejecuta automáticamente un cambio de reloj. En su lugar, escribe una línea de auditoría (con
forensic_grade: false) y devuelve el comando exactokubectl exec ... chronyc settimepara que el operador lo ejecute en el control plane del cluster. - La admin UI en
/admin/time-syncarma la petición, exige un checkbox explícito "Reconozco que esto NO es forensic" y muestra el comando manual. Verdashboard/src/app/api/time-sync/set-from-browser/route.ts.
NO construimos (y no vamos a construir) el camino de auto-ejecución:
- El costo de implementación es alto (DaemonSet privilegiado + enforcement de audit log en el paso de aplicar)
- El riesgo operativo es real (foot-gun para ingenieros con prisa)
- La demanda real es baja (¿cuándo tendrías un laptop con acceso al Dashboard pero ni siquiera un nodo con internet para el relay? Casi nunca en la práctica)
- El camino actual (solo petición) es suficiente: el operador copia-pega un comando
kubectly es dueño del apply
Si tienes un caso de uso fuerte para el camino automatizado completo, abre un issue usando la plantilla de access-request. Lo evaluaremos caso por caso y podríamos construirlo como módulo opt-in con marcadores "non-forensic" hard-coded en las ejecuciones resultantes.
Lo que puedes hacer hoy en lugar de esto¶
- Usa el flujo de la admin UI arriba — abre
/admin/time-sync, acepta el caveat NOT_FORENSIC, copia el comandokubectl chronyc settimeretornado, ejecútalo en el control plane. La línea de auditoría se escribe automáticamente conforensic_grade: falseytime_source = browser_fallback. -
Fija el tiempo manualmente en cada host al instalar por primera vez, usando tu laptop como referencia pero documentándolo:
Esto es honesto sobre ser non-forensic — el operador registra el ajuste manual en las notas del engagement.# On the laptop: date -u # note the UTC time # On each UCS: sudo timedatectl set-time '2026-05-06 14:35:00 UTC' # within ~1s of the laptop's note # Then: sudo systemctl stop chronyd # disable NTP attempts that will fail -
Usa la Opción 2 con un teléfono USB-tetherizado como fuente de internet del nodo del relay si incluso el ruteo ISP está prohibido. Hotspot de teléfono a un nodo = un camino = suficiente.
Cómo el panel Grafana muestra esto¶
El dashboard TLSStress.Art — Time Sync Status (cargado automáticamente cuando se aplica la kustomization) muestra:
- Stat panel — clock skew por host: verde < 100 ms · amarillo < 1 s · naranja < 5 s · rojo > 5 s
- Stat panel — sync status por host: ✓ synced / ✗ NOT synced
- Stat panel — divergencia multi-host: max(offset) − min(offset) a través del cluster
- Time-series — drift en el tiempo por host: captura tendencias sostenidas o picos correlacionados con ejecuciones
- Stat panel — minutos desde la última actualización de sync por host: verde < 5 min · rojo > 30 min
Abre este dashboard antes de iniciar cualquier plan de pruebas. Si algo no está verde, arregla primero el time-sync — no ejecutes.
Relacionados¶
TIME_SYNC.es.md— lo básico que todo operador necesita saberAIRGAP_INSTALL.es.md— instalación air-gap, que es el escenario padre de la Opción 2MONITORING_TEST_VALIDITY.es.md— el framework más amplio de validity-alerts en el que los alertas de time-sync se enchufanUSAGE_POLICY.es.md— las restricciones de licencia se aplican igualmente a las ejecuciones hechas bajo cualquier opción de time-sync