# Relay SNI para `*.alumnos.codecrypto.dev` (Traefik delante + nginx + `ssh -R`)

**Todo** lo que cuelgue de `<usuario>.alumnos.codecrypto.dev` (p. ej. `lista.jviejo.alumnos.codecrypto.dev`,
`a.b.jviejo.alumnos.codecrypto.dev`) llega a **un único túnel por alumno**; el reparto por nombre lo hace el
Traefik del propio alumno. El usuario es `[a-z][a-z0-9]{2,19}`. Solo `.dev` (no `.academy`).
No entran al relay: `<usuario>.alumnos...` (sin etiqueta delante) ni `traefik.alumnos...`.

```
visitante ─TLS─► Traefik del VPS :443 ── otros dominios: routers de siempre
                    └─ SNI *.usuario.alumnos.codecrypto.dev ─(passthrough)─►
                       nginx-alumnos :9443 (stream + ssl_preread)
                          └─► /var/lib/alumnos/<usuario>/web.sock   (uno por alumno)
                                 ═ ssh -R (lo abre el alumno) ═►  127.0.0.1:443 del alumno (su Traefik, termina TLS)
```

## Ficheros
| Fichero | Dónde va |
|---|---|
| `traefik/alumnos.yml` | Config dinámica de Traefik del VPS (una regla TCP passthrough) |
| `nginx/`, `docker-compose.yml` | nginx del relay, en la misma red Docker que Traefik |
| `sshd/50-alumnos.conf` | `/etc/ssh/sshd_config.d/` del VPS |
| `bin/setup-vps.sh` | Preparación única (grupos, directorios, sshd) |
| `bin/alta-alumno.sh`, `bin/baja-alumno.sh` | Alta/baja de alumnos (solo clave **pública**) |
| `alumno/ssh_config.example`, `alumno/traefik-alumno.yml` | Lo que usa el alumno |

## Puesta en marcha en el VPS
1. DNS: `*.alumnos.codecrypto.dev  A  <IP del VPS>` (cubre varios niveles: `x.usuario.alumnos...`) y `tunnel.codecrypto.dev  A  <IP del VPS>`.
2. `sudo relay/bin/setup-vps.sh`
3. `TRAEFIK_NETWORK=<red de tu Traefik> docker compose -f relay/docker-compose.yml up -d --build`
4. Copiar `traefik/alumnos.yml` al directorio del file provider de Traefik (revisar `entryPoints`
   y la sintaxis v2/v3 de `HostSNIRegexp`).
5. `sudo relay/bin/alta-alumno.sh ana 'ssh-ed25519 AAAA... ana'`
6. El alumno abre el túnel (`ssh -N codecrypto-ana`, con `RemoteForward /var/lib/alumnos/ana/web.sock 127.0.0.1:<puerto>`) y su Traefik pide el certificado.

## Verificado en local (nginx 1.27 en Docker)
- `nginx -t` correcto.
- SNI `lista.ana…`, `a.b.c.ana…` llegan al mismo `/var/lib/alumnos/ana/web.sock`
  con el worker de nginx conectando por pertenecer al grupo `nginxsock` (socket 0660).
- SNI sin socket → 502 inmediato; `ana.alumnos…` sin etiqueta, `traefik.alumnos…`, `.academy`, mayúsculas y
  usuarios < 3 caracteres → destino `default`, cierre inmediato.
- OJO nginx: la regex con `{2,19}` va entre comillas, o `nginx -t` falla ("unexpected {").

## Verificado en el servidor real (alumnos-vm, Debian 12, OpenSSH 9.2, Traefik 3.7.16)
- Un solo socket (`web.sock`) del alumno `jviejo` atiende `lista.`, `otra.` y `a.b.` `jviejo.alumnos.codecrypto.dev`.
- La regex comodín no captura ninguno de los 111 nombres de la plataforma (probado contra sus routers reales).
- Cadena completa con un alumno de prueba (ya eliminado): `ssh -R` a socket Unix -> nginx -> Traefik
  (passthrough) -> el cliente recibe el certificado del alumno, no el de la plataforma.
- sshd: socket `srw-rw---- alumno nginxsock`, visible desde el contenedor nginx.
- Certificado real de Let's Encrypt (TLS-ALPN-01, staging y producción) para `lista.jviejo.alumnos.codecrypto.dev`
  emitido por el Traefik del alumno a través de toda la cadena; ver `alumno/edge/`.
- Los proyectos existentes (`*.alumnos.codecrypto.academy`) y el dashboard siguen respondiendo.
- Hallazgo: `AllowTcpForwarding no` rechaza TAMBIÉN el reenvío remoto por socket Unix; se usa
  `AllowTcpForwarding remote` + `PermitListen 127.0.0.1:1` (los TCP quedan rechazados).

## Pendiente
- DNS: `tunnel.codecrypto.dev` (el comodín `*.alumnos` ya resuelve a 35.255.164.51).
- Un certificado por nombre (TLS-ALPN-01): para muchos subdominios del mismo alumno, valorar un comodín por DNS-01.
- `limit_conn` por proyecto no es viable (el SNI aún no existe en esa fase de nginx).

## Límites de Let's Encrypt
Todos los certificados cuelgan de `codecrypto.dev`; con muchos alumnos puedes tocar el tope semanal
de certificados nuevos por dominio. Revisa los límites vigentes y valora ampliación, la Public Suffix
List para `alumnos.codecrypto.dev` u otra CA.
