Cuarto defecto de la misma familia que los tres que el script ya repone, encontrado construyendo DESDE la jaula: los binarios que el agente necesita no están en la imagen de Arch, pero sí sellados en `/store`, que la instancia ya tiene concedido. Son musl estáticos y corren dentro de una imagen glibc, así que no hay que instalar nada — sólo enlazarlos. El que costó encontrarlo es `patch`: takana aplica los parches FUERA del sandbox, o sea que lo busca en el PATH de la jaula, no en el del lab. `wtype` (sin parches) construía y `zsh` (con seis) moría en `spawn patch: No such file or directory`. El `patch` del lab no sirve de nada acá: es musl DINÁMICO y no arranca en una imagen glibc. Los wrappers ya viven en /work/<usuario>/bin, que es bind del anfitrión y sobrevive al recreate; lo único que se pierde es el enlace en el PATH.
92 lines
5.8 KiB
Bash
Executable File
92 lines
5.8 KiB
Bash
Executable File
#!/bin/sh
|
|
# Repone en la jaula de una persona lo que NO cabe en el manifiesto y por eso vive en el `upper`
|
|
# —que por el D3 del ADR 0015 es **caché descartable**—. Se corre EN LA CAJA, como root, DESPUÉS de
|
|
# cada `takana qorpa recreate`.
|
|
#
|
|
# ── POR QUÉ HAY COSAS FUERA DEL MANIFIESTO ──────────────────────────────────────────────────────
|
|
# `instance.toml` declara imagen, paquetes y concesiones. No declara «editá este fichero de la
|
|
# imagen», y tres defectos reales pedían exactamente eso. Los encontró el agente trabajando adentro
|
|
# (2026-09-18) y los arregló a mano; esto es esa mano, escrita, para que sobreviva al recreate.
|
|
#
|
|
# 1. **No hay entrada en `/etc/passwd` para el uid de adentro.** `ssh` y `git` mueren con
|
|
# `No user exists for uid 1001` — ni lo intentan. Pasa porque la instancia de persona va con
|
|
# `root = false` (si no, el CLI se ve uid 0 y rechaza `--dangerously-skip-permissions`), y ese
|
|
# uid no existe en la imagen de Arch.
|
|
# 2. **`/etc/ssh/ssh_config.d` es de la capa inferior y su dueño cae fuera del mapa** (`nobody`),
|
|
# así que `ssh` ABORTA con `Bad owner or permissions on …/20-systemd-ssh-proxy.conf`. No
|
|
# degrada: aborta. Y el directorio no se puede mover (EXDEV en overlay), así que se redirige el
|
|
# `Include` a una copia propia.
|
|
# 3. `known_hosts` se siembra desde el ANFITRIÓN (ver abajo), que es lo que evita el
|
|
# `StrictHostKeyChecking` contra el worker la primera vez.
|
|
set -eu
|
|
|
|
U=${1:-sergio}
|
|
INST=${INST:-claude-$U}
|
|
UP=${UP:-/var/lib/hammer/qorpa/instances/$INST/upper}
|
|
UID_DENTRO=$(id -u "$U")
|
|
|
|
[ -d "$UP" ] || { echo "no existe el upper de $INST en $UP" >&2; exit 1; }
|
|
# El upper es de la persona: escribir como root dejaría ficheros que ella no puede tocar.
|
|
esc() { install -d -o "$U" -g "$U" "$(dirname "$1")"; }
|
|
|
|
# ── 1. la cuenta, dentro de la imagen ───────────────────────────────────────────────────────────
|
|
esc "$UP/etc/passwd"
|
|
if [ ! -f "$UP/etc/passwd" ]; then cp /var/lib/hammer/qorpa/images/*/rootfs/etc/passwd "$UP/etc/passwd" 2>/dev/null || : ; fi
|
|
grep -q "^$U:" "$UP/etc/passwd" 2>/dev/null || \
|
|
echo "$U:x:$UID_DENTRO:$UID_DENTRO::/home/$U:/usr/bin/bash" >> "$UP/etc/passwd"
|
|
esc "$UP/etc/group"
|
|
if [ ! -f "$UP/etc/group" ]; then cp /var/lib/hammer/qorpa/images/*/rootfs/etc/group "$UP/etc/group" 2>/dev/null || : ; fi
|
|
grep -q "^$U:" "$UP/etc/group" 2>/dev/null || echo "$U:x:$UID_DENTRO:" >> "$UP/etc/group"
|
|
chown "$U:$U" "$UP/etc/passwd" "$UP/etc/group" 2>/dev/null || true
|
|
echo " ✓ $U en el passwd/group de la instancia (uid $UID_DENTRO)"
|
|
|
|
# ── 2. el Include de ssh, a un directorio cuyo dueño SÍ está en el mapa ──────────────────────────
|
|
esc "$UP/etc/ssh/ssh_config"
|
|
if [ ! -f "$UP/etc/ssh/ssh_config" ]; then
|
|
cp /var/lib/hammer/qorpa/images/*/rootfs/etc/ssh/ssh_config "$UP/etc/ssh/ssh_config" 2>/dev/null || :
|
|
fi
|
|
if [ -f "$UP/etc/ssh/ssh_config" ]; then
|
|
install -d -o "$U" -g "$U" "$UP/etc/ssh/ssh_config.d.local"
|
|
sed -i "s|^Include /etc/ssh/ssh_config.d/\*.conf|Include /etc/ssh/ssh_config.d.local/*.conf|" "$UP/etc/ssh/ssh_config"
|
|
chown "$U:$U" "$UP/etc/ssh/ssh_config"
|
|
echo " ✓ Include redirigido a ssh_config.d.local (el original es de la capa inferior, dueño fuera del mapa)"
|
|
fi
|
|
|
|
# ── 3. known_hosts sembrado desde el anfitrión ──────────────────────────────────────────────────
|
|
K=/home/$U/.ssh/known_hosts
|
|
install -d -o "$U" -g "$U" -m 700 "/home/$U/.ssh"
|
|
for h in dev.gioser.net git.gioser.net; do
|
|
grep -q "^\[\?$h" "$K" 2>/dev/null || ssh-keyscan -t ed25519 "$h" 2>/dev/null >> "$K" || true
|
|
done
|
|
chown "$U:$U" "$K" 2>/dev/null || true; chmod 600 "$K" 2>/dev/null || true
|
|
echo " ✓ known_hosts con $(grep -c . "$K" 2>/dev/null || echo 0) entradas"
|
|
|
|
# ── 4. las herramientas del STORE, en el PATH de adentro ────────────────────────────────────────
|
|
# MEDIDO el 2026-09-18 construyendo DESDE la jaula. Cuatro binarios que hacen falta adentro y que no
|
|
# tienen por qué venir del gestor de paquetes de la imagen: ya están sellados en `/store`, que la
|
|
# instancia tiene concedido, y son musl estáticos — corren igual dentro de una imagen glibc.
|
|
#
|
|
# · `takana` el que construye. `/opt/takana/target/release/takana` del anfitrión es un enlace a
|
|
# `/usr/bin/takana`, que ADENTRO es el /usr/bin de Arch y no lo tiene.
|
|
# · `patch` takana aplica los parches FUERA del sandbox, así que lo busca en ESTE PATH. Sin él,
|
|
# toda receta con parches muere en `spawn patch: No such file or directory` — `wtype`
|
|
# (sin parches) pasaba y `zsh` (con seis) no. El del lab NO sirve: es musl DINÁMICO y
|
|
# no arranca en una jaula glibc.
|
|
# · `gh` el espejo a GitHub empuja por HTTPS con su token como credential helper.
|
|
# · `hcloud` para ver la flota de Hetzner sin salir de la jaula.
|
|
#
|
|
# Los wrappers viven en /work/<usuario>/bin, que es bind del anfitrión y SOBREVIVE al recreate; lo
|
|
# que se pierde es el enlace en el PATH, y eso es lo que repone esto. Cada wrapper elige el artefacto
|
|
# MÁS RECIENTE del store en vez de un hash cableado, para sobrevivir a un re-sellado.
|
|
BIN=/work/$U/bin
|
|
if [ -d "$BIN" ]; then
|
|
install -d -o "$U" -g "$U" "$UP/usr/bin"
|
|
for t in takana patch gh hcloud; do
|
|
[ -x "$BIN/$t" ] || continue
|
|
ln -sfn "$BIN/$t" "$UP/usr/bin/$t" 2>/dev/null || true
|
|
done
|
|
echo " ✓ takana/patch/gh/hcloud enlazados al PATH desde $BIN"
|
|
fi
|
|
|
|
echo "jaula $INST preparada — lo del manifiesto (paquetes, concesiones) lo pone `takana qorpa provision`"
|