Proyectos
Supply chain · Seguridad

Solo se despliega
lo que está firmado

El reto: el pipeline despliega lo que llega al registro, pero ¿y si lo que llega no lo construyó el CI? Una credencial filtrada o una publicación accidental bastarían para servir una imagen ajena. La respuesta es criptografía en vez de confianza: el servidor solo acepta imágenes cuya firma puede verificar.

La cadena de confianza
build imagen por digest cosign sign clave privada · secreto CI GHCR imagen + firma GITHUB 4LABSSERVER cosign verify clave pública local válida docker up en producción inválida deploy_rejected despliegue abortado

La clave privada nunca toca el servidor; la pública nunca sale de él. Ninguno de los dos extremos puede suplantar al otro: el CI no puede desplegar, y el servidor no puede firmar.

Las decisiones, y su porqué
01
Par de claves propio en vez de keyless
ProblemaEstablecer confianza entre quien construye (GitHub Actions) y quien despliega (el servidor), sin que ninguno dependa de la palabra del otro.
AlternativasEl modo keyless de Sigstore (OIDC + Fulcio + Rekor) evita gestionar claves, pero delega la confianza en una cadena de servicios externos.
DecisiónPar de claves propio. La privada vive como secreto de Actions y nunca toca el servidor; la pública es un fichero en el servidor y nunca sale de él. El ancla de confianza es local, auditable y sin terceros en medio.
02
Se firma el digest, no el tag
ProblemaUn tag como :latest es un puntero mutable: firmar un tag es firmar algo que puede apuntar mañana a otro contenido.
AlternativasFirmar el tag es lo cómodo, y lo que muchos ejemplos muestran.
DecisiónEl CI firma el digest inmutable que devuelve el build (cosign sign imagen@sha256:…). La firma queda ligada al contenido exacto del artefacto, no a un nombre. Cambiar un bit cambia el digest e invalida la firma.
03
Verificación offline, sin log de transparencia
ProblemaEl despliegue no debe depender de la disponibilidad de servicios externos en su camino crítico.
AlternativasVerificar también contra Rekor añade transparencia pública, pero convierte un servicio de terceros en dependencia de cada despliegue.
DecisiónVerificación solo con la clave pública local (--insecure-ignore-tlog). Trade-off explícito y asumido: se renuncia a la transparencia pública a cambio de un despliegue autónomo. La autenticidad la garantiza la clave; el registro de despliegues queda en el propio servidor.
04
Gate fail-closed, en destino
ProblemaFirmar en origen no protege nada si nadie verifica en el punto donde la imagen se convierte en producción.
AlternativasVerificar solo en CI, o verificar en el servidor pero limitarse a avisar y continuar si la firma no cuadra.
DecisiónEl script de despliegue verifica la firma antes de descargar y desplegar. Si no es válida, aborta: no hay modo degradado ni excepción manual. El rechazo queda anotado como evento en el registro de despliegues.
El resultado

La validación fueron dos pruebas sobre el sistema real: una imagen firmada por el CI superó el gate y se desplegó; una imagen sin firma fue rechazada y el despliegue abortado, con el rechazo anotado en el registro de despliegues. El gate es fail-closed: ante la duda, no se despliega.

CosignGitHub ActionsGHCRDockersystemdBash
Connect → Automate → Augment → Deploy.
Firmar en origen, verificar en destino: la confianza no se asume, se comprueba.