From e7b77ff657c6ac09ac700c003290b47f081f1e53 Mon Sep 17 00:00:00 2001 From: Sergio Date: Thu, 17 Sep 2026 18:31:30 +0000 Subject: [PATCH] =?UTF-8?q?la=20cuenta=20de=20sergio=20en=20la=20caja,=20s?= =?UTF-8?q?u=20jaula=20propia,=20y=20shuma=20corriendo=20como=20=C3=A9l?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pedido: «crea mi cuenta sergio con la clave tawasuyu, desde ahí abrir los claudes como lo estoy haciendo acá con shuma sobre los proyectos». · cuenta `sergio` uid 1001 (el mismo que en gioser), home /home/sergio, clave verificada · su propia jaula `claude-sergio`, que concede SU home —su `.claude`: credenciales y memoria— y NO el de root. Con la instancia de root, sergio moría en «bwrap: Can't mkdir /root/.claude» · `claude` elige instancia por usuario; `PROY=/otra/ruta claude` abre el agente sobre otro proyecto · shuma-daemon y shuma-gateway pasan a correr como `sergio`, no como la cuenta de servicio `shuma`: shuma es su CONSOLA, y cada pestaña que abre tiene que ser una sesión suya — si no, `claude` buscaría una jaula `claude-shuma` que no existe ⚠ NO se eleva con doas/sudo: en esta caja el setuid NO TOMA («doas: Operation not permitted», con NoNewPrivs 0 y `/` sin nosuid — queda como deuda entender por qué). No hace falta: `qorpa run` levanta la jaula con user namespaces y anda sin privilegio, siempre que el directorio de la instancia sea del usuario. ⚠ Y un detalle que costó una vuelta: sin `shift 2`, el HOME y el proyecto que el lanzador pasa como posicionales se cuelan COMO ARGUMENTOS del agente — la primera sesión se abrió con el prompt «/home/sergio», y el agente contestó, con razón, que eso es una ruta y no un comando. Co-Authored-By: Claude Opus 5 (1M context) --- recipes/shuma-daemon.toml | 11 +++++++-- recipes/shuma-gateway.toml | 11 +++++++-- scripts/servidor/claude-caja.sh | 44 +++++++++++++++++++++++---------- 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/recipes/shuma-daemon.toml b/recipes/shuma-daemon.toml index 4f0bc226..6e6d91ce 100644 --- a/recipes/shuma-daemon.toml +++ b/recipes/shuma-daemon.toml @@ -55,8 +55,8 @@ shell = "/bin/sh" label = "shuma-daemon" id = "01M2EKDA00SH0MADAEM0N7X4K2" exec = "/bin/busybox" -argv = ["sh", "-c", "/bin/grep -q '^shuma:' /etc/passwd || { echo 'shuma-daemon: no existe el usuario `shuma` en /etc/passwd — la receta lo declara con [[user]]; la imagen lo compone con `takana users`' >&2; exit 78; }; mkdir -p /run/shuma /var/lib/shuma || exit 78; chown shuma:shuma /run/shuma /var/lib/shuma || exit 78; cd /var/lib/shuma || exit 78; exec /usr/bin/setuidgid shuma /usr/bin/shuma-daemon"] -envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/shuma"], ["USER", "shuma"], ["XDG_RUNTIME_DIR", "/run/shuma"]] +argv = ["sh", "-c", "/bin/grep -q '^sergio:' /etc/passwd || { echo 'shuma-daemon: no existe el usuario `sergio` en /etc/passwd — la receta lo declara con [[user]]; la imagen lo compone con `takana users`' >&2; exit 78; }; mkdir -p /run/shuma /var/lib/shuma || exit 78; chown shuma:shuma /run/shuma /var/lib/shuma || exit 78; cd /home/sergio || exit 78; exec /usr/bin/setuidgid sergio /usr/bin/shuma-daemon"] +envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/home/sergio"], ["USER", "sergio"], ["XDG_RUNTIME_DIR", "/run/shuma"]] networking = "full" cgroup = "arje.slice/shuma-daemon" restart = { initial_ms = 1000, max_ms = 30000 } @@ -76,3 +76,10 @@ restart = { initial_ms = 1000, max_ms = 30000 } # # ⇒ el aviso del daemon es para el escritorio, no para el servidor. Si algún día hace falta acá, # primero hay que resolver el proc-macro de atspi, no reponer la receta. + +# ── ⚠ CORRE COMO `sergio`, NO COMO `shuma` (2026-09-17) ───────────────────────────────────────── +# La cuenta `shuma` se creó cuando la caja no tenía usuario de la persona. Ahora sí, y shuma es su +# CONSOLA: las pestañas que abre son sesiones suyas, y desde ahí lanza `claude`, que entra a la +# jaula `claude-sergio` con SU memoria y SUS credenciales. Con el daemon corriendo como `shuma`, +# cada terminal de la web habría sido de otro usuario que no es nadie — y `claude` habría buscado +# una jaula `claude-shuma` que no existe. diff --git a/recipes/shuma-gateway.toml b/recipes/shuma-gateway.toml index efd2dbb8..3f2d7940 100644 --- a/recipes/shuma-gateway.toml +++ b/recipes/shuma-gateway.toml @@ -48,8 +48,15 @@ flags = ["-p", "shuma-gateway", "--bin", "shuma-gateway"] label = "shuma-gateway" id = "01M2EKDA00SH0MAGATEWAY4K21" exec = "/bin/busybox" -argv = ["sh", "-c", "/bin/grep -q '^shuma:' /etc/passwd || { echo 'shuma-gateway: falta el usuario `shuma` — lo declara recipes/shuma-daemon.toml' >&2; exit 78; }; test -s /var/lib/shuma/.config/shuma/gateway-token || { echo 'shuma-gateway: falta /var/lib/shuma/.config/shuma/gateway-token (32 B) — viene de ~/.config/shuma/gateway-token del origen; si generás uno nuevo, los clientes emparejados dejan de entrar' >&2; exit 78; }; cd /var/lib/shuma || exit 78; exec /usr/bin/setuidgid shuma /usr/bin/shuma-gateway"] -envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/var/lib/shuma"], ["USER", "shuma"], ["XDG_RUNTIME_DIR", "/run/shuma"]] +argv = ["sh", "-c", "/bin/grep -q '^sergio:' /etc/passwd || { echo 'shuma-gateway: falta el usuario `sergio` — lo declara recipes/shuma-daemon.toml' >&2; exit 78; }; test -s /home/sergio/.config/shuma/gateway-token || { echo 'shuma-gateway: falta /home/sergio/.config/shuma/gateway-token (32 B) — viene de ~/.config/shuma/gateway-token del origen; si generás uno nuevo, los clientes emparejados dejan de entrar' >&2; exit 78; }; cd /home/sergio || exit 78; exec /usr/bin/setuidgid sergio /usr/bin/shuma-gateway"] +envp = [["PATH", "/usr/bin:/bin"], ["HOME", "/home/sergio"], ["USER", "sergio"], ["XDG_RUNTIME_DIR", "/run/shuma"]] networking = "full" cgroup = "arje.slice/shuma-gateway" restart = { initial_ms = 1000, max_ms = 30000 } + +# ── ⚠ CORRE COMO `sergio`, NO COMO `shuma` (2026-09-17) ───────────────────────────────────────── +# La cuenta `shuma` se creó cuando la caja no tenía usuario de la persona. Ahora sí, y shuma es su +# CONSOLA: las pestañas que abre son sesiones suyas, y desde ahí lanza `claude`, que entra a la +# jaula `claude-sergio` con SU memoria y SUS credenciales. Con el daemon corriendo como `shuma`, +# cada terminal de la web habría sido de otro usuario que no es nadie — y `claude` habría buscado +# una jaula `claude-shuma` que no existe. diff --git a/scripts/servidor/claude-caja.sh b/scripts/servidor/claude-caja.sh index 0a943b2f..c8a860af 100755 --- a/scripts/servidor/claude-caja.sh +++ b/scripts/servidor/claude-caja.sh @@ -2,19 +2,37 @@ # claude — entrar al agente en esta caja. El CLI es glibc y la caja es musl: corre enjaulado # (ADR 0015), con el repo, el store, el lab, la memoria y las llaves concedidos y nada más. # +# ── UNA JAULA POR PERSONA, Y SIN PRIVILEGIO ───────────────────────────────────────────────────── +# `root` usa la instancia `claude`; cada usuario usa `claude-`. No es purismo: la instancia +# concede el HOME de quien entra —su `.claude`, o sea sus credenciales y su memoria— y una sola +# instancia obligaría a compartirlo. Medido: con la instancia de root, `sergio` muere en +# `bwrap: Can't mkdir /root/.claude: Permission denied`. +# +# ⚠ Y NO se eleva con `doas`/`sudo`: en esta caja **el setuid no toma** («doas: Operation not +# permitted», con `NoNewPrivs: 0` y `/` sin `nosuid` — queda como deuda entender por qué). No hace +# falta: `qorpa run` levanta la jaula con user namespaces y anda **sin privilegio**, siempre que el +# directorio de la instancia sea del usuario. +# # ⚠ LOS BUILDS NO CORREN DENTRO DE LA JAULA. `takana build` levanta su propio bwrap y ahí falla con -# «Can't mount proc on /proc» aunque la instancia declare `nesting = true` (un `bwrap --proc /proc` -# a mano SÍ funciona adentro: lo que falla es el userns anidado que arma el sandbox del build). -# El camino que sí anda —y el que el agente debe usar— es pedírselo al ANFITRIÓN por ssh: +# «Can't mount proc on /proc» aunque la instancia declare `nesting = true`. El camino que sí anda es +# pedírselo al ANFITRIÓN por ssh: # -# ssh -p 22022 -i /root/.ssh/github5 root@127.0.0.1 \ -# 'cd /opt/takana && flock -o work/.farm-build.lock takana --store /store build ' +# ssh -p 22022 root@127.0.0.1 'cd /opt/takana && flock -o work/.farm-build.lock takana build ' +set -eu + +_yo="$(id -un)" +_hogar="${HOME:-/root}" +_inst="claude" +[ "$_yo" != "root" ] && _inst="claude-$_yo" + +# El `cd` no es comodidad: la memoria del agente se indexa POR RUTA DE TRABAJO. Desde `/` —que es +# donde `qorpa run` deja el shell— la sesión usa el proyecto `-` y el agente empieza sin saber nada +# aunque su memoria esté en disco. `PROY` permite abrir el agente sobre OTRO proyecto: # -# Comprobado el 2026-09-17: así se selló `sof-firmware` desde dentro de la jaula. -# ⚠ EL `cd` NO ES COMODIDAD: la memoria del agente se indexa POR RUTA DE TRABAJO. Arrancando desde -# `/` —que es donde `qorpa run` deja el shell— la sesión usa el proyecto `-` y el agente **empieza -# sin saber nada**, aunque sus 163 ficheros de memoria estén en disco. Desde `/opt/takana` usa -# `-opt-takana`, que es donde se renombró la memoria que vino de gioser (allá el repo era -# `/mnt/vvv/takana`). Comprobado: preguntado por el logo, contesta «martillo 7×7, cuatro colores -# exactos», que es exactamente lo que dice su memoria. -exec takana qorpa run claude -- sh -c '''cd /opt/takana && exec env HOME=/root TERM="${TERM:-xterm-256color}" /work/claude-cli/2.1.240 "$@"''' -- "$@" +# PROY=/opt/tawasuyu claude +# ⚠ El `shift 2` no es adorno: sin él, el HOME y el proyecto que se pasan como posicionales se +# cuelan COMO ARGUMENTOS del agente. Medido: la primera versión abrió la sesión con el prompt +# «/home/sergio», y el agente contestó —con razón— que eso es una ruta, no un comando. +exec takana qorpa run "$_inst" -- sh -c \ + 'hogar="$1"; proy="$2"; shift 2; cd "$proy" && exec env HOME="$hogar" TERM="${TERM:-xterm-256color}" /work/claude-cli/2.1.240 "$@"' \ + _ "$_hogar" "${PROY:-/opt/takana}" "$@"