From d52824efebbb0b4eb0b9437640d7cc9e51b75514 Mon Sep 17 00:00:00 2001 From: Sergio Date: Tue, 15 Sep 2026 17:58:17 +0000 Subject: [PATCH] =?UTF-8?q?mudados=20tawasuyu.net=20y=20gioser.net=20?= =?UTF-8?q?=E2=80=94=20y=20los=20padres=20que=20inventa=20bwrap=20eran=200?= =?UTF-8?q?700?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Los dos sitios que quedaban con trafico real sirven desde la caja `takana`, con TLS publico y verificados desde fuera. Los servicios viejos SIGUEN corriendo en gioser a proposito (decision del usuario: mover el DNS y dejar el origen vivo, para poder volver en un minuto). · `tawasuyu.net` + `www`: la raiz en gioser era EL MONOREPO ENTERO (145 G) servido por HTTP. El log de accesos dice que las unicas rutas con trafico son `/`, `/descargas` y `/web`, asi que se replicaron solo los dos subarboles que el sitio usa (1,2 M + 3,8 G) con la MISMA estructura, para que cada `rewrite` siga siendo el mismo. · `gioser.net` + `www`: 428 M de estaticos + `/reencuentro`, que necesita PHP. `/hooks/*` NO se muda: lo atiende `webhook-deploy.py`, que redespliega aura/sigma/summa/brahman — los cuatro FOSILES del §6.19 — y tiene cero peticiones. Muere con la caja vieja. · `sergio` dejo de ser `CNAME -> www` y tiene su A propio (apuntando a gioser, sin cambio visible). Sin eso, mover `www` se lo llevaba puesto: en esa zona casi todo cuelga de `www`. PHP no entra al corpus y no hace falta: `php-fpm 8.5.10` corre en la instancia `gioser-php` (ADR 0015), supervisado por arje. Control contra el original en los dos sentidos: POST con la trampa anti-robots da `{"ok":true}` 200 y GET da 405, igual que en gioser. EL MURO, que es general y no de PHP: para montar en `/work/www/gioser-web`, bwrap crea `/work` y `/work/www` —que la imagen no trae— y los crea **0700 root**. Adentro somos root y a mano todo funciona; pero un servicio que BAJA de privilegio (php-fpm a `http`, o cualquier instancia con `run_as`) no puede ni atravesarlos, y el sintoma es un «File not found» sobre un fichero que ESTA. Medido con el control que lo separa de un problema de permisos del anfitrion: como `http`, leer un fichero de la IMAGEN funciona y leer el directorio CONCEDIDO da Permission denied. `grants_to_args` crea ahora los ancestros que faltan con `--perms 0755 --dir`, y SOLO los que la imagen no trae: hacerlo sobre `/etc` o `/home` le cambiaria los modos a la imagen. Con test, y probado rompiendolo a proposito (sale `["/etc","/etc/php"]` en vez de `["/etc/php"]`). Co-Authored-By: Claude Opus 5 (1M context) --- crates/takana-cli/src/qorpa.rs | 68 +++++++++++++++++++++++++-- docs/28-servidor-de-produccion.md | 76 +++++++++++++++++++++++++++++++ 2 files changed, 139 insertions(+), 5 deletions(-) diff --git a/crates/takana-cli/src/qorpa.rs b/crates/takana-cli/src/qorpa.rs index ddbdb9ac..fc587f5b 100644 --- a/crates/takana-cli/src/qorpa.rs +++ b/crates/takana-cli/src/qorpa.rs @@ -1211,9 +1211,30 @@ fn provision(root: &Path, id: &str, force: bool, dry_run: bool) -> Result<()> { Ok(()) } +/// Los directorios INTERMEDIOS de un punto de montaje que la imagen no trae, de arriba hacia abajo. +/// +/// No incluye el punto de montaje en sí: ése lo tapa el `--bind` y hereda los modos del anfitrión. +/// Sólo devuelve los que faltan en la imagen, porque `--dir` sobre uno existente le cambiaría los +/// permisos a la imagen. Ver el comentario largo en `grants_to_args`. +fn ancestros_a_crear(dentro: &str, tree: &Path) -> Vec { + let mut out = Vec::new(); + let mut acc = String::new(); + let partes: Vec<&str> = dentro.trim_matches('/').split('/').collect(); + // `partes.len() - 1`: el último es el punto de montaje, no un ancestro. + for p in partes.iter().take(partes.len().saturating_sub(1)) { + if p.is_empty() { continue; } + acc.push('/'); + acc.push_str(p); + if !tree.join(acc.trim_start_matches('/')).is_dir() { + out.push(acc.clone()); + } + } + out +} + /// Traduce las concesiones a argumentos de bwrap. Devuelve también las advertencias que el usuario /// tiene que LEER, no descubrir: el ADR pide que el socket de Wayland se diga en la cara. -fn grants_to_args(g: &Grants, destino_xdg: &str, wl: Option<&Path>) -> (Vec, Vec) { +fn grants_to_args(g: &Grants, destino_xdg: &str, wl: Option<&Path>, tree: &Path) -> (Vec, Vec) { let mut a: Vec = Vec::new(); let mut warn: Vec = Vec::new(); // Sin dep de libc: el dueño de /proc/self ES el uid efectivo. @@ -1255,6 +1276,22 @@ fn grants_to_args(g: &Grants, destino_xdg: &str, wl: Option<&Path>) -> (Vec