Cadena de Suministro de Software en Linux 2026: Cosign, SBOM con Syft y Grype
Stack práctico para asegurar la cadena de suministro de software en Linux 2026: firma keyless con Cosign, SBOMs con Syft, escaneo con Grype y verificación obligatoria en Kubernetes con Kyverno.
La seguridad de la cadena de suministro de software en Linux consiste en firmar criptográficamente cada artefacto que produces (imágenes, binarios, SBOM), generar una lista de materiales (SBOM) con cada build, y verificar firmas y vulnerabilidades antes de desplegar. En 2026 el stack práctico se reduce a tres herramientas: cosign de Sigstore para firmar y verificar, syft de Anchore para generar SBOMs, y grype para escanear esos SBOMs contra bases de datos de vulnerabilidades. Combinadas, cierran la ventana que SolarWinds, Codecov y XZ Utils dejaron abierta.
El ataque a xz-utils (CVE-2024-3094) demostró que confiar en tarballs upstream sin verificar firmas es una vulnerabilidad de criticidad RCE.
Cosign 2.4 firma imágenes OCI sin gestionar claves: usa OIDC corto-plazo emitido por Fulcio y registra la firma en el log público Rekor.
Syft 1.18 genera SBOMs en SPDX 2.3 o CycloneDX 1.6 desde imágenes, directorios, archivos o lockfiles, sin ejecutar el código.
Grype consume un SBOM Syft y resuelve CVEs en segundos; el modo --fail-on critical rompe el pipeline cuando aparece una vulnerabilidad explotable.
SLSA v1.0 define cuatro niveles de pruebas de procedencia; alcanzar SLSA L3 requiere builders aislados (GitHub Actions, GitLab CI) y firmas no falsificables.
Verificar firmas en producción es trabajo del cluster: políticas de Kyverno o el policy-controller de Sigstore rechazan imágenes sin firma válida.
Anatomía de un ataque a la cadena de suministro
En mis engagements de los últimos dos años, la cadena de suministro es donde encuentro las cuentas más rentables. No necesitas vulnerar un firewall: comprometes la build o un mantenedor upstream y el código firmado entra solo al entorno de producción. El caso CVE-2024-3094 en xz-utils es el ejemplo de manual: un mantenedor con dos años de historial limpio introdujo una puerta trasera en los tarballs de release que pasó sin detectar a Debian, Fedora y Arch durante meses, y solo se cazó por una anomalía de rendimiento en SSH.
El patrón ofensivo se descompone en cinco pasos: comprometer la fuente (typosquatting de paquete, cuenta de mantenedor, fork malicioso), inyectar el payload en una fase que no se audita (script de configure, hook de post-install, tarball pre-generado), depender de que el consumidor descargue por HTTPS sin verificar firma, esperar a que se incluya en una imagen base, y aprovechar el privilegio elevado del proceso víctima. Cada paso tiene un control defensivo concreto: pin de versiones por hash, build reproducible, verificación obligatoria de firmas, generación de SBOM por capa y políticas de admisión en runtime. El resto del artículo recorre exactamente esos controles, herramienta por herramienta.
Sigstore y cosign: firma sin claves
Sigstore es el proyecto de la Linux Foundation que estandariza firmar artefactos sin que el desarrollador tenga que gestionar una PKI. Sus tres componentes funcionan en bloque y solo tiene sentido entenderlos juntos:
Fulcio: una CA pública que emite certificados X.509 de corta duración (10 minutos) atados a una identidad OIDC verificada (GitHub, Google, GitLab, correo corporativo).
Rekor: un transparency log inmutable basado en Merkle trees donde se publica cada firma. Permite auditar después: ¿esta firma existió antes de la fecha del incidente?
cosign: la CLI que orquesta los dos anteriores y firma imágenes OCI, blobs, SBOMs y attestations in-toto.
La novedad práctica de Sigstore es el keyless signing: ya no generas un par de claves que tienes que rotar, custodiar y revocar. El flujo es: cosign abre tu navegador, autenticas con OIDC, Fulcio emite un certificado efímero, cosign firma, publica firma + certificado en Rekor, y descarta la clave privada. La verificación posterior valida que la firma sea de la identidad esperada (por ejemplo, github-actions@my-org) y que esté registrada en Rekor antes del tiempo presente. Esto colapsa la superficie de ataque: no hay clave que robar, y un atacante que comprometa una cuenta OIDC genera firmas que dejan huella pública en el log Rekor, detectable por monitorización.
Cómo firmar una imagen de contenedor con cosign
Cosign 2.4 está en los repos oficiales de Fedora 41 y Ubuntu 24.04, o como binario estático desde el repo de Sigstore. Instalación rápida en cualquier distro:
Firma keyless de una imagen subida a GHCR. El flag --yes evita el prompt interactivo necesario en CI:
# Variables de entorno requeridas por keyless en CI
export COSIGN_EXPERIMENTAL=1
IMAGE="ghcr.io/my-org/api:$(git rev-parse HEAD)"
# Build + push normal
docker buildx build --push -t "$IMAGE" .
# Firma keyless (abre OIDC y publica en Rekor)
cosign sign --yes "$IMAGE"
# tlog entry created with index: 142589371
# Pushing signature to: ghcr.io/my-org/api
Verificación en máquina ajena (sin claves locales). Requiere la identidad del firmante y el issuer OIDC esperados. Esto es lo que evita que un atacante con su propia identidad OIDC válida firme tu imagen y la sustituya:
Un SBOM (Software Bill of Materials) es la lista exhaustiva de componentes (paquetes, librerías, binarios estáticos) incluidos en un artefacto. Sin SBOM, cuando aparece una CVE como Log4Shell te pasas tres días corriendo grep por toda la infraestructura. Con SBOM almacenado y firmado, una query SQL te dice qué imágenes son vulnerables en segundos.
Syft 1.18 soporta los dos formatos relevantes: SPDX 2.3 (estándar ISO/IEC 5962, requerido por la EO 14028 del gobierno de EE. UU.) y CycloneDX 1.6 (más expresivo para vulnerabilidades y servicios). Mi recomendación práctica: genera CycloneDX para consumo interno por Grype, y SPDX para compliance externo.
# Instalación
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh \
| sudo sh -s -- -b /usr/local/bin v1.18.0
# SBOM de una imagen, sin ejecutarla: solo analizando capas
syft "$IMAGE" -o cyclonedx-json=sbom.cdx.json
syft "$IMAGE" -o spdx-json=sbom.spdx.json
# SBOM de un directorio con código fuente
syft dir:./src -o cyclonedx-json=src-sbom.cdx.json
# SBOM de un binario Go estáticamente enlazado (extrae deps del buildinfo)
syft ./bin/api -o cyclonedx-json=binary-sbom.cdx.json
Una vez generado, firma el SBOM también y adjúntalo a la imagen como attestation:
Esta attestation queda enlazada a la imagen por digest, firmada con la identidad del builder y registrada en Rekor. Cualquiera puede después recuperarla con cosign download attestation y auditar exactamente qué versiones de qué paquetes formaron parte del build, sin necesidad de descargar la imagen completa.
Cómo escanear vulnerabilidades con Grype
Grype consume el SBOM de Syft (o directamente una imagen) y resuelve CVEs contra una base de datos federada que combina NVD, GitHub Security Advisories, los avisos de RedHat/Ubuntu/Debian/Alpine y la base de datos GitLab. Comparado con escanear una imagen directamente, partir del SBOM es entre 5× y 20× más rápido porque ya no hay que descomponer capas en cada ejecución.
# Instalación
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh \
| sudo sh -s -- -b /usr/local/bin v0.86.0
# Escaneo a partir del SBOM (rápido)
grype sbom:./sbom.cdx.json --fail-on critical
# Escaneo de la imagen directamente
grype "$IMAGE" --by-cve -o table
# Solo CVEs con exploit conocido (KEV de CISA + EPSS > 0.5)
grype "$IMAGE" --only-fixed --fail-on high \
-c .grype.yaml
El archivo .grype.yaml permite suprimir falsos positivos sin perder trazabilidad — cada supresión exige justificación y fecha de revisión:
ignore:
- vulnerability: CVE-2024-12345
package:
name: libcurl-dev
reason: "Solo en imagen de build, no en runtime. Revisar 2026-12-01."
fail-on-severity: critical
db:
auto-update: true
update-url: https://toolbox-data.anchore.io/grype/databases/listing.json
Builds en servicio hosteado con procedencia firmada
Medio
Manipulación de procedencia tras el build
L3
Builder aislado y procedencia no falsificable
Alto
Compromiso del entorno de build
En mi experiencia auditando organizaciones, SLSA L2 es alcanzable con GitHub Actions + cosign en una tarde de trabajo (que es exactamente lo que cubre este artículo). SLSA L3 requiere usar los reusable workflows oficiales de slsa-github-generator o el equivalente en GitLab, donde el runner que genera la procedencia es distinto del que ejecuta tu build. El salto a L4 (builds reproducibles bit-a-bit con dos partes independientes) sigue siendo poco común fuera de Debian y Nix, y rara vez justifica su coste para una organización media.
Si vienes de la cultura DevOps tradicional, complementa esto con la guía de pipeline DevSecOps con GitHub Actions que ya cubre SAST y secret scanning — el bloque que sigue añade firma y SBOM al mismo pipeline.
Pipeline completo en GitHub Actions
Este workflow compila la imagen, genera SBOM, escanea con Grype, firma con cosign keyless usando la identidad del workflow OIDC, y publica todo. Requiere permissions: id-token: write para que Fulcio acepte el token OIDC del runner:
El runner de GitHub Actions ya emite un token OIDC con el claim job_workflow_ref apuntando al SHA del workflow. Esto significa que cualquier verificador puede atar la firma no solo a tu organización, sino al workflow exacto y al commit que la produjo — un atacante con escritura en la rama no puede falsificarla sin pasar por el mismo workflow, lo que deja huella en los logs de GitHub Actions y en Rekor.
Verificación obligatoria en Kubernetes con Kyverno
Generar firmas que nadie verifica es teatro. El control real vive en el admission controller del cluster. Kyverno 1.13 incluye una regla verifyImages que rechaza pods cuyas imágenes no estén firmadas por la identidad esperada. Pega esta política a tu cluster y cualquier imagen sin firma válida nunca se schedulea:
Si manejas clusters multi-tenant, combina esta política con las restricciones de Pod Security Admission y Kyverno que ya cubrimos en detalle. La capa de firmas no sustituye al hardening de runtime: lo complementa.
Errores comunes y cómo evitarlos
Errores que veo de forma recurrente en organizaciones que adoptan Sigstore por primera vez:
Verificar solo el tag, no el digest. Si verificas my-image:latest, un atacante con write al registry puede repuntarlo a un digest sin firmar. Verifica siempre por @sha256:....
Permitir --certificate-identity-regexp ".*" en producción. Eso acepta cualquier identidad OIDC válida del mundo, no solo la tuya. Pinea la regex a tu organización.
SBOM generado tras el build pero antes del push. Si Syft escanea el filesystem del builder y no la imagen final, captura artefactos que no terminan en producción (toolchain, headers de dev). Escanea siempre la imagen final por digest.
Ignorar CVEs con --add-cpes-if-none. Este flag de Grype reduce falsos negativos pero también puede inflar el ruido; úsalo solo en imágenes con paquetes mal etiquetados, no por defecto.
No firmar las imágenes base internas. Si tu equipo de plataforma publica una imagen base sin firmar y los equipos producto la firman al construir sobre ella, el eslabón débil es la base. El escaneo y la verificación deben empezar en la cima del DAG, complementando los controles que ya cubrimos en escaneo de vulnerabilidades en Linux.
Preguntas frecuentes
¿Qué diferencia hay entre SPDX y CycloneDX?
SPDX 2.3 es un estándar ISO orientado a compliance y licencias; lo exigen contratos del gobierno de EE. UU. CycloneDX 1.6 está pensado para seguridad y soporta vulnerabilidades, servicios y dependencias transitivas con más detalle. En la práctica genera ambos: SPDX para auditoría externa y CycloneDX para consumo por Grype.
¿Puedo usar cosign sin Sigstore público?
Sí. Cosign soporta firma con clave estática (cosign generate-key-pair), con KMS de AWS/GCP/Azure/Vault, o contra una instancia privada de Sigstore desplegada con sigstore-scaffolding. El flujo de verificación es el mismo, cambia solo la raíz de confianza configurada con --rekor-url y --fulcio-url.
¿Cosign reemplaza a Notary o Docker Content Trust?
Sí. Docker Content Trust (basado en Notary v1) está deprecado en Docker Engine 25+ y Notary v2 fue absorbido por la spec OCI 1.1 que cosign implementa. Cualquier despliegue nuevo debe ir a Sigstore; mantener DCT es deuda de seguridad.
¿Grype escanea binarios Go y Rust estáticos?
Sí, Syft extrae el módulo y versión de cada dependencia desde el buildinfo embebido en binarios Go ≥1.18, y desde los metadatos de Cargo en binarios Rust compilados con --release. Grype consume ese SBOM como cualquier otro. Para binarios C/C++ estáticos sin metadatos no hay forma fiable: el control debe ser pinning en el build, no escaneo post-hoc.
¿Cómo verifico una firma de cosign offline?
Descarga firma y certificado con cosign download signature y la entrada de Rekor con cosign download attestation. Después ejecuta cosign verify-blob con los flags --signature, --certificate y --rekor-url= vacío para deshabilitar la consulta online; cosign valida la inclusión en Rekor usando el SET (Signed Entry Timestamp) embebido en el certificado.
Guía práctica para construir un pipeline DevSecOps en Linux con GitHub Actions. Integra Semgrep, Gitleaks y Trivy para blindar tu flujo CI/CD con herramientas open source y código listo para copiar.