diff --git a/docs/28-servidor-de-produccion.md b/docs/28-servidor-de-produccion.md index 6db16715..1dddb658 100644 --- a/docs/28-servidor-de-produccion.md +++ b/docs/28-servidor-de-produccion.md @@ -1964,6 +1964,86 @@ aplicarlo**: las reglas cargan, el `nft -c` pasa, los sitios responden, y el da un cliente REAL hace lo que los clientes reales hacen. El único control que lo habría cazado antes es el que no corrí: **una ráfaga de conexiones desde una IP que NO esté en `confiables`**. +### 6.34 📋 Censo del 2026-09-16: queda UN vhost — y el censo sin privilegio no veía 572 M + +Censo fresco de gioser (`censar.py --local --probe-dns`, sólo lee), para saber **qué falta de +verdad** en vez de arrastrar la lista del 11. + +``` + servicios vivo 23 · no-declarado 17 · declarado-muerto 81 · descartados 6 + dominios apunta-aca 1 · ya-mudado 6 · fosil-sin-dns 7 + datos /home 25 G (copiar 27 G) · /var/lib 8,1 G · /opt 1,2 G · /var/www 288 M +``` + +**De los 29 dominios del Caddyfile sólo UNO sigue sirviéndose desde gioser: `sergio.gioser.net`.** +Comprobado desde fuera, resolución + HTTP, no por el fichero de config: + +| dominio | resuelve a | HTTP | +|---|---|---| +| `tawasuyu.net` · `gioser.net` · `git.tawasuyu.net` · `takana.gioser.net` · `hifas.gioser.net` · `api.sergio.gioser.net` | `2.29.29.217` | 200 | +| `gitea.gioser.net` | `2.29.29.217` | 301 | +| **`sergio.gioser.net`** | **`204.168.193.248` (gioser)** | 200 | +| `api.summa.gioser.net` | `154.197.1.2` (otra máquina) | — | +| `terapeuta.ec`, `andino.ec`, `api.sigma…` y 4 más | **sin DNS** | — | + +⇒ la lista de «tres vhosts» del §9 quedó vieja: `gioser.net` y `tawasuyu.net` ya están mudados +(§6.26) con su origen vivo a propósito. **Lo único que ata el DNS a gioser es la consola de `sergio`.** + +#### El rescate del §6.20 sigue vigente — y caduca de verdad + +Seis procesos corren hoy desde un **inodo borrado**. Cuatro tienen su copia en +`work/mudanza/rescate/` y el sha256 del `/proc//exe` vivo **coincide bit a bit** con lo +rescatado el 12. Los otros dos (`/usr/local/bin/tejido` y `/usr/local/bin/shuma-daemon`) **no están +en el rescate**, pero en disco hay una versión **más nueva** de cada uno (hoy 11:13 y 14:20): no se +perdió nada porque el usuario los está reconstruyendo. La lección es la del §6.20 y no cambió: *ese +paso caduca, y quién caducó se ve mirando `/proc`, no el directorio*. + +#### 🕳️ Y el hallazgo del día: `-e` contesta «no existe» cuando lo que pasa es «no puedo mirar» + +El censo tiene tres estados a propósito (`ausente` / `sin_permiso` / `ok`) porque un `ls` sin +permiso se lee igual que un directorio vacío. Pero el tercer estado sólo se detectaba **sobre la +propia ruta**: cuando el que no deja pasar es un **ANCESTRO**, `[ -e "$p" ]` contesta que no, y la +ruta caía en `ausente` — que el censo **descarta sin decir nada**. + +``` + /root/.local drwx------ root root ← 0700 + /root/.local/bin 572 M de binarios a mano que NINGÚN paquete provee +``` + +Corrido como `sergio`, el censo decía que `/root/.local/bin` no existe. Corrido como root, son +**572 M** de exactamente la clase que la mudanza no puede reconstruir: binarios puestos a mano. +Arreglado —si un ancestro existe y no es atravesable, el estado es `sin_permiso`— y probado en los +dos sentidos, que es lo que distingue un guardián de un adorno: + +``` + ancestro 0700 de otro dueño → sin_permiso (y sale en el ⚠ del resumen) + ruta que NO existe de verdad → ausente (y NO se reporta) ← el control que tiene que pasar +``` + +Con el arreglo, el censo como `sergio` pasa de **2 rutas ciegas a 13** — entre ellas el home entero +de `artix`, que antes no figuraba de ninguna manera. **El censo se corre como root**; la lista de +ciegas es el recibo de lo que una corrida sin privilegio no puede ver. + +#### Lo que queda, y por qué no lo decide el programa + +Descontados los que ya se decidieron (§6.30: `qdrant`, `act_runner`, `fail2ban` mueren) y los que +provee el init del destino, lo que sigue vivo en gioser es **un montón de daemons de tawasuyu que +ningún paquete provee** — binarios a mano en `/usr/local/bin` (1,2 G) y `~/.local/bin` (765 M + 572 M +en el home de root): + +``` + shuma-daemon · shuma-gateway (:7378) la consola de sergio.gioser.net — el único vhost que queda + willay-daemon · willay-crosscheck · tejido (:4102,:33097) · pacha · pacha-secretos + matilda · tupu · thasnuna · sandokan-watch · openclaw · zeroclaw + puerta-f6e393ffbef3999e (:34221,:44961) binario de test en target/debug, BORRADO del disco + webhook-deploy.py +``` + +`planear.py --revisar` los saca con su motivo, pero **la decisión es del usuario y el programa no la +va a inventar**: son las herramientas propias del usuario, no servicios de un catálogo. Y los +**datos** tampoco se recomiendan solos (§3): `/home` son 25 G que incluyen 6,8 G de `.claude` y +1,3 G de binarios sueltos. + ## 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: @@ -2040,9 +2120,12 @@ receta usa uno. ### Lo que falta para el cutover, en este orden ⚠ **La lista de abajo es del 2026-09-11 y los pasos 1-5 YA ESTÁN** (gitea mudado y sirviendo, §6.14; -estáticos mudados, §6.18; fósiles barridos, §6.19; `api.sergio` mudado, §6.25). El estado vigente de -la mudanza está en la tabla del final de §6.25; lo que sigue vivo en gioser son **tres vhosts** -(`sergio` con su `/shuma`, `gioser.net`/`www`, `tawasuyu.net`) y los servicios del censo. +estáticos mudados, §6.18; fósiles barridos, §6.19; `api.sergio` mudado, §6.25; `tawasuyu.net` y +`gioser.net` mudados, §6.26; squid, §6.27-28; hora, latido y rotación, §6.29; cortafuegos, §6.31-33). + +**El estado vigente está medido en el §6.34 (2026-09-16), y es más corto de lo que decía esta lista: +queda UN solo vhost sirviéndose desde gioser — `sergio.gioser.net` — y el montón de daemons propios +del usuario que ningún paquete provee.** Los pasos 6 y 7 son lo único que sigue abierto. 1. **Actualizar la caja a la imagen nueva** (gitea + `arjectl` + las cuentas de `[[user]]`). diff --git a/scripts/mudanza/censar.py b/scripts/mudanza/censar.py index f4b43960..a8e749d0 100755 --- a/scripts/mudanza/censar.py +++ b/scripts/mudanza/censar.py @@ -592,6 +592,14 @@ def fuera_de_git(t): «no hay nada» de lo que no pudo mirar. Por eso hay `sin_permiso`, que es un dato distinto y va al resumen: lo ausente se descarta, lo no mirado se vuelve a mirar con quien pueda. + ⚠ Y el tercer estado hay que buscarlo TAMBIÉN hacia arriba (2026-09-16). La primera versión sólo + lo detectaba cuando la ruta se podía *stat*ear pero no listar; cuando el que no deja pasar es un + ANCESTRO, `[ -e ]` contesta que no existe y la ruta caía en `ausente`, que el censo descarta sin + decir nada. Medido en gioser: `/root/.local` es 0700, así que un censo corrido como `sergio` + reportaba `/root/.local/bin` como si no estuviera — **572 M de binarios puestos a mano que + ningún paquete provee**, justo la clase que la mudanza no puede reconstruir. Corriendo como + root aparecen. Ahora, si un ancestro existe y no es atravesable, el estado es `sin_permiso`. + Todo en UNA sola llamada (un script generado) y no una por ruta: por SSH, N rutas × M homes son decenas de conexiones y el censo tarda más que el trabajo que describe. """ @@ -610,10 +618,21 @@ def fuera_de_git(t): q = shlex.quote(ruta) prog.append( f'p={q}; ' - 'if [ ! -e "$p" ]; then st=ausente; ' - 'elif [ -f "$p" ]; then st=fichero; ' - 'elif ls -1 "$p" >/dev/null 2>&1; then st=ok; ' - 'else st=sin_permiso; fi; ' + # `-e` responde NO tanto para «no está» como para «no puedo mirar»: si un ANCESTRO no es + # atravesable, el test falla igual que si la ruta no existiera. Medido: /root/.local es + # 0700, y un censo corrido como `sergio` daba /root/.local/bin por AUSENTE — 572 M de + # binarios a mano que ningún paquete provee, desaparecidos en silencio del censo. + 'if [ -e "$p" ]; then ' + ' if [ -f "$p" ]; then st=fichero; ' + ' elif ls -1 "$p" >/dev/null 2>&1; then st=ok; ' + ' else st=sin_permiso; fi; ' + 'else ' + ' st=ausente; d=${p%/*}; ' + ' while [ -n "$d" ]; do ' + ' if [ -e "$d" ]; then [ -x "$d" ] || st=sin_permiso; break; fi; ' + ' d=${d%/*}; ' + ' done; ' + 'fi; ' 'sz=$(timeout 20 du -shx "$p" 2>/dev/null | cut -f1); ' 'printf "P\t%s\t%s\t%s\n" "$p" "$st" "${sz:-?}"; ' '[ "$st" = ok ] && ls -1 "$p" 2>/dev/null | head -20 | sed "s/^/E\t/"; ' @@ -920,8 +939,8 @@ def resumen(c): if ciegos: # Un `ls` sin permiso se lee IGUAL que un directorio ausente. Decirlo es la diferencia # entre «no hay nada» y «no pude mirar», y sólo una de las dos se puede descartar. - print(f" ⚠ {len(ciegos)} ruta(s) EXISTEN y el censo no pudo leerlas " - f"(corré el censo como su dueño o con privilegio):") + print(f" ⚠ {len(ciegos)} ruta(s) que el censo NO PUDO MIRAR — no es lo mismo que " + f"ausentes (corré el censo como su dueño o con privilegio):") for f in ciegos: print(f" {f['path']}") if c["datos"]: