Arquitectura

Arquitectura y diseno

Por que Rust

SSH-Frontière esta escrito en Rust por tres razones:

  1. Seguridad de memoria: sin buffer overflow, sin use-after-free, sin null pointer. Para un componente de seguridad que funciona como shell de inicio, esto es critico.

  2. Binario estatico: se compila con el target x86_64-unknown-linux-musl (otros targets posibles sin garantia de funcionamiento), el binario ocupa ~1 Mo y no tiene ninguna dependencia del sistema. Se copia en el servidor y listo.

  3. Rendimiento: el programa arranca, valida, ejecuta y termina en milisegundos. Sin runtime, sin garbage collector, sin JIT.

Sincrono y efimero

SSH-Frontière es un programa sincrono y one-shot. Sin daemon, sin async, sin Tokio.

El ciclo de vida es simple:

  1. sshd autentica la conexion SSH por clave
  2. sshd hace fork y ejecuta ssh-frontiere como shell de inicio
  3. ssh-frontiere valida y ejecuta el comando
  4. El proceso termina

Cada conexion SSH crea un nuevo proceso. Sin estado compartido entre conexiones, sin problemas de concurrencia.

Estructura del codigo

El codigo esta organizado en modulos con responsabilidades claras:

ModuloResponsabilidad
main.rsPunto de entrada, aplanamiento de argumentos, llamada al orquestador
orchestrator.rsFlujo principal: banner, cabeceras, comando, respuesta, bucle de sesion
config.rsEstructuras de configuracion TOML, validacion fail-fast
protocol.rsProtocolo de cabeceras: parser, banner, auth, sesion, body
crypto.rsSHA-256 (implementacion FIPS 180-4), base64, nonce, challenge-response
dispatch.rsParsing de comandos (comillas, key=value), resolucion, RBAC
chain_parser.rsParser de cadenas de comandos (operadores ;, &, |)
chain_exec.rsEjecucion de cadenas: secuencia estricta (;), permisiva (&), recuperacion (|)
discovery.rsComandos help y list: descubrimiento de dominios y acciones
logging.rsLogging JSON estructurado, enmascaramiento de argumentos sensibles
output.rsRespuesta JSON, codigos de salida
lib.rsExposicion de crypto para el binario proof y helpers de fuzz

Cada modulo tiene su archivo de tests (*_tests.rs) en el mismo directorio.

Un binario auxiliar proof (src/bin/proof.rs) permite calcular los proofs de autenticacion para los tests E2E y la integracion con clientes.

Protocolo de cabeceras

SSH-Frontière utiliza un protocolo de texto sobre stdin/stdout. Los prefijos difieren segun la direccion:

Cliente hacia servidor (stdin):

PrefijoFuncion
+ Configura: directivas (auth, session, body)
# Comenta: ignorados por el servidor
(texto plano)Comando: dominio accion [argumentos]
. (solo en una linea)Fin de bloque: termina un bloque de comando

Servidor hacia cliente (stdout):

PrefijoFuncion
#> Comenta: banner, mensajes informativos
+> Configura: capabilities, challenge nonce
>>> Responde: respuesta JSON final
>> Stdout: salida estandar en streaming (ADR 0011)
>>! Stderr: salida de error en streaming

Flujo de conexion

CLIENTE                                 SERVIDOR
  |                                        |
  |  <-- banner + capabilities ----------  |   #> ssh-frontiere 0.1.0
  |                                        |   +> capabilities rbac, session, help, body
  |                                        |   +> challenge nonce=a1b2c3...
  |                                        |   #> type "help" for available commands
  |                                        |
  |  --- +auth (opcional) -------------->  |   + auth token=runner-ci proof=deadbeef...
  |  --- +session (opcional) ----------->  |   + session keepalive
  |                                        |
  |  --- comando (texto plano) --------->  |   forgejo backup-config
  |  --- fin de bloque ----------------->  |   .
  |  <-- streaming stdout ---------------  |   >> Backup completed
  |  <-- respuesta JSON final -----------  |   >>> {"status_code":0,"status_message":"executed",...}
  |                                        |
  |  (si session keepalive)                |
  |  --- comando 2 --------------------->  |   infra healthcheck
  |  --- fin de bloque ----------------->  |   .
  |  <-- respuesta JSON 2 ---------------  |   >>> {"status_code":0,...}
  |  --- fin de sesion (bloque vacio) -->  |   .
  |  <-- session closed ------------------  |   #> session closed

Respuesta JSON

Cada comando produce una respuesta JSON final en una sola linea, prefijada por >>>. La salida estandar y de error se envian en streaming mediante >> y >>!:

>> Backup completed
>>> {"command":"forgejo backup-config","status_code":0,"status_message":"executed","stdout":null,"stderr":null}

Protocolo body

La cabecera +body permite transmitir contenido multilínea al proceso hijo via stdin. Cuatro modos de delimitacion:

Configuracion TOML

El formato de configuracion es TOML declarativo. Eleccion documentada en la ADR 0001:

La configuracion se valida al cargarse (fail-fast): sintaxis TOML, completitud de campos, coherencia de los placeholders, al menos un dominio, al menos una accion por dominio, valores enum no vacios.

Politica de dependencias

SSH-Frontière tiene una politica de cero dependencias no vitales. Cada crate externa debe estar justificada por una necesidad real.

Dependencias actuales

3 dependencias directas, ~20 dependencias transitivas:

CrateUso
serde + serde_jsonSerializacion JSON (logging, respuestas)
tomlCarga de la configuracion TOML

Matriz de evaluacion

Antes de agregar una dependencia, se evalua segun 8 criterios ponderados (nota /5): licencia (eliminatorio), gobernanza (x3), comunidad (x2), frecuencia de actualizacion (x2), tamano (x3), dependencias transitivas (x3), funcionalidades (x2), no encerramiento (x1). Puntuacion minima: 3.5/5.

Auditoria

Como se diseno el proyecto

SSH-Frontière se desarrollo en fases sucesivas (1 a 9, con fases intermedias 2.5 y 5.5), pilotado por agentes Claude Code con una metodologia TDD sistematica:

FaseContenido
1Dispatcher funcional, config TOML, RBAC 3 niveles
2Configuracion produccion, scripts de operaciones
2.5SHA-256 FIPS 180-4, BTreeMap, timeout graceful
3Protocolo de cabeceras unificado, auth challenge-response, sesiones
4Tests E2E SSH Docker, limpieza de codigo, integracion forge
5Tags de visibilidad, filtrado horizontal por tokens
5.5Nonce opcional, argumentos con nombre, binario proof (incluye la fase 6, fusionada)
7Guia de configuracion, dry-run --check-config, help sin prefijo
8Tipos de error estructurados, clippy pedantic, cargo-fuzz, proptest
9Protocolo body, argumentos libres, max_body_size, codigo de salida 133

El proyecto fue disenado por:

Donde el humano y la maquina trabajan juntos, mejor, mas rapido, con mayor seguridad.