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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user