feat(brahman-handshake+ente-zero): allowlist explicita de peers libp2p

Capa de politica sobre el trust criptografico de Fase 3. Hasta ahora
cualquier peer Ed25519-valido pasaba el handshake remoto; con
allowlist activa, solo los peers explicitamente listados. Aplica
unicamente al path libp2p — el path Unix sigue usando SO_PEERCRED
del kernel.

API nueva en brahman_handshake::peer_allowlist:
- PeerAllowlist::from_iter / from_file con AllowlistError tipado.
- Formato del archivo: PeerId base58 por linea, # comentarios (linea
  entera o inline), lineas vacias ignoradas. Errores de parseo
  reportan numero de linea.
- is_allowed, len, is_empty, iter.

Wire en el server:
- ServerConfig.allowlist: Option<PeerAllowlist>. None = modo abierto
  (compat). Some = solo los listados.
- Gate en do_handshake ANTES de la verificacion de firma — la
  comparacion BTreeSet O(log n) es mas barata que crypto, asi que
  rechazamos peers invalidos antes de gastar ciclos.
- HandshakeError::Unauthorized("peer X no esta en la allowlist").

Wire en Arje (ente-zero):
- Env var BRAHMAN_PEER_ALLOWLIST apuntando a un archivo.
- setup_brahman_allowlist carga al startup; degrada a None si el
  archivo falla (doctrina PID 1: no romper por subsistemas
  opcionales).

Activacion end-to-end:
  BRAHMAN_LISTEN_MULTIADDR=/ip4/0.0.0.0/tcp/4101 \\
  BRAHMAN_PEER_ALLOWLIST=/etc/brahman/allowlist.txt \\
  ente-zero

Tests: 6 unit en peer_allowlist + 1 E2E en network_libp2p
(libp2p_handshake_allowlist_admits_listed_rejects_others). 25 tests
verdes en brahman-handshake. Sin regresion en ente-zero.

Pendientes: denylist explicita, hot reload via SIGHUP/watch, aplicar
politica a nivel de swarm via libp2p_allow_block_list::Behaviour
para rechazar ANTES del Noise handshake.
This commit is contained in:
Sergio
2026-05-09 15:27:15 +00:00
parent 2e6afd0973
commit 505748dd41
8 changed files with 425 additions and 1 deletions
+72
View File
@@ -6,6 +6,78 @@ ratio/diff ver `git show <sha>`.
## 2026-05-09
### feat(brahman-handshake+ente-zero): allowlist explícita de peers libp2p
Capa de política sobre el trust criptográfico de Fase 3. Hasta ahora
cualquier peer con keypair Ed25519 válida pasaba el handshake remoto;
con allowlist activa, sólo los peers explícitamente listados. Aplica
únicamente al path libp2p — el path Unix sigue usando SO_PEERCRED
del kernel, que es autenticación de proceso local, no de red.
API nueva en `brahman_handshake::peer_allowlist`:
- `PeerAllowlist::from_iter([peer_id, ...])` para tests/inline.
- `PeerAllowlist::from_file(path)` parsea texto plano: un PeerId
base58 por línea, `#` para comentarios (línea entera o inline),
líneas vacías ignoradas. Errores de parseo incluyen número de
línea para debug rápido.
- `is_allowed(peer)`, `len()`, `is_empty()`, `iter()`.
- `AllowlistError { Io, InvalidPeerId }`.
Wire en el server:
- `ServerConfig.allowlist: Option<PeerAllowlist>`. `None` = modo
abierto (compat con todo lo anterior). `Some` = sólo los listados.
- Gate en `do_handshake` ANTES de la verificación de firma — la
comparación O(log n) en BTreeSet es más barata que crypto, así
que rechazamos peers inválidos antes de gastar ciclos. Se devuelve
`HandshakeError::Unauthorized("peer X no está en la allowlist")`.
Wire en Arje (`ente-zero`):
- Nueva env var `BRAHMAN_PEER_ALLOWLIST` apuntando a un archivo.
- `setup_brahman_allowlist()` carga al startup; degrada a `None`
(modo abierto) si el archivo falla, consistente con la doctrina
PID 1 de no romper por subsistemas opcionales.
Ejemplo de archivo de allowlist:
```text
# Peers permitidos en la malla brahman de prod-eu-1
# Generados con: ente-zero (peer_id loggeado al arrancar)
12D3KooWFooBarBazFooBarBazFooBarBazFooBarBazFooBarBaz
12D3KooWQuxQuxQuxQuxQuxQuxQuxQuxQuxQuxQuxQuxQuxQuxQux # operador 2
```
Activación end-to-end:
```sh
BRAHMAN_LISTEN_MULTIADDR=/ip4/0.0.0.0/tcp/4101 \\
BRAHMAN_PEER_ALLOWLIST=/etc/brahman/allowlist.txt \\
ente-zero
```
Tests:
- 6 unit en `peer_allowlist::tests`: from_iter, parse limpio, parse
con comentarios inline, parse rechaza PeerId inválido (y reporta
número de línea), I/O error en archivo faltante, empty list
rechaza todo.
- 1 E2E en `network_libp2p.rs`:
`libp2p_handshake_allowlist_admits_listed_rejects_others`. A
configura `allowlist = [allowed_peer]`. Cliente con keypair
permitida pasa el handshake (sesión registrada, farewell limpio).
Segundo cliente con keypair distinta es rechazado con error
ANTES de que se le verifique la firma. Sanidad: el conteo de
sesiones del server queda en 0 tras el rechazo.
25 tests verdes en brahman-handshake (12 unit + 7 handshake legacy
+ 3 discovery + 3 libp2p). Ningún regreso en ente-zero (4/4
keypair_store).
Pendiente futuro:
- Denylist explícita (negada — banear peers específicos sin tener
que listar a todos los demás).
- Hot reload de la allowlist sin restart del Init (signal SIGHUP o
watch del archivo).
- Aplicar la política a nivel de swarm vía
`libp2p_allow_block_list::Behaviour` para rechazar conexiones
ANTES del Noise handshake (hoy se rechaza después, gastando un
round-trip TCP+Noise por cada intento denegado).
### feat(brahman-net+handshake): stop_providing automático en cleanup de sesión
Cierra el pendiente conocido del DHT: hasta ahora cuando una sesión
con outputs cerraba (Farewell, EOF, error), el record que la