Módulo 6: Supply Chain Sbom And Signing

4. Qué resuelve Sigstore: keyless frente a keypair

Descripción

Con el SBOM generado, la pregunta que queda abierta es cómo firmar lambda/function.zip de forma que cualquiera pueda verificar, después, que no cambió. La respuesta obvia —"usa PKI, como siempre"— trae un problema práctico real que esta lección diseca antes de tocar cosign en la lección 5: la gestión de certificados es, históricamente, la parte más costosa y propensa a error de firmar software. Sigstore —el proyecto detrás de cosign— nació específicamente para resolver ese problema, con un mecanismo llamado keyless signing. Esta lección explica cómo funciona, y por qué esta guía, de forma deliberada, no lo usa — eligiendo, en cambio, el mecanismo más antiguo y más simple: un par de llaves criptográficas generado localmente.

Conexión con el módulo

Esta lección es el puente conceptual entre el SBOM (lecciones 2-3) y la firma real (lecciones 5-7). Todo lo que decidas entender aquí sobre por qué esta guía usa un keypair, no identidad efímera, te va a evitar una confusión legítima en la lección 5, cuando cosign generate-key-pair te pida generar un archivo .key en vez de pedirte iniciar sesión con una cuenta de GitHub o Google.


El problema que PKI tradicional siempre tuvo, sin Sigstore de por medio

Firmar código digitalmente no es una idea nueva — la infraestructura de clave pública (Public Key Infrastructure, PKI) existe desde los años 90, y su mecanismo central es simple de describir: alguien genera un par de llaves (una privada, que nunca comparte; una pública, que distribuye), firma un artefacto con la privada, y cualquiera puede verificar esa firma con la pública correspondiente. El problema nunca fue el mecanismo criptográfico en sí —es sólido, y lleva décadas probado—. El problema fue, siempre, la gestión alrededor de ese mecanismo:

  • ¿Dónde vive la llave privada, y quién la protege? Si vive en un archivo en disco, cualquiera con acceso a ese disco puede robarla y firmar cosas en tu nombre, indefinidamente, hasta que alguien note el robo.
  • ¿Qué pasa si la llave se filtra? Hay que revocarla —publicar, en algún lugar que todo el mundo consulte, que esa llave específica ya no es confiable— y rotar a una nueva, un proceso manual, lento, y frecuentemente mal ejecutado en la práctica real de la industria.
  • ¿Cómo sabe alguien, meses después, que la llave que usaste para firmar en su momento seguía siendo válida en ese momento exacto —no antes de que existiera, no después de que la revocaras—? Sin un mecanismo adicional, una firma antigua y una robada hoy pero aplicada a una fecha falsificada se ven exactamente igual.

Multiplicado por miles de mantenedores de proyectos de código abierto, cada uno gestionando sus propias llaves con su propio nivel de disciplina, este problema es, en gran parte, la razón por la que firmar software nunca se volvió una práctica universal antes de Sigstore — el costo de gestión superaba, para la mayoría de los equipos, el beneficio percibido.


Cómo Sigstore lo resuelve: identidad efímera en vez de llaves de larga vida

Sigstore —el proyecto que sostiene cosign, iniciado por Google, Red Hat y Purdue University, hoy parte de Open Source Security Foundation— invierte la pregunta: en vez de "¿cómo protegemos una llave privada para siempre?", pregunta "¿por qué necesitamos una llave de larga vida, si podemos generar una nueva, válida por minutos, cada vez que alguien firma algo?". El mecanismo tiene tres piezas:

  • Fulcio — una autoridad de certificación que, en vez de emitir certificados de larga duración como una CA tradicional, emite certificados de corta vida (típicamente minutos), atados a una identidad verificada externamente por OIDC —tu cuenta de GitHub, de Google, o —el caso que más importa a esta guía— la identidad de un workflow de GitHub Actions autenticado por su propio proveedor OIDC—.
  • Rekor — un registro de transparencia público e inmutable: cada firma keyless queda registrada ahí, con marca de tiempo, de forma que cualquiera puede verificar, después, que esa firma existió en ese momento exacto, sin depender de que el certificado efímero (que ya expiró hace tiempo) siga siendo válido.
  • El flujo OIDC — quien firma se autentica ante un proveedor de identidad que ya conoce (GitHub, Google), recibe un token de corta vida, Fulcio lo cambia por un certificado igual de efímero, se firma el artefacto, y el certificado puede desecharse de inmediato: no hay nada de larga vida que proteger ni que rotar.
   FIRMA KEYLESS (Sigstore, identidad OIDC efímera)
   ─────────────────────────────────────────────────

   [tu identidad OIDC]  →  [Fulcio emite cert. de minutos]  →  [firmas el artefacto]
         │                                                            │
         │                                                            ▼
         │                                              [Rekor registra la firma, para siempre]
         │                                                            │
         └──── nada de larga vida que proteger o rotar ───────────────┘

Es una solución genuinamente elegante al problema histórico de PKI: nadie gestiona una llave privada de forma manual, porque no existe ninguna llave de larga vida — cada firma usa una identidad que expira en minutos, y el registro público (Rekor) es lo que preserva la evidencia de que esa firma ocurrió, sin depender de la vigencia del certificado efímero.


Por qué esta guía, de forma deliberada, NO usa keyless

Con una solución tan elegante disponible, vale la pena explicar con precisión por qué las lecciones 5 a 7 de este módulo usan el mecanismo más antiguo —un keypair local generado con cosign generate-key-pair— en vez de la identidad efímera que acabas de leer. La razón no es que keyless sea inferior — es que, en el laboratorio $0 de esta guía, keyless dependería exactamente del mismo límite que ya documentaste, en vivo, en el Módulo 2:

Primera razón: la validación real de OIDC ya está fuera de alcance en este laboratorio, por una causa técnica confirmada. El Módulo 2, lección 6, ejecutó awslocal sts assume-role-with-web-identity con un JWT construido a mano, y documentó, con la fuente exacta, por qué LocalStack Hobby no valida de verdad la firma ni el emisor de un token OIDC —la funcionalidad IAM Policy Enforcement que haría esa validación real es exclusiva de los planes de pago Base/Ultimate—. Firmar "keyless" en un workflow de GitHub Actions bajo act tendría el mismo problema, agravado: act simula la ejecución de un workflow, pero no tiene la infraestructura para emitir un token OIDC real de GitHub Actions ni para que Fulcio lo valide contra el emisor real (token.actions.githubusercontent.com) — cualquier intento de firma keyless en este laboratorio sería, en el mejor de los casos, una simulación de una simulación, sin ninguna garantía real detrás.

Segunda razón: keyless exige una cuenta y una conexión de red real a la infraestructura pública de Sigstore. Fulcio y Rekor son servicios alojados —gratuitos, sin necesidad de tarjeta de crédito, pero servicios de red reales, no locales—. Cada firma keyless implica una llamada de red real a esa infraestructura pública, y cada verificación keyless implica consultar Rekor para confirmar que la firma quedó registrada. Un keypair local, en cambio, no necesita ninguna cuenta ni ninguna llamada de red: cosign generate-key-pair, cosign sign-blob y cosign verify-blob corren, los tres, completamente en tu máquina, sin depender de que ningún servicio externo esté disponible en el momento exacto en que ejecutas el comando — una propiedad que las lecciones 5, 6 y 7 de este módulo van a confirmar, en vivo, con la salida real de cada comando.

Tercera razón, la más honesta de las tres: un keypair local sigue siendo Sigstore de verdad, no una versión "de juguete". cosign soporta ambos mecanismos —keyless y keypair— como ciudadanos de primera clase, no como un modo "reducido" para principiantes. La misma especificación de bundle de verificación, la misma familia de comandos (sign-blob/verify-blob), el mismo algoritmo de firma (ECDSA sobre la curva P-256, por defecto) — la única diferencia real es de dónde sale la llave privada: de un archivo local que tú generaste (esta guía), o de un certificado efímero que Fulcio emitió a partir de tu identidad OIDC (el flujo que un equipo con una cuenta de GitHub Actions real usaría en producción, exactamente lo que cicd-and-gitops-on-aws-guide nombró, sin construir, en su propio diseño).

   TRES MECANISMOS DE FIRMA, EL MISMO cosign
   ───────────────────────────────────────────

   PKI tradicional          Sigstore keyless           Sigstore keypair (esta guía)
   ────────────────         ─────────────────           ─────────────────────────────
   Llave de larga vida      Certificado de minutos      Llave de larga vida
   Gestión manual, costosa  Identidad OIDC (GitHub/     Gestión manual, pero LOCAL
                            Google), sin llave que          y sin costo de infraestructura
                            proteger                        pública

   Requiere: nada externo   Requiere: cuenta OIDC +     Requiere: nada externo
   (offline posible)        Fulcio + Rekor (red real)   (offline, como esta guía)

              todos verificables con: cosign verify-blob / verify

Cómo se vería el flujo keyless real, si esta guía pudiera construirlo

Vale la pena mostrar, sin ejecutarlo, la forma que tendría un apply.yml firmando de forma keyless en un pipeline de GitHub Actions real —contra una cuenta de GitHub y AWS reales, no contra act/LocalStack—, para que reconozcas el patrón si lo encuentras en la documentación oficial o en un proyecto de producción:

# Representativo — requiere GitHub Actions real (no act) y AWS real (no LocalStack).
# NO se ejecuta en esta guía, por las tres razones explicadas arriba.
permissions:
  id-token: write   # necesario para que GitHub emita el token OIDC

steps:
  - name: Sign function.zip (keyless)
    run: |
      cosign sign-blob \
        --yes \
        lambda/function.zip \
        --bundle manifest.sig
      # cosign detecta automáticamente el token OIDC ambiente de GitHub Actions,
      # lo intercambia por un certificado de Fulcio, firma, y sube a Rekor.

Fíjate en lo que no aparece: ningún --key cosign.key. En el flujo keyless, cosign detecta automáticamente la identidad OIDC del entorno de ejecución (permissions: id-token: write es lo que le da a ese workflow el permiso de pedir ese token a GitHub) — no hay ningún archivo de llave que generar, distribuir, ni proteger. Es, en un pipeline de producción real con una cuenta de GitHub y AWS reales, probablemente la elección correcta para la mayoría de los equipos en 2026. Esta guía no la construye porque el laboratorio en el que corre —act contra LocalStack Hobby, $0, sin cuenta— no puede probarla de verdad, no porque sea inferior al mecanismo que sí vas a construir en las lecciones 5 a 7.


Errores comunes

Pensar que "keyless" significa "sin ninguna criptografía", solo verificación de identidad (de nombre, confuso pero comprensible). Qué pasa: alguien, guiándose solo por la palabra "keyless" ("sin llave"), asume que Sigstore reemplaza la firma criptográfica por algo más simple, como una simple confirmación de identidad. Cómo detectarlo: si tu explicación del flujo keyless no incluye ningún certificado ni ninguna firma ECDSA. Cómo corregirlo: "keyless" se refiere específicamente a que no gestionas ninguna llave privada de larga vida — pero sigue habiendo una llave privada real, generada de forma efímera dentro del proceso de firma, y sigue habiendo una firma criptográfica real sobre el artefacto. Lo que desaparece es la gestión manual de una llave permanente, no la criptografía misma.

Confundir Rekor (el registro de transparencia) con un simple registro de auditoría interno de una empresa (de alcance). Qué pasa: alguien asume que Rekor es privado, como un log de auditoría dentro de la infraestructura de una organización. Cómo detectarlo: si esperas que una firma keyless quede oculta para el público. Cómo corregirlo: Rekor es, por diseño, un registro público: cualquier firma keyless que se suba ahí es consultable por cualquiera en internet, para siempre — es precisamente esa propiedad pública la que le da su valor de transparencia (nadie puede alegar, después, que una firma nunca existió). Esta propiedad, además, es una de las razones prácticas por las que un artefacto interno de una empresa —sin intención de ser público— podría preferir, deliberadamente, el modo --tlog-upload=false/--insecure-ignore-tlog=true que las lecciones 5 a 7 de este módulo usan: mantener la firma completamente privada, sin publicar nada en un registro público.

Concluir que esta guía "no usa Sigstore de verdad" porque no usa keyless (de jerarquía falsa, el error más importante de esta lección). Qué pasa: alguien, después de leer sobre Fulcio/Rekor, asume que el keypair local de las lecciones 5-7 es una versión "diluida" o "de práctica" de Sigstore, no el mecanismo real. Cómo detectarlo: si tu expectativa es que la firma de la lección 6 sea "menos válida" criptográficamente que una firma keyless. Cómo corregirlo: cosign con un keypair local es, exactamente igual que el modo keyless, Sigstore real —el mismo binario, la misma especificación de bundle, el mismo algoritmo de firma—. La diferencia entre los dos modos es de gestión de identidad (de dónde sale la llave privada), no de solidez criptográfica de la firma resultante. Una firma con keypair local, verificada con cosign verify-blob --key cosign.pub, es tan válida criptográficamente como una firma keyless verificada contra un certificado de Fulcio.


Ejercicios

Ejercicio 1 — Explica el problema histórico de PKI a alguien que nunca gestionó una llave criptográfica. En dos o tres frases, sin usar la palabra "certificado", explica por qué firmar software fue, durante décadas, más difícil de lo que el mecanismo criptográfico en sí sugeriría.

Ver solución

Una respuesta completa suena, más o menos, así: "Firmar algo digitalmente es matemáticamente simple, pero alguien tiene que guardar, para siempre, la llave secreta que hace la firma — y si esa llave se pierde o se la roban, hay que avisarle a todo el mundo que deje de confiar en ella, un proceso lento y que casi nunca se hace bien. El problema nunca fue firmar; fue cuidar la llave indefinidamente, sin que nadie la pierda ni la robe, durante años."

Ejercicio 2 — Decide, para dos escenarios reales, cuál mecanismo (keyless o keypair) sería la elección correcta en producción, fuera del contexto de esta guía. (a) Un equipo de una startup de cinco personas, con GitHub Actions y una cuenta AWS real, firmando artefactos de despliegue varias veces al día. (b) Un proveedor de software que firma binarios que se distribuyen a clientes que verifican la firma años después, sin ninguna conexión a internet en el momento de la verificación.

Ver solución

Para (a), keyless es probablemente la elección correcta: el equipo ya tiene GitHub Actions con identidad OIDC real, no necesita gestionar ninguna llave de larga vida, y cada firma keyless queda registrada en Rekor de forma pública y verificable. Para (b), un keypair local —o incluso un mecanismo de KMS gestionado con la llave protegida por hardware, otra opción que cosign soporta y que esta guía no cubre— es más apropiado: la verificación tiene que funcionar años después, sin depender de que la infraestructura pública de Sigstore (Fulcio, Rekor) siga existiendo en esa forma exacta, y sin depender de una conexión de red en el momento de verificar. Este segundo escenario es, de hecho, muy similar en espíritu a por qué esta guía usa keypair: verificación offline, sin dependencias externas en el momento de comprobar la firma.

Ejercicio 3 — Identifica qué parte del apply.yml representativo de esta lección cambiaría si Andes Cargo migrara de LocalStack a una cuenta AWS real con GitHub Actions real. Comparando el bloque YAML representativo de esta lección con lo que vas a construir en la lección 6 (keypair), ¿qué tendría que cambiar exactamente, y qué se mantendría igual?

Ver solución

Cambiaría: el flag --key cosign.key desaparecería (no hay llave local que pasar); aparecería permissions: id-token: write en el nivel del job, para que GitHub le dé al workflow permiso de solicitar un token OIDC; y los flags --tlog-upload=false/--insecure-ignore-tlog=true de las lecciones 6-7 desaparecerían también, porque en producción real sí querrías que la firma se registre en el Rekor público, no que se mantenga offline. Se mantendría igual: el nombre del comando (cosign sign-blob), el artefacto que se firma (lambda/function.zip), y el mecanismo de verificación en el otro extremo (cosign verify-blob, aunque con --certificate-identity/--certificate-oidc-issuer en vez de --key cosign.pub). El principio de que "un .zip sin firmar no debería llegar a apply" —el corazón de este módulo— no cambia en absoluto entre ambos mecanismos.


Resumen y siguiente paso

Esta lección explicó qué problema histórico resuelve Sigstore —la gestión de llaves privadas de larga vida, el costo real detrás de por qué firmar software nunca se volvió universal antes de este proyecto— y cómo lo resuelve con identidad efímera vía Fulcio y un registro público de transparencia vía Rekor. Con esa base, viste, con tres razones concretas y verificables, por qué esta guía elige deliberadamente el mecanismo más simple —un keypair local— en vez de keyless: el mismo límite de validación real de OIDC que el Módulo 2 ya documentó, la necesidad de que todo corra $0 y sin conexión de red obligatoria, y la confirmación de que un keypair local sigue siendo Sigstore real, no una versión reducida.

Antes de avanzar deberías poder: explicar la diferencia entre Fulcio y Rekor sin confundir sus roles; justificar, con tus propias palabras, por qué esta guía no puede construir keyless de verdad en este laboratorio; y reconocer que la elección de mecanismo de firma es sobre gestión de identidad, no sobre solidez criptográfica.

La lección 5 deja la teoría atrás por el resto del módulo: vas a instalar cosign de verdad, confirmar su versión, y generar tu primer keypair local — con la password interactiva resuelta de una forma que no bloquea la automatización.

Recursos

  1. Sigstore — Documentación oficial — punto de entrada al proyecto completo, incluidos Fulcio, Rekor y cosign.
  2. Sigstore — Signing with a self-managed key — el mecanismo exacto que las lecciones 5-7 de este módulo construyen: cosign generate-key-pair, sign-blob, verify-blob con un keypair local.
  3. Sigstore — Keyless signatures — el mecanismo que esta lección explica pero no construye, con el flujo completo de Fulcio/Rekor documentado en detalle.
  4. Este curso, Módulo 2, lección 6 (06-hands-on-what-localstack-does-and-does-not-validate.md) — la fuente exacta y ya ejecutada del límite de validación de OIDC que esta lección cita como una de las tres razones para no usar keyless.