DOS ERRORES MÍOS, uno de dirección y otro técnico, los dos en la cosecha automática:
1. DIRECCIÓN: declaró `busybox` como dep en 29 recetas — cuando la Etapa C lo está
ELIMINANDO (USERLAND_COMPONENTS=[uutils,findutils,…], ya cerrada; joyas-reusables
§5 lo confirma: "uutils… Ubuntu 25.10 los envía como default"). Estaba cimentando
la deuda que el roadmap borra.
2. TÉCNICO: busybox YA está en el runtime base de harkaq (`ro /bin/busybox`, `ro
/bin/sh` — el sandbox corre `sh -c` y /bin/sh→/bin/busybox). Es CONTRATO, no dep.
Declararlo apila el busybox de hammer sobre el de Alpine y ROMPE el build:
binutils daba "cannot run C compiled programs" en las DOS máquinas. Verificado:
sin busybox declarado, binutils construye (b3:f4507dcd…). Y la cadena se explica:
binutils roto ⇒ zlib (que lo declara) tampoco construía.
Auditoría de la cosecha (diff real, no la línea completa del +): añadió sólo 5 deps
distintas — make ×73, busybox ×29, perl ×8, pkgconf ×5, binutils ×1. Sólo busybox
estaba mal; las otras 4 son deps reales medidas. busybox revertido de 30 recetas
(queda sólo en busybox.toml, pre-existente).
Es el mismo error que ya me habían señalado con otro disfraz: MEDIR BIEN Y ACCIONAR
MAL. harkaq midió correcto (el build toca /bin/busybox: es el shell); la acción
correcta no era declararlo sino reconocerlo como contrato.
+ COORDINACIÓN del §3 con Fable 5 (mismo diseño, tareas repartidas):
- Su lección casper queda CONFIRMADA y REFORZADA: la clausura de build no sólo le
FALTAN las clases del mundo (offline) — también le SOBRA casi todo (headers, gcc).
Medido: htop (estático, 0 NEEDED) no toca NADA al correr ⇒ política = su binario.
- Su "la clase viaja como campo de la ConcesionCapacidad, sin formato nuevo" se
cumple LITERALMENTE: lo firmado es format::Permisos = u32 bitmask en 36 bytes
canónicos (Ring 0) ⇒ las clases SON los bits. La cripto no se toca.
- Diseño unificado: frontera (clases, u32, declaradas) + detalle (paths, Landlock,
medidos). D3 rige en ambos.
- Reparto: clases→Fable 5; medición/harness→Opus. CONTACTO: runtime-policy.sh ahora
emite la CLASE detectada (/etc/resolv.conf→dns, /etc/ssl/certs→tls-certs, …), no
sólo el path: la medición alimenta la tabla, la tabla decide el bit.
- Consumidor esperando: plan-jaula-juegos F1 (Steam que no puede leer ~/.ssh).
+ juez.sh (§10): nombre único por corrida (con uno fijo pega en caché ⇒ falso
"sin evidencia"). Fue el juez quien destapó todo esto en su primera corrida real.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
83 lines
4.1 KiB
Bash
Executable File
83 lines
4.1 KiB
Bash
Executable File
#!/bin/sh
|
||
# runtime-policy.sh <binario> [args…] — política de RUNTIME derivada por medición (SDD 17 §3).
|
||
#
|
||
# La clausura que harkaq mide en BUILD **no** es la política de runtime: un build lee `zlib.h`,
|
||
# `make`, el compilador; el binario resultante no lee nada de eso. Son dos clausuras de dos
|
||
# sujetos distintos. Lo que sí sirve es la TÉCNICA: correr el binario bajo la jaula con política
|
||
# mínima y dejar que las denegaciones digan qué necesita. Eso ES su política de runtime, derivada
|
||
# por evidencia y no escrita a mano.
|
||
#
|
||
# scripts/harkaq/runtime-policy.sh store/<hash>-htop/usr/bin/htop --version
|
||
#
|
||
# BASELINE: el `sh` que lanza al binario también deniega cosas (locale, ld.so.cache). Se mide
|
||
# primero un `true` para conocer ese ruido y RESTARLO — si no, la política del binario se lleva
|
||
# la basura del lanzador.
|
||
#
|
||
# Cómo encaja con la concesión firmada de arje/wawa (PLAN-ATESTACION):
|
||
# - `ConcesionCapacidad` firma `(blake3(binario), u32)` en 36 bytes canónicos, zero-alloc, Ring 0
|
||
# ⇒ es la FRONTERA gruesa. NO se toca: meterle paths rompería todas las firmas y contradice
|
||
# "capacidad = frontera física, no tabla".
|
||
# - Esta política es el DETALLE granular (fs, paths), vive en userspace y se aplica con Landlock.
|
||
# Los dos coexisten: el kernel verifica la frontera; Landlock aplica el detalle.
|
||
set -eu
|
||
|
||
BIN="${1:?uso: runtime-policy.sh <binario> [args…]}"
|
||
shift || true
|
||
HUB="$(cd "$(dirname "$0")/../.." && pwd)"
|
||
cd "$HUB"
|
||
BIN="$(cd "$(dirname "$BIN")" && pwd)/$(basename "$BIN")" # absoluto
|
||
POL="$(mktemp)"; BASE="$(mktemp)"; MED="$(mktemp)"
|
||
trap 'rm -f "$POL" "$BASE" "$MED"' EXIT
|
||
|
||
# Política mínima DERIVADA (no a mano: escribirla a mano se olvida de libc.musl y el sh ni
|
||
# arranca). El sandbox de harkaq-run monta el HOST ⇒ el cierre es el del sh DEL HOST.
|
||
scripts/harkaq/harkaq-base-closure.py / /bin/sh 2>/dev/null > "$POL"
|
||
echo "ro $BIN" >> "$POL"
|
||
|
||
denegados() { # corre $1 bajo la jaula y lista los paths denegados
|
||
HARKAQ_TIMEOUT=10 scripts/harkaq/harkaq-run.sh "$POL" "$1" 2>/dev/null \
|
||
| grep '^{' | head -1 \
|
||
| python3 -c 'import json,sys
|
||
try:
|
||
v=json.load(sys.stdin)
|
||
if v["estado"]=="SinEvidencia": print("SIN-EVIDENCIA:"+v.get("motivo","")[:60]); raise SystemExit
|
||
for d in v["denials"]: print(d["path"])
|
||
except Exception: pass'
|
||
}
|
||
|
||
echo "══ baseline: el ruido del lanzador (sh)"
|
||
denegados "true" | sort -u > "$BASE"
|
||
grep -q 'SIN-EVIDENCIA' "$BASE" && { echo " $(cat "$BASE")"; echo " ⇒ sin evidencia fiable: no se afirma ninguna política"; exit 3; }
|
||
sed 's/^/ − /' "$BASE"
|
||
|
||
echo "══ medición: $(basename "$BIN") $*"
|
||
denegados "$BIN $*" | sort -u > "$MED"
|
||
grep -q 'SIN-EVIDENCIA' "$MED" && { echo " $(cat "$MED")"; exit 3; }
|
||
|
||
echo
|
||
echo "══ POLÍTICA DE RUNTIME de $(basename "$BIN") (medida − baseline):"
|
||
propios=$(comm -23 "$MED" "$BASE")
|
||
if [ -z "$propios" ]; then
|
||
echo " (ninguna) — el binario no tocó NADA fuera de sí mismo."
|
||
echo " ⇒ política Landlock: sólo \`ro $BIN\`. Es el caso de un estático que no abre ficheros."
|
||
else
|
||
echo "$propios" | sed 's/^/ ro /'
|
||
# CONTACTO con el §3 (clases de servicio, diseño de Fable 5): la medición no sólo lista
|
||
# paths — propone la CLASE. El path va a Landlock (detalle, userspace); la clase va al bit
|
||
# del u32 firmado (frontera, Ring 0). La medición ALIMENTA la tabla; la tabla decide el bit.
|
||
# Ninguno ensancha por su cuenta (D3).
|
||
clases=""
|
||
echo "$propios" | grep -qE '/etc/(resolv\.conf|hosts)' && clases="$clases dns"
|
||
echo "$propios" | grep -qE '/etc/ssl/certs' && clases="$clases tls-certs"
|
||
echo "$propios" | grep -qE '/usr/share/zoneinfo|/etc/TZ' && clases="$clases tz"
|
||
echo "$propios" | grep -qE '/dev/log' && clases="$clases syslog"
|
||
if [ -n "$clases" ]; then
|
||
echo
|
||
echo " ── clases de servicio detectadas (§3: van al u32 firmado, NO a Landlock):"
|
||
for c in $clases; do echo " $c"; done
|
||
echo " ⇒ declarar por nombre en la ConcesionCapacidad; el resto de arriba es Landlock."
|
||
fi
|
||
echo
|
||
echo " ⇒ los `ro` van a la política Landlock del binario instalado."
|
||
fi
|