🧨 el curl de la distro no puede validar TLS: el capath no tiene hash-links

Al traer la primera imagen ajena, la caja falló con «curl failed to verify the legitimacy of the
server». El certificado del servidor estaba bien; lo que falta es de este lado:

    * CApath: /etc/ssl/certs
    * SSL certificate ... unable to get local issuer certificate (20)
    $ curl --cacert /etc/ssl/certs/ca-certificates.crt ...   → 200

El curl del corpus se compila con `--with-ca-path=/etc/ssl/certs` y SIN CAfile, y ese directorio
tiene un solo fichero: el bundle. Un capath necesita hash-links (`c_rehash`) — el ÍNDICE — y no los
hay. Los certificados están; el índice no.

Por qué faltan: la receta deja el hook `/etc/ca-certificates/update.d/certhash`, que en Alpine
ejecuta `apk` al instalar. **En takana no lo ejecuta nadie**: la distro proyecta artefactos, no corre
post-install. Todo presente, y el paso que lo conecta no existe.

Alcance: no es sólo `qorpa pull`. Es `install --repo https://…`, el mirror y cualquier curl/wget de
una caja instalada. No falla al construir ni al instalar: falla la primera vez que la máquina intenta
bajar algo. No se había visto porque el hub usa el curl de Artix.

Arreglado en la receta: los hash-links se generan EN EL BUILD (donde c_rehash y perl existen) y
viajan en el artefacto, con una guarda que falla ruidosamente si el rehash no produce ninguno.
Re-sella `ca-certificates`, raíz de `perfil.base`. Apaño mientras tanto: `CURL_CA_BUNDLE=…` (200).

Y el estado de qorpa en la caja: preflight de «BLOQUEA 1 · LIMITA 4» a «LIMITA 1» — qorpa movido a
/work (la raíz tenía 3 G), subuid provisionado y `setcap` aplicado a newuidmap/newgidmap SIN tocar el
artefacto del store (inodes distintos: en una caja instalada esos binarios son copias). El pull baja
y verifica el sha256, y muere al desempacar: el rootfs de Arch es `.tar.zst` y el `tar` de la imagen
es el de busybox. `qorpa pull` necesita `zstd -dc | tar -x` para `.zst`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016Tf9T4vGzsMoT7eS8YzMFn
This commit is contained in:
Sergio
2026-09-14 21:27:38 +00:00
co-authored by Claude Opus 5
parent 146ace3ce5
commit 88decb0498
2 changed files with 75 additions and 0 deletions
+53
View File
@@ -1353,6 +1353,59 @@ servicio vivo no tiene hoy camino a takana**. Las salidas son tres y hay que ele
es para exactamente esto; (2) que ese servicio viva en otra máquina —`summa` ya lo hace, y su DNS lo
demuestra—; o (3) que muera. Ninguna es técnica: es una decisión.
### 6.22 🧨 El `curl` de la distro NO PUEDE VALIDAR TLS — y por eso nada se baja por HTTPS
Al ir a traer la primera imagen ajena (`qorpa pull`), la caja falló con `curl failed to verify the
legitimacy of the server`. El certificado del servidor estaba bien; lo que falta es de este lado:
```
* CApath: /etc/ssl/certs
* SSL certificate ... unable to get local issuer certificate (20)
$ curl --cacert /etc/ssl/certs/ca-certificates.crt ... → 200
```
El `curl` del corpus está compilado **con `--with-ca-path=/etc/ssl/certs` y sin CAfile**, y ese
directorio contiene **un solo fichero**: el bundle. Un *capath* necesita **hash-links**
(`c_rehash`) — el índice — y no los hay. Los certificados están, el índice no.
**Por qué faltan**: `recipes/ca-certificates.toml` deja escrito el hook
`/etc/ca-certificates/update.d/certhash`, que en Alpine ejecuta `apk` al instalar. **En takana no lo
ejecuta nadie**: la distro proyecta artefactos, no corre post-install. Es la misma familia que
[[subcomando-sin-driver]] — todo presente, y el paso que lo conecta no existe.
**Alcance**: no es sólo `qorpa pull`. Es `takana install --repo https://…`, el mirror, y cualquier
`curl`/`wget` de un script en una caja instalada. **No falla al construir ni al instalar: falla la
primera vez que la máquina intenta bajar algo.** Nadie lo había visto porque el hub usa el `curl` de
Artix.
**Arreglado en la receta**: los hash-links se generan **en el build** —donde `c_rehash` y perl ya
existen— y viajan dentro del artefacto, con una guarda que **falla ruidosamente si el rehash no
produce ninguno**. Re-sella `ca-certificates`, que es raíz de `perfil.base`.
**Apaño mientras tanto**, para invocaciones puntuales: `CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt`
(comprobado: 200).
### 6.23 `qorpa` en la caja: preflight en verde, y el segundo muro es `tar`
Con el backend de `sergio` sin camino a musl (§6.21), la salida elegida es **qorpa**. Estado medido
en la caja:
| paso | resultado |
|---|---|
| userns anidado · overlayfs sin privilegios | ✅ ya estaban |
| **disco** | ❌→✅ `/var/lib/hammer` vive en la raíz (3 G) ⇒ `qorpa` **se movió a `/work`** (62 G) por symlink |
| `subuid`/`subgid` | ❌→✅ rango escrito para root |
| `newuidmap`/`newgidmap` sin capability | ❌→✅ `setcap` aplicado — **y el artefacto del store NO se tocó** (inodes distintos: en una caja instalada esos binarios son copias, no hardlinks) |
| veredicto del preflight | **de «BLOQUEA 1 · LIMITA 4» a «LIMITA 1»** |
| `qorpa pull` de Arch bootstrap | baja y **verifica el sha256**, y falla al desempacar |
El segundo muro: el rootfs de Arch es `.tar.zst` y **el `tar` de la imagen es el de busybox**, que no
lo entiende — el mismo tropiezo que ya costó el lab (`tar: unrecognized option: zstd`). `zstd`
está instalado. ⇒ `qorpa pull` tiene que descomprimir con `zstd -dc | tar -x` cuando el archivo es
`.zst`, en vez de dárselo entero a `tar`. Es un arreglo pequeño en el CLI, y hasta entonces la
alternativa es una imagen `.tar.gz` (Ubuntu base) — que además obliga a reinstalar las deps del venv,
porque el de gioser trae extensiones de **CPython 3.14** y Ubuntu 24.04 lleva 3.12.
## 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:
+22
View File
@@ -65,6 +65,28 @@ make install DESTDIR="/out"
exec /usr/bin/c_rehash /etc/ssl/certs
EOF
chmod +x "/out"/etc/ca-certificates/update.d/certhash
# ── LOS HASH-LINKS, HECHOS EN EL BUILD (2026-09-14) ──────────────────────────────────────
# En Alpine ese hook de arriba lo ejecuta `apk` al instalar. **Acá no lo ejecuta nadie**: takana
# proyecta artefactos, no corre post-install. Resultado medido en la caja de producción: el
# `curl` de la distro está compilado con `--with-ca-path=/etc/ssl/certs` y SIN CAfile, así que
# mira un directorio que sólo tiene el bundle — y **no puede validar ningún certificado**:
#
# * CApath: /etc/ssl/certs
# * SSL certificate ... unable to get local issuer certificate (20)
#
# No falla al construir ni al instalar: falla la primera vez que la caja intenta bajar algo por
# HTTPS (`qorpa pull`, `install --repo https://…`, el mirror). Con `--cacert` el mismo curl da
# 200, que es la prueba de que los certificados están y lo que falta es el ÍNDICE.
#
# Se generan acá, donde `c_rehash` y perl existen, y viajan dentro del artefacto.
cp "/out"/usr/share/ca-certificates/*.crt "/out"/etc/ssl/certs/ 2>/dev/null || true
c_rehash "/out"/etc/ssl/certs >/dev/null 2>&1 || /usr/bin/c_rehash "/out"/etc/ssl/certs >/dev/null 2>&1 || true
# Falla ruidosamente si el rehash no produjo NADA: un capath sin enlaces es exactamente el bug
# que esto viene a cerrar, y sellarlo en silencio lo devolvería intacto.
n=$(find "/out"/etc/ssl/certs -name '*.0' | wc -l)
[ "$n" -gt 0 ] || { echo "ca-certificates: c_rehash no generó hash-links en /etc/ssl/certs" >&2; exit 1; }
echo "ca-certificates: $n hash-links en el capath"
}
_abuild_phase
'''