From f32b28c63e0f0b95a21cb1a7bdebbfea84712a45 Mon Sep 17 00:00:00 2001 From: Sergio Date: Wed, 16 Sep 2026 19:07:20 +0000 Subject: [PATCH] =?UTF-8?q?CUTOVER=20de=20`sergio.gioser.net`:=20el=20?= =?UTF-8?q?=C3=BAltimo=20vhost=20sale=20de=20gioser=20=E2=80=94=20ya=20no?= =?UTF-8?q?=20queda=20ninguno?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `https://sergio.gioser.net/` y `/shuma/` contestan 200 contra 2.29.29.217, con TLS público. **Ningún dominio resuelve ya a gioser.** ⚠ Y el 200 de `/shuma/` había que mirarlo dos veces: el bloque termina en `try_files … /index.html`, así que CUALQUIER ruta inexistente devuelve 200 con la SPA — un `curl -o /dev/null` habría dado el mismo verde con el proxy mal puesto. Lo que decide es el cuerpo (17 bytes: «shuma-gateway ok») y el control es compararlo con lo que el origen viejo sigue sirviendo. Igual con `/shuma/rpc`: las dos máquinas contestan el mismo 400 con el mismo JSON. Lo construido, en orden: las dos recetas sellaron en el worker con los hashes anticipados y salen `statically linked` (nada de cargador, que era lo que le faltaba al binario glibc de gioser) · la cuenta `shuma` declarada con `[[user]]`, uid 967, y el descenso con `setuidgid` en el argv de la Card porque el payload Native de arje NO tiene campo de usuario · la config que no se regenera (`gateway-token`, `identity.x25519`) instalada, y la guarda de la Card la EXIGE en vez de crearla: un token nuevo deja fuera a los clientes ya emparejados · las dos Cards en `cards.d` Y en el genesis de la semilla · las dos recetas y los dos labels declarados en `perfil.servidor`. ⚠ LA SHELL DE LA CUENTA NO ES UN DETALLE: el default de `[[user]]` es `/bin/false` —correcto para gitea o squid, exactamente lo contrario para un demonio que abre PTYs—. Con la shell inerte el servicio arranca, se supervisa, contesta 200 y cada pestaña muere al instante: un fallo que no se ve al desplegar, se ve al usarlo. Va `shell = "/bin/sh"`. `[[user]]` y `[[service]]` están fuera de `hash_inputs`: declarar todo eso no movió ningún ArtifactHash, comprobado antes y después en los dos. Y una corrección al §6.25: para cambiar el VALOR de un rrset no hace falta DELETE+POST (que deja una ventana sin registro). La API tiene `POST /v1/zones//rrsets///actions/set_records`, que es atómica. Con TTL 60 la resolución pública cambió en menos de 8 segundos. El origen de gioser sigue corriendo a propósito, como en §6.26: volver atrás es una llamada. Co-Authored-By: Claude Opus 5 (1M context) --- docs/28-servidor-de-produccion.md | 67 +++++++++++++++++++++++++++++++ docs/state/targets.toml | 12 +++++- recipes/shuma-daemon.toml | 36 +++++++++++++++++ recipes/shuma-gateway.toml | 21 ++++++++++ 4 files changed, 135 insertions(+), 1 deletion(-) diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md index 43df6e35..cc601bc0 100644 --- a/docs/28-servidor-de-produccion.md +++ b/docs/28-servidor-de-produccion.md @@ -2153,6 +2153,73 @@ por criterio ajeno. De `/var/lib` (8,1 G) se movió sólo el estado de los daemons que viven: **6,1 G son swap** y 1,9 G el gitea, que ya está mudado desde el §6.14. +### 6.37 ✅ CUTOVER DE `sergio.gioser.net` — el último vhost, y gioser deja de servir *(2026-09-16)* + +**Ya no queda ningún dominio sirviéndose desde gioser.** El último era éste, y lo que lo ataba no +era el sitio sino su consola: `/shuma/*`. Verificado desde fuera, contra la IP nueva: + +``` + https://sergio.gioser.net/ → 200 408 bytes, la SPA + https://sergio.gioser.net/shuma/ → 200 «shuma-gateway ok» ← el gateway, no la SPA + https://sergio.gioser.net/ruta-inventada → 200 la SPA (el fallback, que es lo correcto) +``` + +⚠ **El `200` de `/shuma/` había que mirarlo dos veces.** El bloque termina en +`try_files {path} {path}/ /index.html`, así que **cualquier ruta inexistente devuelve 200 con la +SPA**: un `curl -o /dev/null` que sólo mira el código habría dado el mismo verde con el proxy mal +puesto. Lo que decide es el CUERPO — 17 bytes que dicen `shuma-gateway ok` — y el control es +compararlo con lo que el origen viejo sigue sirviendo, byte por byte. Igual con `/shuma/rpc`: las +dos máquinas contestan el MISMO `400 {"error":"bad json: …"}`. + +#### Lo que hubo que construir, en orden + +1. **Las dos recetas** (§6.34) sellaron en el worker: `b3:107dfb54…-shuma-gateway` (6,3 M) y + `b3:f9b92acc…-shuma-daemon` (5,6 M) — los hashes que `takana hash` había anticipado antes de + encolarlas. `file` dice **`statically linked`**: nada de cargador, que era exactamente lo que le + faltaba al binario glibc de gioser (y cuyo inodo, además, ya estaba borrado del disco). +2. **La cuenta, declarada y sin privilegio.** `[[user]] shuma` (uid 967) en la receta del daemon, y + el descenso con `setuidgid` dentro del `argv` de la Card, igual que gitea — porque el payload + `Native` de arje **no tiene campo de usuario**. Declararlo «como estaba» lo habría puesto de + root, y esto es un demonio que abre PTYs y lanza shells. +3. **⚠ Y la shell de la cuenta NO es un detalle.** El default de `[[user]]` es `/bin/false`, que es + lo correcto para gitea o squid y **exactamente lo contrario** para éste: con la shell inerte el + servicio arranca, se supervisa, contesta 200… y cada pestaña que alguien abra muere al instante. + Un fallo que no se ve al desplegar: se ve al usarlo. Va `shell = "/bin/sh"`. +4. **La config que no se regenera.** El `gateway-token` (32 B) y `keys/identity.x25519` viajaron a + `/var/lib/shuma/.config/shuma/`: generar unos nuevos deja fuera a los clientes ya emparejados. + Por eso la guarda de la Card **exige** el token y no lo crea, y dice de dónde sale. +5. **Las dos Cards** entran en `/etc/arje/cards.d/` **y** en el `genesis` de `/ente/seed.card.json` + (son dos preguntas distintas: qué se puede encarnar a pedido y qué arranca solo). La semilla + quedó con 13 entradas y vuelve a parsear. +6. **Declarado en `perfil.servidor`**: los dos paquetes y los dos labels en `servicios`. Entran como + PAR — el gateway sin el daemon es un puente a ninguna parte. + +`[[user]]` y `[[service]]` están **fuera de `hash_inputs`**, así que declarar todo eso no movió +ningún ArtifactHash: comprobado en los dos, antes y después. + +#### 🔁 El DNS: la API tiene una acción para esto y el §6.25 no la había encontrado + +Aquel cutover dejó escrito «un `PUT` sobre el rrset no puede cambiar el tipo: hay que `DELETE` del +CNAME y `POST` del A». Es cierto para un cambio de TIPO. Para cambiar el VALOR, el `PUT` tampoco +sirve —`can't update records with this endpoint`— pero existe una acción que lo hace **atómica**: + +``` +POST /v1/zones//rrsets///actions/set_records {"records":[{"value":"…"}]} +``` + +Sin ventana sin registro, que es lo que sí tiene el `DELETE`+`POST`. Con TTL 60 la resolución +pública cambió **en menos de 8 segundos**, y volver atrás es la misma llamada con la IP vieja. + +#### El origen sigue vivo, a propósito + +Como en §6.26: en gioser `shuma-daemon`, `shuma-gateway` y su bloque de caddy **siguen corriendo**. +Mudar el nombre y dejar el origen encendido permite volver en un minuto, y no cuesta nada mientras +la máquina exista. Lo que ya no existe es la dependencia: **ningún dominio resuelve a gioser**. + +⚠ Pendiente menor, anotado donde se va a leer: el daemon avisa al arrancar que no encuentra +`shuma-askpass` («sudo/ssh pedirán la clave por el TTY»). Es otro crate del mismo workspace y es +otra receta — no bloquea la consola. + ## 7. Reusar los scripts que ya existen, y no escribir de nuevo Pedido explícito del usuario. El inventario de lo que ya hace el trabajo: diff --git a/docs/state/targets.toml b/docs/state/targets.toml index 43764965..c889dd1f 100644 --- a/docs/state/targets.toml +++ b/docs/state/targets.toml @@ -1325,6 +1325,10 @@ paquetes = [ # copiar un directorio — lo que NO es trivial es el binario, ver el encabezado de `recipes/gitea.toml`. # Su `[[service]]` está en la receta y el perfil lo arranca (abajo, en `servicios`). "gitea", + # `shuma-daemon` / `shuma-gateway` (2026-09-16): la consola remota que sirve `/shuma/*` en + # `sergio.gioser.net`. Construidos desde el monorepo de tawasuyu por decisión del usuario, en vez + # de mudar el binario glibc de gioser —cuyo inodo, además, ya estaba BORRADO del disco. + "shuma-daemon", "shuma-gateway", # `curl`/`wget`: bajar del mirror y del repo. `takana install --repo https://…` los necesita del # lado del cliente, y son también la única forma de diagnosticar un origen caído desde la caja. "curl", "wget", @@ -1428,4 +1432,10 @@ paquetes = [ # métrica reclama: el latido y la hora. Sus paquetes ya estaban declarados arriba y sellados —lo que # faltaba era el eslabón que los ARRANCA, que es este. Una caja con `cronie` instalado y sin `crond` # corriendo se ve idéntica a una con latido, y no rota un log ni respalda nada. -servicios = ["sshd", "gitea", "caddy", "minga", "squid", "crond", "chronyd"] +# ⚠ `shuma-daemon` y `shuma-gateway` (2026-09-16): la consola de `sergio.gioser.net`, que es el +# ÚLTIMO vhost que quedaba atado a gioser (SDD 28 §6.34). Entran como PAR: el gateway sin el daemon +# es un puente a ninguna parte, y por eso los dos labels van juntos o no va ninguno. +# La cuenta `shuma` la declara `recipes/shuma-daemon.toml` con `[[user]]`, sin privilegio y con +# `shell = /bin/sh` —no `/bin/false`—, porque este demonio existe para abrir PTYs: con la shell +# inerte el servicio arranca, se supervisa, y cada pestaña muere al instante. +servicios = ["sshd", "gitea", "caddy", "minga", "squid", "crond", "chronyd", "shuma-daemon", "shuma-gateway"] diff --git a/recipes/shuma-daemon.toml b/recipes/shuma-daemon.toml index f77d7263..a91d81b0 100644 --- a/recipes/shuma-daemon.toml +++ b/recipes/shuma-daemon.toml @@ -24,3 +24,39 @@ compiler = "zig-cc" target = "x86_64-linux-musl" link = "static" flags = ["-p", "shuma-daemon", "--bin", "shuma-daemon"] + +# ── LA CUENTA (SDD 30) ────────────────────────────────────────────────────────────────────────── +# Fuera de `hash_inputs`: declarar esto NO re-hashea shuma-daemon (control corrido, el hash no se +# movió). +# +# ⚠ EN GIOSER CORRE COMO `sergio`, Y ESO NO SE PUEDE TRADUCIR SOLO. El payload `Native` de arje no +# tiene campo de usuario (SDD 29 §4.0 quinquies), así que una tarjeta escrita «como estaba» lo +# pondría de ROOT — y esto es un demonio que abre PTYs y ejecuta shells: correrlo de root no es +# perder una opción, es entregar la máquina. La cuenta es propia y sin privilegio, y el descenso se +# hace en el comando con `setuidgid`, igual que gitea. +[[user]] +name = "shuma" +uid = 967 +home = "/var/lib/shuma" +# ⚠ Y LA SHELL NO ES UN DETALLE ACÁ. El default de `[[user]]` es `/bin/false`, que es lo correcto +# para un demonio (gitea, squid) y **lo exactamente contrario** para éste: shuma-daemon existe para +# abrir PTYs y lanzar shells. Con `/bin/false` el servicio arranca, se supervisa, contesta… y cada +# pestaña que alguien abra muere al instante. Un fallo que no se ve al desplegar: se ve al usarlo. +shell = "/bin/sh" + +# ── EL SERVICIO ───────────────────────────────────────────────────────────────────────────────── +# Las guardas salen 78 NOMBRANDO EL ARREGLO. Es lo que hace la diferencia entre un servicio que +# reintenta para siempre con «unknown user» y uno que dice qué falta: medido con gitea, cuya imagen +# arrancaba perfecta y sin el servicio por el que existe. +# +# `XDG_RUNTIME_DIR` no es decoración: es donde el daemon pone su socket, y es por ese socket que el +# gateway le habla. En gioser vale `/run/shuma`, medido en el entorno del proceso vivo. +[[service]] +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"]] +networking = "full" +cgroup = "arje.slice/shuma-daemon" +restart = { initial_ms = 1000, max_ms = 30000 } diff --git a/recipes/shuma-gateway.toml b/recipes/shuma-gateway.toml index 39f85cd1..efd2dbb8 100644 --- a/recipes/shuma-gateway.toml +++ b/recipes/shuma-gateway.toml @@ -32,3 +32,24 @@ target = "x86_64-linux-musl" # exactamente lo que le falta hoy al binario de gioser, que es ELF dinámico contra glibc. link = "static" flags = ["-p", "shuma-gateway", "--bin", "shuma-gateway"] + +# ── EL SERVICIO (SDD 30) ──────────────────────────────────────────────────────────────────────── +# Fuera de `hash_inputs`: declarar esto NO re-hashea el gateway. +# +# La cuenta la declara `shuma-daemon` —es la misma— y por eso acá no va otro `[[user]]`: dos recetas +# declarando la misma cuenta con distinto uid abortan la imagen a propósito (`takana users --merge`). +# +# ⚠ DOS GUARDAS, Y LAS DOS SALEN 78 DICIENDO EL ARREGLO: +# 1. sin el usuario, esto correría de root — y es el puente HTTP hacia un demonio que abre shells; +# 2. sin `gateway-token` no autentica a nadie. El fichero son 32 bytes que en gioser viven en +# `~/.config/shuma/gateway-token`; si se genera uno nuevo, los clientes ya emparejados dejan de +# entrar. Por eso la guarda no lo crea: lo EXIGE y nombra de dónde sale. +[[service]] +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"]] +networking = "full" +cgroup = "arje.slice/shuma-gateway" +restart = { initial_ms = 1000, max_ms = 30000 }