Nota

SLSA L0–L3 y provenance: una cadena de confianza desde el commit hasta admission

Un SBOM dice «qué hay dentro», una firma «quién firmó». Entre ambos está provenance — «cómo se construyó». Desde SolarWinds es la pregunta que admission tiene que hacer.

Después de SolarWinds, Codecov y log4shell quedó claro que verificar el artefacto al final del pipeline ya no basta. Un SBOM dice qué hay dentro de la imagen. Una firma de Cosign demuestra que quien firmó tenía un token OIDC válido. Entre ambos queda el hueco por el que se cuelan los ataques a la cadena de suministro: un builder sustituido, un workflow alterado, el commit equivocado. SLSA cierra ese hueco con provenance — una descripción verificable por máquinas de cómo exactamente se construyó el artefacto.

Qué promete cada nivel

SLSA v1.0 (mantenido por OpenSSF) define cuatro niveles. L0 significa cero garantías — el estado por defecto. L1 exige un proceso de build documentado y provenance generada incluso sin firma: lo justo para atrapar errores honestos y sustituciones rápidas. L2 sube el listón a un builder hosted y aislado con provenance firmada — cierra el tampering entre build y publish. L3 es un builder ephemeral endurecido con provenance fuerte: el modelo de amenazas pasa a incluir un builder comprometido y un insider con acceso al CI.

L3 está casi out-of-the-box en GitHub Actions vía slsa-framework/slsa-github-generator (reusable workflow). GitLab sigue en L2 parcial. Un builder self-hosted etiquetado «aquí corremos L2» sin aislamiento real es la mentira más común de la industria, y SLSA se niega a validarla.

Qué hay dentro de provenance

Provenance es un in-toto Statement con predicado slsa.dev/provenance/v1. Dentro: subject — la imagen y su digest; buildDefinition.externalParameters — repository, ref, ruta al workflow; buildDefinition.resolvedDependencies — el commit de git; runDetails.builder.id — el identificador del builder; runDetails.metadata — invocation ID y timestamps.

Esos campos responden cuatro preguntas: qué se construyó, desde dónde, con qué y cuándo. Una firma sin provenance solo confirma que «alguien con un token válido firmó algo». Provenance sin firma es JSON sin prueba. Funcionan juntas o no funcionan.

Sigstore: firmar sin claves de larga vida

Cosign en modo keyless (GA desde 2.0+) elimina el problema del key management. La identidad viene de OIDC: GitHub Actions, Google, tu Keycloak en air-gapped. Fulcio emite un certificado que vive 10 minutos, Cosign firma el artefacto con él, y la entrada va a parar a Rekor — un transparency log público append-only.

Una sola sintaxis cubre todo artefacto OCI:

cosign sign --yes ghcr.io/org/payments-api:v1.2.3
cosign attest --predicate provenance.json --type slsaprovenance ghcr.io/org/payments-api:v1.2.3
cosign attest --predicate sbom.cdx.json --type cyclonedx ghcr.io/org/payments-api:v1.2.3

Con Helm 4 y OCI como canal primario de distribución, el mismo cosign sign aplica a los artefactos chart. Un transparency log, un modelo de verificación, una regla de admission.

Cerrar el bucle en admission

Firmar sin verificar es security theater — mencionado en todo documento de SLSA y omnipresente en producción. La verificación debe vivir en el admission controller; de lo contrario cualquier imagen entra al clúster esquivando el control.

Una Kyverno ClusterPolicy con verifyImages acepta un keyless attestor: regex sobre el subject (ruta al workflow), el OIDC issuer, la URL de Rekor. Un Pod cuya imagen no pasa la verificación no se crea. En el lado CLI lo hace slsa-verifier:

slsa-verifier verify-image ghcr.io/org/payments-api:v1.2.3 \
  --source-uri github.com/org/payments-api \
  --source-tag v1.2.3 \
  --builder-id 'https://github.com/slsa-framework/slsa-github-generator/.github/workflows/builder_container_slsa3.yml@refs/tags/v2.0.0'

Sin --builder-id la cadena se rompe: la firma es válida, pero quién construyó y dónde — desconocido.

Anti-patrones que aparecen más de lo que deberían

Una sola clave para dev/staging/prod borra el aislamiento de blast radius. Un email del firmante hardcodeado en la policy no escala a un equipo. latest en verifyImages.imageReferences apunta a un target mutable — la firma queda sin sentido. Provenance con internalParameters vacíos pierde el contexto de invocación. Rekor desactivado (--rekor-url="") elimina la detección: un token OIDC robado pasa en silencio.

La alternativa K8s-native

Tekton Chains es un observer para TaskRun: genera SLSA provenance de forma automática, firma vía Sigstore keyless, empuja la attestation junto a la imagen en OCI y la entrada a Rekor. Resultado: SLSA L3 sin salir de Kubernetes. El precio es Tekton mismo — un pipeline engine con su propio conjunto de CRDs (Tasks, Pipelines, Triggers) y un overhead operativo no trivial. Encaja en equipos de platform engineering que ya se comprometieron con un CI K8s-first.

Tres respuestas a tres preguntas

Un SBOM responde «qué hay dentro del artefacto». Provenance — «cómo se construyó». Una firma de Cosign — «quién responde por él». Un ataque a la cadena de suministro tumba una de esas tres respuestas; SLSA está diseñado para que el admission controller pueda hacer las tres antes de que un Pod sea creado.

© 2026 axyi.ru · CC BY 4.0