Files
takana/scripts/harkaq/harkaq-policy.sh
T
sergioandClaude Opus 4.8 24c6ab455e harkaq: §4 corregido (política POR FICHERO) + Q2 con método y primeros datos reales
CORRECCIÓN ESTRUCTURAL del §4 (otra herencia del marco nix). El Draft 2 decía
`fs_ro: [store paths de la clausura]`. Falso: esos paths NO existen dentro del
sandbox. `Sandbox::bwrap_args` apila las deps con --overlay-src y las FUNDE en
el mismo /usr que el rootfs Alpine, a propósito, para que el compilador las
encuentre sin plumbing de flags. Medido:

  /usr/include/zlib.h   (dep DECLARADA)        dev=63 ino=13641973
  /usr/include/stdio.h  (Alpine, NO declarado) dev=63 ino=4196788
  /usr/include          (el directorio)        dev=61 ino=15  ← overlayfs, uno

Una regla sobre /usr concede las dos ⇒ la evidencia no valdría nada. Ni `dev`
distingue. ⇒ La clausura se enumera FICHERO A FICHERO (computable: en el host
sabemos qué aporta cada dep). Dos consecuencias: `list` ≠ `ro` (pkgconf NECESITA
escanear /usr/lib/pkgconfig, pero listar no es leer; Landlock separa READ_DIR de
READ_FILE) y máscara por tipo (derechos de-sólo-dir sobre un fichero ⇒ EINVAL y
la regla entera se cae).

Piezas nuevas: harkaq-policy.sh (deriva la clausura, D1) + q2-runtime-base.sh
(responde Q2 midiendo, no adivinando) + harkaq-exec con `list`/máscara por tipo.

Q2, primeros datos con un build REAL en el sandbox REAL contra la dep zlib:
  estado: Impuro | canario: True | contador kernel: 3
      fs.read_file /usr/bin/env
      fs.read_file /usr/lib/libz.so.1.3.2   ← la zlib de ALPINE!

El build se linkaba contra la zlib de Alpine en vez de la dep declarada (que
aporta libz.a). Sin harkaq eso pasa en verde y el artefacto queda dependiendo de
Alpine. ES EXACTAMENTE EL BUG DEL §0, cazado en un build real.
Piso duro del runtime base: /bin/sh → /bin/busybox → /lib/ld-musl (sin eso ni el
canario corre ⇒ el mínimo es "lo que hace falta para que el canario pueda correr").

Dos rastrillos: el canario no puede depender de un binario (`cat` moría en
/bin/cat ⇒ ahora `read _ < $CANARY`, sólo builtins) ni vivir en /tmp (la política
concede `rw /tmp` ⇒ el canario caía DENTRO de la clausura y no denegaba nada).
El segundo lo cazó D9 MISMO: el lector se negó a certificar en vez de decir
Hermetico. El canario funcionando como se diseñó, sobre un bug de quien lo diseñó.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 15:20:39 -04:00

52 lines
2.2 KiB
Bash
Executable File

#!/bin/sh
# harkaq-policy — deriva la política de la clausura declarada (SDD 16, D1).
#
# harkaq-policy.sh <dir-dep> [<dir-dep>...] > politica
#
# D1: la política NO se escribe, se DERIVA. La receta ya declara sus `deps` por hash; cada dep
# es un artefacto sellado del store (`store/<hash>-<name>/usr/...`). La clausura es, exactamente,
# el conjunto de ficheros que esas deps aportan — ni uno más.
#
# El sandbox de hammer apila cada dep con `--overlay-src` sobre el rootfs Alpine, así que el
# fichero `store/<hash>-zlib/usr/include/zlib.h` aparece dentro como `/usr/include/zlib.h`. La
# traducción es sólo quitar el prefijo del store: el resto del path YA es el path del sandbox.
#
# Por qué fichero a fichero y no por directorio: adentro, la dep declarada y el rootfs Alpine
# comparten `/usr` (medido: /usr/include/zlib.h y /usr/include/stdio.h son el mismo directorio
# overlayfs). Una regla sobre /usr concede los dos y la evidencia deja de significar algo. Los
# `list` de los directorios se emiten para que las herramientas puedan ESCANEAR (pkgconf, los
# tests de configure) sin poder LEER lo no declarado — Landlock separa READ_DIR de READ_FILE.
set -eu
emit_dirs() {
# Los directorios que contienen ficheros de la clausura: listables, no legibles.
# Se emiten deduplicados y con sus ancestros, porque un `ls /usr` también es un escaneo
# legítimo y su denegación sería un falso positivo, no deuda de de-Alpinización.
while IFS= read -r d; do
while [ "$d" != "/" ] && [ -n "$d" ]; do
echo "list $d"
d=$(dirname "$d")
done
done | sort -u
}
TMP="$(mktemp)"
trap 'rm -f "$TMP" "$TMP.d"' EXIT
: > "$TMP"
: > "$TMP.d"
for dep in "$@"; do
real=$(readlink -f "$dep")
# `.hammer/` es metadata del artefacto (recipe.toml), no parte de la clausura que el build
# consume: si el build lo leyera sería una denegación legítima que queremos ver.
find "$real" -type f -o -type l | grep -v "/\.hammer/" | while IFS= read -r f; do
inside="${f#$real}"
echo "ro $inside" >> "$TMP"
dirname "$inside" >> "$TMP.d"
done
done
echo "# harkaq-policy: clausura derivada de $# dep(s)"
sort -u "$TMP.d" | emit_dirs
sort -u "$TMP"