chore(issues): close 0167+0168+0169+0170 livekit hardening bundle

VPS commit: 8eef89b (egutierrez/element_matrix_chat)

- 0167: STUN leak fixed (use_external_ip:false + node_ip hardcoded)
- 0168: UDP range expanded 50000-50200 -> 50000-50500
- 0169: API secret rotated (old key LK44e009c6e92b -> new LK5f6b38bb)
- 0170: livekit.example.yaml refreshed + header comments cleaned

Verification:
- 0 STUN packets to Google during restart (tcpdump 60s window)
- Endpoint /livekit/sfu/ HTTP 200
- LiveKit logs: nodeIP=135.125.201.30, portICERange=[50000,50500]
- Containers livekit + livekit-jwt healthy

New secret stored in pass: matrix/livekit-secret-rotation-2026-05-25
This commit is contained in:
2026-05-25 00:44:15 +02:00
parent 22692c1ed2
commit 70ec825e32
4 changed files with 0 additions and 0 deletions
@@ -0,0 +1,62 @@
---
id: "0167"
title: "Eliminar STUN leak a Google en LiveKit (hardcode external_ip)"
status: pendiente
type: infra
domain:
- matrix
scope: app:element_matrix_chat
priority: baja
depends: []
blocks: []
related: ["0166"]
created: 2026-05-24
updated: 2026-05-24
tags: [matrix, livekit, privacy, stun]
---
# 0167 — Eliminar STUN leak a Google en LiveKit (hardcode external_ip)
**Status:** pendiente
**Created:** 2026-05-24
**Type:** infra
**Priority:** baja
**Domain:** matrix
**Scope:** app:element_matrix_chat
**Depends:**
**Blocks:**
## Problema
`rtc.use_external_ip: true` con `external_ip` vacio → LiveKit hace STUN query a `stun.l.google.com:19302` cada arranque para descubrir IP publica. Leak metadata server (IP del VPS) a Google. Contradice premisa "self-host privacy first".
## Objetivo
LiveKit conoce su IP publica sin contactar STUN externos.
## Plan
1. Determinar IP publica VPS: `curl -s ifconfig.me`.
2. Editar `configs/livekit/livekit.yaml`:
```yaml
rtc:
use_external_ip: false
node_ip: "<IP_PUBLICA>"
```
3. Si TURN propio desplegado (issue 0166), usar coturn como STUN propio.
4. Restart `element_matrix_chat-livekit-1`.
5. Test: call funciona igual.
6. Auditar: `docker logs element_matrix_chat-livekit-1 | grep -i stun` no muestra queries a google.
## Acceptance
- [ ] `tcpdump -i eth0 dst stun.l.google.com` no captura paquetes tras restart.
- [ ] Calls Element Call siguen funcionando 1:1 y grupo.
## Definition of Done
- [ ] Repetibilidad: reboot VPS, 0 paquetes a stun.l.google.com.
- [ ] Observabilidad: log LiveKit confirma IP hardcoded.
## Notas
Bajo impacto operacional pero alta consistencia con doctrina self-host. Si IP del VPS cambia (rara vez con VPS estatico), actualizar config manual o automatizar con script de healthcheck.
@@ -0,0 +1,58 @@
---
id: "0168"
title: "Ampliar UDP range LiveKit de 200 a 500 ports"
status: pendiente
type: infra
domain:
- matrix
scope: app:element_matrix_chat
priority: baja
depends: []
blocks: []
related: ["0166"]
created: 2026-05-24
updated: 2026-05-24
tags: [matrix, livekit, scaling, webrtc]
---
# 0168 — Ampliar UDP range LiveKit de 200 a 500 ports
**Status:** pendiente
**Created:** 2026-05-24
**Type:** infra
**Priority:** baja
**Domain:** matrix
**Scope:** app:element_matrix_chat
**Depends:**
**Blocks:**
## Problema
LiveKit configurado con `port_range_start: 50000`, `port_range_end: 50200` (200 ports UDP). Cada participante usa ~2 ports → cap **~100 participantes concurrentes** sumando TODAS las calls del server. OK para uso personal hoy, justo si se anaden grupos simultaneos o reuniones >10 personas.
## Objetivo
Sostener al menos 250 participantes concurrentes sin port exhaustion.
## Plan
1. Editar `configs/livekit/livekit.yaml`: `port_range_end: 50500`.
2. Actualizar `docker-compose.yml` para exponer rango ampliado (300 puertos UDP adicionales).
3. Abrir rango en firewall VPS (UFW/iptables).
4. Restart stack LiveKit.
5. Smoke test: call funciona.
## Acceptance
- [ ] `docker port element_matrix_chat-livekit-1` muestra 50000-50500 UDP.
- [ ] `ss -lun | grep -c "0.0.0.0:50"` >= 500 tras restart.
- [ ] Call test OK.
## Definition of Done
- [ ] Repetibilidad: stack reinicia limpio.
## Notas
`docker-compose.yml` actualmente lista los 200 ports uno a uno (verboso pero explicito). Considerar usar sintaxis `"50000-50500:50000-50500/udp"` para legibilidad.
NO incrementar a >1000 sin medir consumo memoria LiveKit — cada port asignado tiene overhead minimo pero acumula.
@@ -0,0 +1,60 @@
---
id: "0169"
title: "Rotar LIVEKIT_SECRET (expuesto en sesion auditoria)"
status: pendiente
type: bugfix
domain:
- matrix
scope: app:element_matrix_chat
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-24
updated: 2026-05-24
tags: [matrix, livekit, security, secret-rotation]
---
# 0169 — Rotar LIVEKIT_SECRET (expuesto en sesion auditoria)
**Status:** pendiente
**Created:** 2026-05-24
**Type:** bugfix
**Priority:** alta
**Domain:** matrix
**Scope:** app:element_matrix_chat
**Depends:**
**Blocks:**
## Problema
Durante auditoria 2026-05-24 (sesion Claude), `docker inspect element_matrix_chat-livekit-jwt-1` volco `LIVEKIT_SECRET=b00e98f70722bc...` cleartext en stdout de la sesion. Aunque la sesion es del operador, el secret quedo en log de conversacion + potencialmente en backups del log + transcripts. Rotacion necesaria por higiene.
## Objetivo
Nuevo secret 32 bytes hex, mismo `api_key` (o regenerar ambos), stack restart sin perdida sesion.
## Plan
1. Generar nuevo secret: `openssl rand -hex 32`.
2. Editar `configs/livekit/livekit.yaml` → bloque `keys:` con nuevo valor.
3. Editar `.env` de docker-compose (var `LIVEKIT_SECRET` consumida por `livekit-jwt`).
4. Restart `element_matrix_chat-livekit-1` y `element_matrix_chat-livekit-jwt-1` en orden.
5. Test call Element Call → handshake JWT OK.
6. Guardar secret antiguo + nuevo en `pass` con timestamp rotacion.
## Acceptance
- [ ] `docker inspect ... --format "{{.Config.Env}}"` muestra secret nuevo.
- [ ] Element Call inicia call sin error "invalid token".
- [ ] Entry `pass matrix/livekit-secret` actualizada.
## Definition of Done
- [ ] Repetibilidad: rotacion documentada como funcion del registry (candidato `livekit_secret_rotate_bash_infra`).
- [ ] Observabilidad: rotation log con timestamp.
## Notas
Considerar promover el procedimiento a funcion del registry: `livekit_secret_rotate_bash_infra(ssh_host, compose_dir)` que automatiza pasos 1-5 y guarda en pass via `gpg_pass_write`.
Patron similar para otros secrets del stack (Synapse macaroon, MAS encryption key, postgres passwords) → capability group nuevo `secret-rotation`.
@@ -0,0 +1,55 @@
---
id: "0170"
title: "Renombrar livekit.example.yaml -> livekit.yaml en bind mount"
status: pendiente
type: chore
domain:
- matrix
scope: app:element_matrix_chat
priority: baja
depends: []
blocks: []
related: []
created: 2026-05-24
updated: 2026-05-24
tags: [matrix, livekit, hygiene]
---
# 0170 — Renombrar livekit.example.yaml -> livekit.yaml en bind mount
**Status:** pendiente
**Created:** 2026-05-24
**Type:** chore
**Priority:** baja
**Domain:** matrix
**Scope:** app:element_matrix_chat
**Depends:**
**Blocks:**
## Problema
`configs/livekit/livekit.yaml` mantiene los comentarios "Copy this file..." del template original. Funciona pero confunde: parece config sin completar. El bind mount apunta directo a este archivo, asi que renombrar limpiamente el archivo template y mantener `livekit.yaml` limpio para mantenimiento.
## Objetivo
`livekit.yaml` limpio sin comentarios de "example", `livekit.example.yaml` separado como referencia template inicial en repo.
## Plan
1. Crear `configs/livekit/livekit.example.yaml` con plantilla limpia (placeholders).
2. Eliminar comentarios "Copy this file..." del `livekit.yaml` actual.
3. Verificar `.gitignore` cubre `livekit.yaml` real pero no `livekit.example.yaml`.
4. Commit en `egutierrez/element_matrix_chat`.
## Acceptance
- [ ] `head -3 configs/livekit/livekit.yaml` NO menciona "example".
- [ ] `configs/livekit/livekit.example.yaml` versionado.
- [ ] Stack restart sin cambios funcionales.
## Definition of Done
- [ ] PR mergeado en `dataforge/element_matrix_chat`.
## Notas
Tarea de higiene puro. Cero impacto runtime. Mejora onboarding futuro si otro operador clona el repo.