docs(devsecops): documentar 4+1 hallazgos de infraestructura y TODOs de Cosign

5 playbooks nuevos en docs/playbooks/ del portal:
- SQLite en modo DELETE causando SQLITE_BUSY, por que WAL lo resuelve.
- Anchors YAML no soportados por el parser de Gitea Actions, incluido
  el diagnostico equivocado inicial (indentacion) que se corrigio
  despues -- documentado con honestidad como leccion de proceso.
- Docker-in-Docker vs sibling containers, y por que Semgrep corre
  nativo en un venv en vez de como container aparte.
- El SNI/realm HTTP del tunel rompiendo especificamente a Cosign
  (docker push funcionaba, cosign sign no), conectado con el
  incidente ya documentado del 504 -- misma capa de ingress, dos
  fallas distintas.
- Contencion de recursos entre Gitea y su runner: causa raiz real
  confirmada en vivo durante esta misma sesion (ambos contenedores
  reiniciados a mitad de un build, sin limites de CPU/memoria reales),
  con fix propuesto (ver rama fix/gitea-runner-resource-limits en
  scripts/) pendiente de aplicacion manual.

Se actualiza el playbook del 504 para linkear hacia el nuevo hallazgo
de contencion, cerrando el loop que habia quedado abierto ahi.

cosign.md: nueva seccion "Limitaciones conocidas y mejoras futuras"
con los dos TODO reales -- firma por tag en vez de digest (con el
warning real de Cosign visto en los logs) y transparency log de Rekor
deshabilitado, explicado en simple.

index.md: tabla resumen de los 3 pipelines (imagen + adaptacion de
Semgrep por stack), diagrama generalizado para reflejar que es el
mismo patron en los 3 workflows, y "que falta" actualizado.

Validado con mkdocs build --strict (0 errores, 0 warnings) antes de
commitear.
This commit is contained in:
2026-08-15 15:17:43 -05:00
parent 826f199ebc
commit be3e5c2a4c
9 changed files with 666 additions and 13 deletions
@@ -0,0 +1,93 @@
# Incidente: el ingress del túnel rompió específicamente a Cosign, no al resto del pipeline (2026-08)
| | |
|---|---|
| **Ventana del incidente** | 2026-08-15, primer intento de firmar una imagen con Cosign en `build.yaml` |
| **Servicio afectado** | Cadena Cloudflare Tunnel → NPM (Nginx Proxy Manager) → Gitea, específicamente el endpoint del registry de contenedores |
| **Impacto** | `cosign sign` fallaba siempre, en cada intento — el resto del pipeline (Gitleaks, Trivy, Semgrep, build, push de la imagen) funcionaba normal contra el mismo registry |
## Síntoma
Todos los pasos anteriores del pipeline pasaban, incluido `docker push`
de la imagen al registry de Gitea. El paso de Cosign, inmediatamente
después, fallaba siempre con:
```text
Error: signing [gitea.cruzcloud.net/***/ecommerce-frontend:v1.0.102]:
accessing entity: invalid realm in www-authenticate: realm scheme
"http" not allowed for a secure registry; use https
```
Lo llamativo: **`docker push` a ese mismo registry, un paso antes,
funcionaba sin problema.** Si el registry estuviera mal configurado en
general, se esperaría que fallara también el push, no solo la firma.
## Diagnóstico
El mensaje da la pista exacta: al autenticar contra el registry,
Cosign recibe un header `WWW-Authenticate` cuyo campo `realm` apunta a
una URL con esquema `http://` en vez de `https://`. Cosign rechaza
explícitamente seguir un realm `http` contra lo que él considera "un
registry seguro" (un dominio público, no `localhost`) — es una
protección intencional del lado de Cosign, no un bug.
`docker push`/`docker login` son más permisivos con esto (o cachean la
sesión de otra forma) y no chocan con el mismo problema, por eso solo
Cosign lo mostraba.
## Causa raíz
El realm que arma Gitea en su respuesta `WWW-Authenticate` depende de
que Gitea sepa correctamente que la petición le llegó por HTTPS
(típicamente vía el header `X-Forwarded-Proto` o el Server Name que ve
en la conexión TLS que termina antes de llegar a él). La cadena real
acá es **Cloudflare Tunnel → NPM → Gitea** — si el proxy host de NPM
para `gitea.cruzcloud.net` no tiene el *Origin Server Name* (SNI hacia
el origen) configurado correctamente, Gitea puede terminar creyendo
que la conexión entrante fue por HTTP plano, y arma el realm con ese
esquema equivocado.
## Fix aplicado
Corrección de la regla de ingress de `cloudflared`/NPM para
`gitea.cruzcloud.net`: HTTPS hacia el origen con el *Origin Server
Name* correcto. El fix fue enteramente de infraestructura — no hubo
ningún cambio en este repo más allá de un commit vacío para volver a
disparar el pipeline y confirmar el fix contra credenciales reales.
## La misma causa raíz de fondo, dos síntomas distintos
Este incidente **no es el mismo bug** que el
[504 Gateway Timeout intermitente del túnel](incidente-504-gitea-tunnel.md)
ya documentado — ese fue inestabilidad de QUIC/UDP en `cloudflared`,
resuelto forzando `TUNNEL_TRANSPORT_PROTOCOL=http2`. Pero **ambos
viven en la misma capa**: la cadena de ingress
Cloudflare Tunnel → NPM → Gitea, mal configurada de dos formas
distintas, descubiertas en momentos distintos:
- Una regla de **transporte** (QUIC vs HTTP/2) causando 504s
intermitentes en *cualquier* petición HTTP a los dominios detrás del
túnel.
- Una regla de **SNI/origin server name** causando que Gitea generara
un realm HTTP incorrecto, rompiendo específicamente a un cliente
(Cosign) que valida ese detalle de forma estricta.
La lección compartida: en una cadena de proxies con TLS terminando en
varios saltos, un solo campo de configuración mal puesto en cualquiera
de los saltos puede manifestarse como fallas completamente distintas
según qué tan estricto sea el cliente al otro lado — la mayoría de las
herramientas HTTP normales ni lo notan, pero una herramienta de
seguridad como Cosign, que valida explícitamente el esquema del
realm, sí.
## Validación
Confirmado en una corrida real posterior al fix: `cosign sign` y
`cosign verify` completaron sin error contra el registry real, con
credenciales reales, de punta a punta.
!!! note "Nota relacionada, no parte de este incidente"
El mismo log de este fallo mostró, además del error de realm, el
warning esperado de Cosign sobre firmar por tag en vez de por
digest — ver
[limitaciones conocidas de Cosign](../devsecops/cosign.md#limitaciones-conocidas-y-mejoras-futuras).