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:
@@ -104,6 +104,74 @@ Si la imagen fue firmada por este pipeline, el comando termina con
|
||||
firmó, porque la firmó otra llave, o porque la imagen fue modificada
|
||||
después — termina con `exit 1` y un error explícito.
|
||||
|
||||
## Limitaciones conocidas y mejoras futuras
|
||||
|
||||
Dos limitaciones identificadas al validar el pipeline end-to-end,
|
||||
documentadas a propósito como TODO — ninguna de las dos se implementó
|
||||
todavía.
|
||||
|
||||
### 1. Se firma por tag, no por digest
|
||||
|
||||
Hoy el pipeline firma `ecommerce-frontend:v1.0.104` — un **tag**, no un
|
||||
**digest** (`sha256:...`). Un tag es una etiqueta mutable: nada impide
|
||||
que, después de firmada la imagen, alguien (o un bug en el propio
|
||||
pipeline) vuelva a subir contenido distinto bajo el mismo tag
|
||||
`v1.0.104`. La firma original seguiría "verificando" — porque Cosign,
|
||||
al verificar por tag, resuelve el tag al digest que tenga *en ese
|
||||
momento*, no al que tenía cuando se firmó. Si el tag fue reasignado,
|
||||
se está verificando una imagen distinta de la que realmente se firmó,
|
||||
sin que nada avise.
|
||||
|
||||
Esto no es hipotético: el propio Cosign lo advierte en cada firma de
|
||||
este pipeline (visto en los logs reales de cada run):
|
||||
|
||||
```text
|
||||
WARNING: Image reference gitea.cruzcloud.net/.../ecommerce-frontend:v1.0.102
|
||||
uses a tag, not a digest, to identify the image to sign.
|
||||
This can lead you to sign a different image than the intended one.
|
||||
```
|
||||
|
||||
**TODO:** capturar el digest exacto que devuelve `docker push` (o
|
||||
`docker buildx build --metadata-file`) y firmar/verificar contra ese
|
||||
digest en vez del tag —
|
||||
`gitea.cruzcloud.net/devops/ecommerce-frontend@sha256:...` en vez de
|
||||
`:v1.0.104`. No implementado todavía; requiere ajustar el step de
|
||||
build para exponer el digest como output y pasarlo a los steps de
|
||||
firma y verificación.
|
||||
|
||||
### 2. El transparency log (Rekor) está deshabilitado
|
||||
|
||||
Ya se explicó arriba, en la sección "Por qué `--tlog-upload=false`" de
|
||||
esta misma página, la decisión consciente de no publicar en el
|
||||
transparency log para este registry privado. Vale la pena nombrar en
|
||||
simple qué es lo que se está dejando afuera:
|
||||
|
||||
Un **transparency log** (Rekor, el de Sigstore) es un registro
|
||||
público, append-only, criptográficamente verificable, de "quién firmó
|
||||
qué imagen y cuándo" — pensalo como un libro contable público que
|
||||
nadie puede editar ni borrar después de escrito, solo agregar filas
|
||||
nuevas. Cualquiera puede consultar ese libro para confirmar de forma
|
||||
independiente (sin confiar en el propio proyecto, ni en la llave
|
||||
privada, ni en el registry) que una firma específica existió en un
|
||||
momento específico.
|
||||
|
||||
Sin transparency log, la garantía que queda es más débil: *"esta firma
|
||||
la generó quien tuviera la llave privada en el momento en que se
|
||||
verificó"* — pero no hay ningún registro externo e inmutable que
|
||||
demuestre *cuándo* se generó, ni una forma de detectar si alguien con
|
||||
acceso a la llave privada firmó algo por fuera del pipeline sin que
|
||||
quede rastro. Para un lab personal con un registry privado, ese
|
||||
trade-off es razonable (ver la advertencia más abajo en esta misma
|
||||
página). Pero es una limitación real, no solo un detalle de
|
||||
configuración — importa especialmente el día que este mismo patrón se
|
||||
use en un contexto con más de una persona firmando, o con un registry
|
||||
que deje de ser privado.
|
||||
|
||||
**TODO:** si en algún momento el registry deja de ser exclusivamente
|
||||
privado, o se suma más de una persona con acceso a la llave de firma,
|
||||
reevaluar habilitar el transparency log público de Sigstore (o correr
|
||||
uno privado propio) en vez de mantenerlo deshabilitado.
|
||||
|
||||
## Qué falta (a propósito, todavía)
|
||||
|
||||
Hoy la verificación de firma corre como smoke test **dentro del mismo
|
||||
|
||||
Reference in New Issue
Block a user