diff --git a/recipes/openssh.toml b/recipes/openssh.toml new file mode 100644 index 00000000..91faacb9 --- /dev/null +++ b/recipes/openssh.toml @@ -0,0 +1,77 @@ +# OpenSSH 10.3p1 — servidor/cliente SSH desde fuente (Etapa C, LA excepción C del userland Rust). +# +# El recorrido de la Etapa C reemplaza busybox pieza por pieza con reimplementaciones Rust maduras +# (coreutils/findutils/diffutils/ripgrep/netup). OpenSSH es la EXCEPCIÓN deliberada: NO hay servidor +# SSH en Rust con la madurez/auditoría de OpenSSH (russh/thrussh son librerías cliente, no un sshd de +# producción), así que la red de servicios se apoya en el OpenSSH portable canónico, construido desde +# fuente con hammer (no `apk add openssh`). Provee sshd + ssh + ssh-keygen + scp/sftp. +# +# DE-ALPINIZADO: build vainilla del tarball portable upstream (cdn.openbsd.org), sin los parches de +# Alpine (que son para PAM/login.conf/utmp, que aquí NO se usan). Es un SERVICIO del producto, no un +# input del 4/4 (of_tree) ni una herramienta del toolchain ⇒ no necesita casar byte-a-byte con nada; +# el criterio es "construye y corre". +# +# DEPS (deps.build → materialize como capas overlay en /usr del sandbox): +# - openssl (recipes/openssl.toml): libcrypto.a estática + headers. sshd enlaza la cripto. +# - zlib (recipes/zlib.toml): libz.a estática. compresión opcional del transporte. +# Ambas son no-shared (sólo .a) ⇒ el enlace de OpenSSH es estático por construcción contra ellas; se +# fuerza LDFLAGS=-static para un sshd/ssh completamente estáticos musl (sin .so que resolver, robusto +# para un daemon de arranque temprano). getpwnam/getservbyname en musl estático funcionan sin NSS. +# +# COMPILADOR: zig cc con zig 0.13.0 (NO 0.16). Igual que openssl/binutils/python3: zig 0.14+ MISCOMPILA +# binarios musl (regresión 0.14, bisect con flex) y 0.13.0 la esquiva. Coherente además con que +# libcrypto.a se compiló con el mismo zig 0.13.0. +# +# RUNTIME (NO build — se cablea en la card de arje / init, ver Etapa C "servicios"): +# - usuario de privsep `sshd` en /etc/passwd y el dir /var/empty (root:root, 0711). La privsep es +# OBLIGATORIA en OpenSSH 10.x. +# - host keys: generarlas con `ssh-keygen -A` en el primer arranque (por eso el install es +# `install-nokeys`: NO generamos claves en build-time — serían no-reproducibles y secretas). +# +# GOTCHA install: `make install` normal corre host-key + check-config (invoca ssh-keygen/sshd y haría +# chown a un usuario inexistente fuera de root) ⇒ se usa `install-nokeys`. STRIP_OPT="" desactiva el +# stripping en la instalación (no dependemos de un `strip` concreto en el sandbox; el binario zig ya +# sale razonable, y de-strippear es cosmético para un servicio). + +name = "openssh" +version = "10.3p1" + +[source] +tarball = "https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-10.3p1.tar.gz" +sha256 = "56682a36bb92dcf4b4f016fd8ec8e74059b79a8de25c15d670d731e7d18e45f4" + +[build] +compiler = "zig-cc" +target = "x86_64-linux-musl" +link = "static" +zig_version = "0.13.0" + +[deps] +build = ["zlib", "openssl"] + +[build.phases] +# --sysconfdir=/etc/ssh: sshd_config/ssh_config viven en /etc/ssh. --with-ssl-dir/--with-zlib=/usr: +# donde las deps overlayean sus .a + headers. --without-pam/--without-selinux: musl sin PAM, sin SELinux. +# --with-privsep-path=/var/empty: jaula de privsep. LDFLAGS=-static: binarios musl ESTÁTICOS completos +# (el target sólo trae el loader musl, no .so de zlib/openssl). --without-hardening: OBLIGATORIO con +# -static. OpenSSH activa PIE (-fPIE/-pie) por defecto vía "toolchain hardening"; `-pie` GANA sobre +# `-static` en zig cc/lld ⇒ sin esto el binario sale dinámico (PIE). configure desactiva el PIE +# automático justo cuando el hardening está off (configure ~L16057: "Turn off automatic PIE when +# toolchain hardening is off"). Se sacrifica stack-protector/RELRO del servicio — re-endurecer luego. +# +# GOTCHA ZIG -static + .so del devfs: el devfs Alpine trae `libz.so` en /usr/lib (zlib runtime base), +# y `zig cc -static -lz`, con .a y .so presentes, PREFIERE el .so y enlaza DINÁMICO igual (NEEDED +# libz.so.1) — verificado: `-l:libz.a` da estático puro, `-lz` no. libcrypto se libra sólo porque el +# devfs no trae libcrypto.so. Para robustez explícita forzamos AMBOS al archivo estático exacto vía +# `sed` sobre `CHANNELLIBS` del Makefile (único sitio con `-lcrypto`/`-lz`): los .a vienen de las deps +# overlay en /usr/lib. +# +# GOTCHA ZIG -static + libc NATIVO: el sandbox exporta CC="zig cc -mcpu=baseline" SIN `-target`, o sea +# zig nativo (Alpine musl). En modo nativo `zig cc -static` enlaza el `libc.so` del SISTEMA igual +# (NEEDED libc.so) en vez del musl embebido de zig — todo lo demás queda estático pero libc no. +# Override del CC con `-target x86_64-linux-musl`: fuerza el musl bundle de zig en modo estático ⇒ +# binario 100% estático (sin interpreter, sin NEEDED). Mismo triple ABI ⇒ enlaza con las .a nativas +# (libcrypto.a/libz.a). Se fija en configure (autotools sustituye CC en el Makefile ⇒ compile+link). +configure = "./configure --prefix=/usr --sysconfdir=/etc/ssh --with-ssl-dir=/usr --with-zlib=/usr --with-privsep-path=/var/empty --without-pam --without-selinux --without-hardening CC='zig cc -mcpu=baseline -target x86_64-linux-musl' LDFLAGS=-static && sed -i '/^CHANNELLIBS=/{s/-lcrypto/-l:libcrypto.a/g;s/-lz/-l:libz.a/g}' Makefile" +compile = "make -j\"$(nproc)\"" +install = "make install-nokeys DESTDIR=/out STRIP_OPT="