Files
takana/scripts/harkaq/runtime-policy.sh
T
sergioandClaude Opus 4.8 be52e735ab cierres §3: política de runtime derivada por medición (sin tocar la cripto)
El #3 decía "la clausura medida por harkaq en build = permisos del binario
instalado". MEDIDO: es falso en dos ejes, y por eso NO se implementó así.

1. SUJETO: la clausura de BUILD (zlib.h, make, gcc) no es la de RUNTIME. Un build
   lee headers; el binario resultante no. Dos clausuras, dos sujetos.
2. GRANULARIDAD/CRIPTO: lo que la ConcesionCapacidad firma NO es card-core::
   Permissions (struct) sino format::Permisos = u32 bitmask, dentro de
   `mensaje_capacidad` = hash(32)||permisos_le(4) = 36 bytes canónicos, zero-alloc,
   con espejo no_std en wawa-kernel (Ring 0). Meterle paths rompería TODAS las
   firmas y contradice el "capacidad = frontera física, no tabla" que el propio
   plan cita como referencia.

⇒ Camino elegido: la concesión firmada QUEDA INTACTA (frontera gruesa, Ring 0) y
la clausura granular se deriva por MEDICIÓN como política Landlock en userspace,
donde Vec<String> sí cabe. Coexisten: el kernel verifica la frontera, Landlock
aplica el detalle.

runtime-policy.sh: corre el binario bajo la jaula con política mínima DERIVADA
(harkaq-base-closure, no a mano) y resta el BASELINE del lanzador (el sh deniega
locale/ld.so.cache; sin restarlo, la política del binario se lleva esa basura).

Demo real — htop (musl-estático): baseline 3 paths del sh; htop --version no tocó
NADA propio ⇒ su política de runtime es sólo `ro <su binario>`. Medido, no supuesto.
htop tiene 0 NEEDED: para un estático la política NO sale de las libs, sólo de
correrlo — lo que confirma que la técnica de harkaq es la única vía para el #3.

+ FIX del lector (lo cazó el chequeo anti-pérdida `nº ACCESS == denials`): descartaba
los ACCESS previos al canario porque hasta verlo no sabe su domain= — pero el sh
deniega ANTES del probe y esas son suyas. El kernel contaba 4 y el lector recibía 1.
Ahora se bufferizan con su domain y se rescatan retroactivamente al ver el canario.
Sin ese chequeo habría emitido una política con 1 de 4 accesos, perfectamente creíble.
(Primer intento de fix —drenar el socket al ver el deallocated— era plausible y NO
era la causa: el contador siguió en 4 vs 1.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-16 22:02:05 -04:00

68 lines
3.1 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 /'
echo
echo " ⇒ pegar en la política Landlock del binario instalado."
fi