{"id":707,"date":"2026-03-21T20:11:54","date_gmt":"2026-03-21T19:11:54","guid":{"rendered":"https:\/\/secure-pipelines.com\/ci-cd-security\/lab-secure-build-pipeline-tekton-tekton-chains-2\/"},"modified":"2026-03-25T06:41:01","modified_gmt":"2026-03-25T05:41:01","slug":"lab-secure-build-pipeline-tekton-tekton-chains-2","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-secure-build-pipeline-tekton-tekton-chains-2\/","title":{"rendered":"Lab: Implementacion de un pipeline de construccion seguro con Tekton y Tekton Chains"},"content":{"rendered":"<h2>Descripcion general<\/h2>\n<p>Tekton es un potente framework de codigo abierto, nativo de Kubernetes, para crear sistemas de integracion continua y entrega continua (CI\/CD). Se ejecuta como un conjunto de Custom Resource Definitions (CRDs) en cualquier cluster de Kubernetes, permitiendote definir pipelines como YAML declarativo que son portables entre entornos.<\/p>\n<p><strong>Tekton Chains<\/strong> es un proyecto complementario que agrega seguridad automatica de la cadena de suministro a tus pipelines de Tekton. Una vez instalado, Chains observa los TaskRuns completados, firma automaticamente sus resultados usando Cosign u otros firmantes, y genera <a href=\"\/es\/ci-cd-security\/artifact-provenance-attestations-slsa-in-toto\/\">atestaciones de procedencia SLSA<\/a> \u2014 todo sin requerir ningun cambio en tus definiciones de pipeline existentes.<\/p>\n<p>En este laboratorio practico, haras lo siguiente:<\/p>\n<ul>\n<li>Desplegar Tekton Pipelines y Tekton Chains en un cluster local de Kubernetes<\/li>\n<li>Configurar Chains para firmar automaticamente artefactos y generar procedencia in-toto<\/li>\n<li>Construir una imagen de contenedor a traves de un Tekton Pipeline<\/li>\n<li>Verificar la firma y la procedencia SLSA generadas automaticamente<\/li>\n<li>Agregar un paso de escaneo de vulnerabilidades al pipeline<\/li>\n<li>Explorar la firma sin claves con Sigstore Fulcio<\/li>\n<li>Aplicar politicas de imagenes firmadas en el momento del despliegue<\/li>\n<\/ul>\n<p>Al final de este laboratorio, tendras un pipeline de construccion seguro completamente funcional que produce imagenes de contenedor firmadas y atestadas con procedencia verificable \u2014 logrando el cumplimiento de SLSA Level 2 automaticamente.<\/p>\n<h2>Requisitos previos<\/h2>\n<p>Antes de comenzar este laboratorio, asegurate de tener las siguientes herramientas instaladas en tu estacion de trabajo:<\/p>\n<ul>\n<li><strong>Cluster de Kubernetes<\/strong> \u2014 Usaremos <a href=\"https:\/\/kind.sigs.k8s.io\/\" target=\"_blank\" rel=\"noopener\">kind<\/a> (Kubernetes in Docker) para un cluster local. Alternativamente, minikube tambien funciona.<\/li>\n<li><strong>kubectl<\/strong> \u2014 El CLI de Kubernetes, version 1.26 o posterior.<\/li>\n<li><strong>Helm<\/strong> \u2014 El gestor de paquetes de Kubernetes, version 3.x.<\/li>\n<li><strong>tkn<\/strong> \u2014 El <a href=\"https:\/\/tekton.dev\/docs\/cli\/\" target=\"_blank\" rel=\"noopener\">CLI de Tekton<\/a>, utilizado para interactuar con los recursos de Tekton.<\/li>\n<li><strong>Cosign<\/strong> \u2014 Parte del <a href=\"\/es\/ci-cd-security\/signing-verifying-container-images-sigstore-cosign\/\">proyecto Sigstore<\/a>, utilizado para firmar y verificar imagenes de contenedor.<\/li>\n<li><strong>jq<\/strong> \u2014 Un procesador JSON de linea de comandos para inspeccionar los payloads de procedencia.<\/li>\n<li><strong>Un registro de contenedores<\/strong> \u2014 Un registro al que puedas hacer push, como GitHub Container Registry (GHCR) o Docker Hub. Necesitaras acceso de escritura y credenciales validas.<\/li>\n<\/ul>\n<p>Este laboratorio asume familiaridad con los conceptos basicos de Kubernetes (pods, namespaces, configmaps) y conceptos generales de CI\/CD.<\/p>\n<h2>Configuracion del entorno<\/h2>\n<h3>Paso 1: Crear un cluster kind<\/h3>\n<p>Comienza creando un cluster de Kubernetes nuevo usando kind:<\/p>\n<pre><code>kind create cluster --name tekton-lab\nkubectl cluster-info --context kind-tekton-lab<\/code><\/pre>\n<p>Confirma que el cluster esta en ejecucion:<\/p>\n<pre><code>kubectl get nodes\n# NAME                       STATUS   ROLES           AGE   VERSION\n# tekton-lab-control-plane   Ready    control-plane   30s   v1.31.0<\/code><\/pre>\n<h3>Paso 2: Instalar Tekton Pipelines<\/h3>\n<p>Instala la ultima version de Tekton Pipelines:<\/p>\n<pre><code>kubectl apply --filename https:\/\/storage.googleapis.com\/tekton-releases\/pipeline\/latest\/release.yaml<\/code><\/pre>\n<p>Espera a que los pods de Tekton Pipelines esten listos:<\/p>\n<pre><code>kubectl get pods -n tekton-pipelines --watch<\/code><\/pre>\n<p>Deberias ver los pods <code>tekton-pipelines-controller<\/code> y <code>tekton-pipelines-webhook<\/code> en ejecucion:<\/p>\n<pre><code>NAME                                           READY   STATUS    RESTARTS   AGE\ntekton-pipelines-controller-7f6b9b5b95-xk2rj   1\/1     Running   0          45s\ntekton-pipelines-webhook-6c4f8b7d4f-m9nlp      1\/1     Running   0          45s<\/code><\/pre>\n<h3>Paso 3: Instalar Tekton Chains<\/h3>\n<p>Instala Tekton Chains en su propio namespace:<\/p>\n<pre><code>kubectl apply --filename https:\/\/storage.googleapis.com\/tekton-releases\/chains\/latest\/release.yaml<\/code><\/pre>\n<p>Verifica que Chains esta en ejecucion:<\/p>\n<pre><code>kubectl get pods -n tekton-chains\n# NAME                                        READY   STATUS    RESTARTS   AGE\n# tekton-chains-controller-5f4b7c8d6f-r7t2x   1\/1     Running   0          30s<\/code><\/pre>\n<p>En este punto, tanto Tekton Pipelines como Tekton Chains estan ejecutandose en tu cluster.<\/p>\n<h2>Ejercicio 1: Configurar Tekton Chains para firma con Cosign<\/h2>\n<p>Tekton Chains necesita una clave de firma y configuracion para saber como y donde almacenar firmas y atestaciones. En este ejercicio, generaras un par de claves Cosign y configuraras Chains para usar almacenamiento OCI con el formato de atestacion in-toto.<\/p>\n<h3>Generar un par de claves Cosign<\/h3>\n<p>Cosign puede generar un par de claves y almacenarlo directamente como un Secret de Kubernetes en el namespace <code>tekton-chains<\/code>:<\/p>\n<pre><code>cosign generate-key-pair k8s:\/\/tekton-chains\/signing-secrets<\/code><\/pre>\n<p>Se te pedira que ingreses una contrasena para la clave privada. Para este laboratorio, puedes presionar Enter para dejarla vacia. Cosign crea un Secret llamado <code>signing-secrets<\/code> que contiene la clave privada, la clave publica y la contrasena.<\/p>\n<p>Verifica que el secret fue creado:<\/p>\n<pre><code>kubectl get secret signing-secrets -n tekton-chains\n# NAME              TYPE     DATA   AGE\n# signing-secrets   Opaque   3      10s<\/code><\/pre>\n<h3>Configurar almacenamiento y formato de Chains<\/h3>\n<p>A continuacion, configura Chains para almacenar firmas en el registro OCI junto con la imagen y para generar procedencia en el formato in-toto:<\/p>\n<pre><code>kubectl patch configmap chains-config -n tekton-chains \\\n  -p='{\"data\":{\"artifacts.oci.storage\":\"oci\",\"artifacts.taskrun.format\":\"in-toto\",\"artifacts.taskrun.storage\":\"oci\"}}'<\/code><\/pre>\n<p>Esta configuracion le indica a Chains que:<\/p>\n<ul>\n<li><strong>artifacts.oci.storage: oci<\/strong> \u2014 Almacene las firmas de artefactos OCI en el registro OCI<\/li>\n<li><strong>artifacts.taskrun.format: in-toto<\/strong> \u2014 Genere atestaciones en el formato <a href=\"https:\/\/in-toto.io\/\" target=\"_blank\" rel=\"noopener\">in-toto<\/a>, que es el estandar para la procedencia SLSA<\/li>\n<li><strong>artifacts.taskrun.storage: oci<\/strong> \u2014 Almacene las atestaciones de TaskRun en el registro OCI<\/li>\n<\/ul>\n<h3>Reiniciar el controlador de Chains<\/h3>\n<p>Despues de cambiar la configuracion, reinicia el controlador de Chains para que aplique los nuevos ajustes:<\/p>\n<pre><code>kubectl rollout restart deployment tekton-chains-controller -n tekton-chains\nkubectl rollout status deployment tekton-chains-controller -n tekton-chains<\/code><\/pre>\n<h3>Como funciona Chains<\/h3>\n<p>Con Chains configurado, esto es lo que sucede automaticamente cada vez que un TaskRun se completa:<\/p>\n<ol>\n<li>El controlador de Chains detecta el TaskRun completado.<\/li>\n<li>Inspecciona los resultados del TaskRun en busca de referencias de imagenes OCI (especificamente resultados llamados <code>IMAGE_URL<\/code> e <code>IMAGE_DIGEST<\/code>).<\/li>\n<li>Firma la imagen usando la clave Cosign almacenada en el Secret <code>signing-secrets<\/code>.<\/li>\n<li>Genera una atestacion de procedencia in-toto que captura los detalles de la construccion.<\/li>\n<li>Sube la firma y la atestacion al registro OCI.<\/li>\n<li>Anota el TaskRun con <code>chains.tekton.dev\/signed=true<\/code>.<\/li>\n<\/ol>\n<p>Nada de esto requiere ninguna modificacion a tus Tasks o Pipelines.<\/p>\n<h2>Ejercicio 2: Crear un pipeline de construccion<\/h2>\n<p>Ahora crearas un Tekton Pipeline que clona un repositorio Git y construye una imagen de contenedor usando Kaniko. Primero, configura las credenciales del registro para que Tekton pueda hacer push de imagenes.<\/p>\n<h3>Configurar credenciales del registro<\/h3>\n<p>Crea un Secret de Kubernetes con tus credenciales del registro. Reemplaza los valores de ejemplo con los datos reales de tu registro:<\/p>\n<pre><code>export REGISTRY_SERVER=ghcr.io\nexport REGISTRY_USER=your-username\nexport REGISTRY_PASSWORD=your-token\n\nkubectl create secret docker-registry registry-credentials \\\n  --docker-server=$REGISTRY_SERVER \\\n  --docker-username=$REGISTRY_USER \\\n  --docker-password=$REGISTRY_PASSWORD\n\nkubectl patch serviceaccount default -p '{\"secrets\": [{\"name\": \"registry-credentials\"}]}'<\/code><\/pre>\n<h3>Crear el Task de construccion<\/h3>\n<p>Crea un archivo llamado <code>build-task.yaml<\/code>. Este Task acepta una URL de repositorio Git, un nombre de imagen destino, y usa Kaniko para construir y subir la imagen:<\/p>\n<pre><code>apiVersion: tekton.dev\/v1\nkind: Task\nmetadata:\n  name: git-clone-and-build\nspec:\n  params:\n    - name: repo-url\n      type: string\n      description: The Git repository URL to clone\n    - name: image\n      type: string\n      description: The image reference to build and push (e.g., ghcr.io\/user\/app:tag)\n  results:\n    - name: IMAGE_URL\n      description: The image URL that was built\n    - name: IMAGE_DIGEST\n      description: The digest of the built image\n  workspaces:\n    - name: source\n  steps:\n    - name: clone\n      image: alpine\/git:2.43.0\n      script: |\n        #!\/usr\/bin\/env sh\n        set -eu\n        git clone $(params.repo-url) $(workspaces.source.path)\/src\n        echo \"Repository cloned successfully\"\n    - name: build-and-push\n      image: gcr.io\/kaniko-project\/executor:latest\n      args:\n        - --dockerfile=$(workspaces.source.path)\/src\/Dockerfile\n        - --context=$(workspaces.source.path)\/src\n        - --destination=$(params.image)\n        - --digest-file=$(results.IMAGE_DIGEST.path)\n      securityContext:\n        runAsUser: 0\n    - name: write-url\n      image: alpine:3.19\n      script: |\n        #!\/usr\/bin\/env sh\n        set -eu\n        echo -n \"$(params.image)\" &gt; \"$(results.IMAGE_URL.path)\"\n        echo \"Image URL written: $(params.image)\"<\/code><\/pre>\n<p>Aplica el Task:<\/p>\n<pre><code>kubectl apply -f build-task.yaml<\/code><\/pre>\n<h3>Crear el Task de escaneo de vulnerabilidades (para uso posterior)<\/h3>\n<p>Crea <code>vuln-scan-task.yaml<\/code> \u2014 lo agregaras al pipeline en un ejercicio posterior:<\/p>\n<pre><code>apiVersion: tekton.dev\/v1\nkind: Task\nmetadata:\n  name: vulnerability-scan\nspec:\n  params:\n    - name: image\n      type: string\n      description: The image reference to scan\n  steps:\n    - name: scan\n      image: anchore\/grype:latest\n      args:\n        - $(params.image)\n        - --fail-on\n        - critical\n        - --output\n        - table<\/code><\/pre>\n<h3>Crear el Pipeline<\/h3>\n<p>Crea <code>build-pipeline.yaml<\/code> que encadena los pasos de clonacion y construccion:<\/p>\n<pre><code>apiVersion: tekton.dev\/v1\nkind: Pipeline\nmetadata:\n  name: secure-build\nspec:\n  params:\n    - name: repo-url\n      type: string\n    - name: image\n      type: string\n  workspaces:\n    - name: shared-workspace\n  tasks:\n    - name: build\n      taskRef:\n        name: git-clone-and-build\n      params:\n        - name: repo-url\n          value: $(params.repo-url)\n        - name: image\n          value: $(params.image)\n      workspaces:\n        - name: source\n          workspace: shared-workspace<\/code><\/pre>\n<p>Aplica el Pipeline:<\/p>\n<pre><code>kubectl apply -f build-pipeline.yaml<\/code><\/pre>\n<h3>Ejecutar el Pipeline<\/h3>\n<p>Crea un PipelineRun para ejecutar el pipeline. Reemplaza la referencia de imagen con tu registro:<\/p>\n<pre><code>apiVersion: tekton.dev\/v1\nkind: PipelineRun\nmetadata:\n  generateName: secure-build-run-\nspec:\n  pipelineRef:\n    name: secure-build\n  params:\n    - name: repo-url\n      value: \"https:\/\/github.com\/GoogleContainerTools\/kaniko.git\"\n    - name: image\n      value: \"ghcr.io\/your-username\/tekton-lab:v1\"\n  workspaces:\n    - name: shared-workspace\n      volumeClaimTemplate:\n        spec:\n          accessModes:\n            - ReadWriteOnce\n          resources:\n            requests:\n              storage: 1Gi<\/code><\/pre>\n<p>Guarda esto como <code>pipelinerun.yaml<\/code> y crealo:<\/p>\n<pre><code>kubectl create -f pipelinerun.yaml<\/code><\/pre>\n<p>Monitorea la ejecucion del pipeline:<\/p>\n<pre><code>tkn pipelinerun logs -f --last\n# [build : clone] Cloning into '\/workspace\/source\/src'...\n# [build : clone] Repository cloned successfully\n# [build : build-and-push] INFO[0001] Resolved base image golang:1.22\n# [build : build-and-push] ...\n# [build : build-and-push] INFO[0045] Pushing image to ghcr.io\/your-username\/tekton-lab:v1\n# [build : write-url] Image URL written: ghcr.io\/your-username\/tekton-lab:v1<\/code><\/pre>\n<p>La construccion deberia completarse exitosamente. Tambien puedes verificar el estado del PipelineRun:<\/p>\n<pre><code>tkn pipelinerun list\n# NAME                     STARTED        DURATION   STATUS\n# secure-build-run-x7k2p   1 minute ago   1m 15s     Succeeded<\/code><\/pre>\n<h2>Ejercicio 3: Verificar la firma automatica<\/h2>\n<p>Una vez que el PipelineRun se completa, Tekton Chains detecta automaticamente el TaskRun completado, firma la imagen construida y anota el TaskRun. Todo esto sucede en segundo plano \u2014 no se necesitan cambios en el pipeline.<\/p>\n<h3>Esperar a que Chains firme<\/h3>\n<p>Chains procesa los TaskRuns completados de forma asincrona. Espera unos momentos, luego verifica las anotaciones del TaskRun:<\/p>\n<pre><code># Get the TaskRun name from the PipelineRun\nTASKRUN=$(kubectl get taskrun -l tekton.dev\/pipeline=secure-build -o name --sort-by=.metadata.creationTimestamp | tail -1)\necho $TASKRUN\n\n# Check if Chains has signed it\nkubectl get $TASKRUN -o jsonpath='{.metadata.annotations.chains\\.tekton\\.dev\/signed}'<\/code><\/pre>\n<p>La salida deberia ser:<\/p>\n<pre><code>true<\/code><\/pre>\n<p>Si aun muestra vacio, espera unos segundos e intenta de nuevo \u2014 Chains necesita tiempo para procesar la firma.<\/p>\n<h3>Verificar la firma con Cosign<\/h3>\n<p>Ahora verifica la firma de la imagen usando la clave publica del par de claves Cosign que generaste anteriormente:<\/p>\n<pre><code>cosign verify \\\n  --key k8s:\/\/tekton-chains\/signing-secrets \\\n  ghcr.io\/your-username\/tekton-lab:v1<\/code><\/pre>\n<p>Deberias ver una salida confirmando que la verificacion fue exitosa:<\/p>\n<pre><code>Verification for ghcr.io\/your-username\/tekton-lab:v1 --\nThe following checks were performed on each of these signatures:\n  - The cosign claims were validated\n  - The signatures were verified against the specified public key\n\n[{\"critical\":{\"identity\":{\"docker-reference\":\"ghcr.io\/your-username\/tekton-lab\"},\"image\":{\"docker-manifest-digest\":\"sha256:abc123...\"},\"type\":\"cosign container image signature\"},\"optional\":{}}]<\/code><\/pre>\n<h3>Inspeccionar las anotaciones del TaskRun<\/h3>\n<p>Chains anota el TaskRun con metadatos detallados sobre la operacion de firma:<\/p>\n<pre><code>kubectl get $TASKRUN -o jsonpath='{.metadata.annotations}' | jq .<\/code><\/pre>\n<p>Las anotaciones clave incluyen:<\/p>\n<pre><code>{\n  \"chains.tekton.dev\/signed\": \"true\",\n  \"chains.tekton.dev\/transparency\": \"https:\/\/rekor.sigstore.dev\/api\/v1\/log\/entries?logIndex=...\",\n  \"chains.tekton.dev\/signature-taskrun-...\": \"...\"\n}<\/code><\/pre>\n<p>La anotacion <code>chains.tekton.dev\/signed=true<\/code> confirma que Chains proceso y firmo exitosamente este TaskRun. Si se configuro un log de transparencia, tambien veras una referencia a una entrada del log de Rekor.<\/p>\n<h2>Ejercicio 4: Inspeccionar la procedencia SLSA<\/h2>\n<p>Mas alla de las firmas simples, Tekton Chains genera atestaciones completas de procedencia SLSA. Estas atestaciones describen <em>como<\/em> se construyo el artefacto \u2014 que fuente se uso, que pasos de construccion se ejecutaron y que herramientas estuvieron involucradas.<\/p>\n<h3>Obtener la atestacion de procedencia<\/h3>\n<p>Usa Cosign para verificar y recuperar la atestacion in-toto:<\/p>\n<pre><code>cosign verify-attestation \\\n  --key k8s:\/\/tekton-chains\/signing-secrets \\\n  --type slsaprovenance \\\n  ghcr.io\/your-username\/tekton-lab:v1 | jq -r '.payload' | base64 -d | jq .<\/code><\/pre>\n<h3>Comprender la estructura de la procedencia<\/h3>\n<p>La atestacion de procedencia sigue el formato in-toto Statement con un predicado de procedencia SLSA. Aqui hay un desglose de los campos clave:<\/p>\n<pre><code>{\n  \"_type\": \"https:\/\/in-toto.io\/Statement\/v0.1\",\n  \"predicateType\": \"https:\/\/slsa.dev\/provenance\/v0.2\",\n  \"subject\": [\n    {\n      \"name\": \"ghcr.io\/your-username\/tekton-lab\",\n      \"digest\": {\n        \"sha256\": \"abc123def456...\"\n      }\n    }\n  ],\n  \"predicate\": {\n    \"builder\": {\n      \"id\": \"https:\/\/tekton.dev\/chains\/v2\"\n    },\n    \"buildType\": \"tekton.dev\/v1beta1\/TaskRun\",\n    \"invocation\": {\n      \"configSource\": {},\n      \"parameters\": {\n        \"repo-url\": \"https:\/\/github.com\/GoogleContainerTools\/kaniko.git\",\n        \"image\": \"ghcr.io\/your-username\/tekton-lab:v1\"\n      }\n    },\n    \"buildConfig\": {\n      \"steps\": [\n        {\n          \"entryPoint\": \"...\",\n          \"arguments\": null,\n          \"environment\": {\n            \"container\": \"clone\",\n            \"image\": \"alpine\/git:2.43.0@sha256:...\"\n          }\n        },\n        {\n          \"entryPoint\": \"...\",\n          \"environment\": {\n            \"container\": \"build-and-push\",\n            \"image\": \"gcr.io\/kaniko-project\/executor:latest@sha256:...\"\n          }\n        }\n      ]\n    },\n    \"materials\": [\n      {\n        \"uri\": \"oci:\/\/alpine\/git:2.43.0\",\n        \"digest\": { \"sha256\": \"...\" }\n      },\n      {\n        \"uri\": \"oci:\/\/gcr.io\/kaniko-project\/executor:latest\",\n        \"digest\": { \"sha256\": \"...\" }\n      }\n    ]\n  }\n}<\/code><\/pre>\n<p>Veamos cada campo en detalle:<\/p>\n<ul>\n<li><strong>subject<\/strong> \u2014 El artefacto que se produjo, identificado por su URL de registro y digest SHA-256. Esto es sobre lo que trata la procedencia.<\/li>\n<li><strong>builder.id<\/strong> \u2014 Identifica el sistema de construccion. Tekton Chains establece esto como <code>https:\/\/tekton.dev\/chains\/v2<\/code>.<\/li>\n<li><strong>buildConfig.steps<\/strong> \u2014 Registra cada paso que se ejecuto en el TaskRun, incluyendo las imagenes de contenedor exactas utilizadas (fijadas por digest).<\/li>\n<li><strong>materials<\/strong> \u2014 Lista los artefactos de entrada consumidos durante la construccion, como las imagenes base. Cada material incluye un digest para la reproducibilidad.<\/li>\n<li><strong>invocation.parameters<\/strong> \u2014 Captura los parametros pasados al TaskRun, mostrando exactamente que entradas impulsaron la construccion.<\/li>\n<\/ul>\n<p>Estos datos de procedencia satisfacen los requisitos de <strong>SLSA Level 2<\/strong>: el proceso de construccion se define en un servicio de construccion (Tekton), y la procedencia es generada automaticamente por Tekton Chains (no por el script de construccion en si). La procedencia esta firmada, proporcionando evidencia contra manipulaciones.<\/p>\n<h2>Ejercicio 5: Agregar un Task de escaneo de vulnerabilidades<\/h2>\n<p>Un pipeline seguro no solo debe firmar artefactos, sino tambien verificar que estan libres de vulnerabilidades conocidas antes del despliegue. En este ejercicio, agregaras un paso de escaneo de vulnerabilidades Grype al pipeline.<\/p>\n<h3>Aplicar el Task de escaneo<\/h3>\n<p>Aplica el Task de escaneo de vulnerabilidades que creaste anteriormente:<\/p>\n<pre><code>kubectl apply -f vuln-scan-task.yaml<\/code><\/pre>\n<h3>Actualizar el Pipeline<\/h3>\n<p>Actualiza <code>build-pipeline.yaml<\/code> para incluir el escaneo de vulnerabilidades despues del paso de construccion:<\/p>\n<pre><code>apiVersion: tekton.dev\/v1\nkind: Pipeline\nmetadata:\n  name: secure-build\nspec:\n  params:\n    - name: repo-url\n      type: string\n    - name: image\n      type: string\n  workspaces:\n    - name: shared-workspace\n  tasks:\n    - name: build\n      taskRef:\n        name: git-clone-and-build\n      params:\n        - name: repo-url\n          value: $(params.repo-url)\n        - name: image\n          value: $(params.image)\n      workspaces:\n        - name: source\n          workspace: shared-workspace\n    - name: vulnerability-scan\n      runAfter:\n        - build\n      taskRef:\n        name: vulnerability-scan\n      params:\n        - name: image\n          value: $(params.image)<\/code><\/pre>\n<p>Aplica el pipeline actualizado:<\/p>\n<pre><code>kubectl apply -f build-pipeline.yaml<\/code><\/pre>\n<h3>Probar con una imagen vulnerable<\/h3>\n<p>Para demostrar que el escaneo detecta vulnerabilidades, crea un Dockerfile que use una imagen base con vulnerabilidades conocidas y sube un repositorio o modifica los parametros segun corresponda. Si la imagen contiene vulnerabilidades criticas, Grype fallara el paso:<\/p>\n<pre><code>tkn pipelinerun logs -f --last\n# [vulnerability-scan : scan] NAME             INSTALLED  FIXED-IN  TYPE  VULNERABILITY   SEVERITY\n# [vulnerability-scan : scan] libcrypto3       3.0.12     3.0.13    apk   CVE-2024-0727   Critical\n# [vulnerability-scan : scan] 1 critical vulnerability found\n# [vulnerability-scan : scan] ERROR: failed to pass severity threshold\n#\n# TaskRun failed: step \"scan\" exited with code 1<\/code><\/pre>\n<p>El pipeline falla correctamente en el paso de escaneo, evitando que una imagen vulnerable sea promovida.<\/p>\n<h3>Probar con una imagen parcheada<\/h3>\n<p>Ahora ejecuta el pipeline contra un repositorio con una imagen base actualizada. Cuando no se encuentran vulnerabilidades criticas, el escaneo pasa:<\/p>\n<pre><code>tkn pipelinerun logs -f --last\n# [vulnerability-scan : scan] No critical vulnerabilities found\n# PipelineRun completed successfully<\/code><\/pre>\n<p>El flujo del pipeline ahora es: <strong>git-clone \u2192 build-push \u2192 vulnerability-scan<\/strong>. Solo las imagenes que pasan el escaneo de vulnerabilidades son firmadas por Tekton Chains, porque Chains solo procesa TaskRuns <em>exitosos<\/em>.<\/p>\n<h2>Ejercicio 6: Firma sin claves con Fulcio (Avanzado)<\/h2>\n<p>Gestionar claves de firma de larga duracion introduce complejidad operativa y riesgo de seguridad. <strong>Fulcio<\/strong> de Sigstore proporciona firma sin claves emitiendo certificados de corta duracion vinculados a una identidad OIDC. En este ejercicio, configuraras Tekton Chains para usar firma sin claves.<\/p>\n<h3>Actualizar la configuracion de Chains<\/h3>\n<p>Modifica la configuracion de Chains para habilitar la firma sin claves:<\/p>\n<pre><code>kubectl patch configmap chains-config -n tekton-chains -p='{\"data\":{\n  \"signers.x509.fulcio.enabled\": \"true\",\n  \"signers.x509.fulcio.address\": \"https:\/\/fulcio.sigstore.dev\",\n  \"transparency.enabled\": \"true\",\n  \"transparency.url\": \"https:\/\/rekor.sigstore.dev\"\n}}'<\/code><\/pre>\n<p>Tambien necesitas eliminar o renombrar el secret <code>signing-secrets<\/code> existente para que Chains recurra al modo sin claves:<\/p>\n<pre><code>kubectl delete secret signing-secrets -n tekton-chains<\/code><\/pre>\n<p>Reinicia el controlador de Chains:<\/p>\n<pre><code>kubectl rollout restart deployment tekton-chains-controller -n tekton-chains<\/code><\/pre>\n<h3>Configurar OIDC para Chains<\/h3>\n<p>Chains necesita un token OIDC para autenticarse con Fulcio. En un servicio de Kubernetes gestionado (GKE, EKS, AKS), puedes usar workload identity. Para un cluster kind local, puedes configurar Spiffe\/SPIRE o usar un proveedor OIDC ambiental. La documentacion de Tekton Chains proporciona instrucciones de configuracion para cada entorno.<\/p>\n<p>Para una configuracion de produccion en GKE, la cuenta de servicio se federa automaticamente:<\/p>\n<pre><code># Example: GKE workload identity binding\ngcloud iam service-accounts add-iam-policy-binding \\\n  tekton-chains-sa@your-project.iam.gserviceaccount.com \\\n  --role roles\/iam.workloadIdentityUser \\\n  --member \"serviceAccount:your-project.svc.id.goog[tekton-chains\/tekton-chains-controller]\"<\/code><\/pre>\n<h3>Ejecutar el Pipeline con firma sin claves<\/h3>\n<p>Lanza un nuevo PipelineRun:<\/p>\n<pre><code>kubectl create -f pipelinerun.yaml<\/code><\/pre>\n<p>Despues de completarse, verifica con verificacion sin claves especificando la identidad esperada y el emisor OIDC:<\/p>\n<pre><code>cosign verify \\\n  --certificate-identity \"https:\/\/kubernetes.io\/namespaces\/tekton-chains\/serviceaccounts\/tekton-chains-controller\" \\\n  --certificate-oidc-issuer \"https:\/\/your-oidc-issuer\" \\\n  ghcr.io\/your-username\/tekton-lab:v2<\/code><\/pre>\n<p>La verificacion ahora se basa en la cadena de certificados de Fulcio en lugar de un par de claves estatico. Este enfoque elimina completamente la gestion de claves: cada operacion de firma obtiene un certificado nuevo de corta duracion, y el evento de firma se registra en el log de transparencia de Rekor para auditabilidad.<\/p>\n<h2>Ejercicio 7: Aplicar imagenes firmadas en el despliegue<\/h2>\n<p>Firmar imagenes solo es util si <em>aplicas<\/em> la verificacion de firmas en el momento del despliegue. En este ejercicio, desplegaras el policy-controller de Sigstore para rechazar cualquier imagen de contenedor que carezca de una firma valida de Tekton Chains.<\/p>\n<h3>Instalar el Policy Controller de Sigstore<\/h3>\n<pre><code>helm repo add sigstore https:\/\/sigstore.github.io\/helm-charts\nhelm repo update\n\nhelm install policy-controller sigstore\/policy-controller \\\n  --namespace cosign-system \\\n  --create-namespace \\\n  --set webhook.configMapName=policy-controller-config<\/code><\/pre>\n<p>Espera a que el policy controller este listo:<\/p>\n<pre><code>kubectl get pods -n cosign-system --watch<\/code><\/pre>\n<h3>Crear una politica de imagenes<\/h3>\n<p>Crea una <code>ClusterImagePolicy<\/code> que requiera que las imagenes esten firmadas por tu clave de Tekton Chains. Guarda esto como <code>image-policy.yaml<\/code>:<\/p>\n<pre><code>apiVersion: policy.sigstore.dev\/v1beta1\nkind: ClusterImagePolicy\nmetadata:\n  name: tekton-chains-signed\nspec:\n  images:\n    - glob: \"ghcr.io\/your-username\/**\"\n  authorities:\n    - key:\n        data: |\n          -----BEGIN PUBLIC KEY-----\n          YOUR_COSIGN_PUBLIC_KEY_HERE\n          -----END PUBLIC KEY-----\n      attestations:\n        - name: must-have-slsa-provenance\n          predicateType: \"https:\/\/slsa.dev\/provenance\/v0.2\"\n          policy:\n            type: cue\n            data: |\n              predicateType: \"https:\/\/slsa.dev\/provenance\/v0.2\"<\/code><\/pre>\n<p>Reemplaza la clave publica con la clave publica de Cosign que generaste anteriormente:<\/p>\n<pre><code># Extract the public key\nkubectl get secret signing-secrets -n tekton-chains -o jsonpath='{.data.cosign\\.pub}' | base64 -d<\/code><\/pre>\n<p>Aplica la politica:<\/p>\n<pre><code>kubectl apply -f image-policy.yaml<\/code><\/pre>\n<h3>Aplicar la politica en un namespace<\/h3>\n<p>Etiqueta un namespace para habilitar la aplicacion de la politica:<\/p>\n<pre><code>kubectl create namespace secure-apps\nkubectl label namespace secure-apps policy.sigstore.dev\/include=true<\/code><\/pre>\n<h3>Prueba: Desplegar una imagen firmada<\/h3>\n<p>Despliega la imagen que fue firmada por Tekton Chains:<\/p>\n<pre><code>kubectl run signed-app \\\n  --image=ghcr.io\/your-username\/tekton-lab:v1 \\\n  --namespace=secure-apps\n# pod\/signed-app created<\/code><\/pre>\n<p>El despliegue tiene exito porque la imagen tiene una firma valida y una atestacion de procedencia.<\/p>\n<h3>Prueba: Desplegar una imagen sin firmar<\/h3>\n<p>Ahora intenta desplegar una imagen que no fue firmada:<\/p>\n<pre><code>kubectl run unsigned-app \\\n  --image=ghcr.io\/your-username\/unsigned-image:latest \\\n  --namespace=secure-apps\n# Error from server (BadRequest): admission webhook \"policy.sigstore.dev\" denied the request:\n# validation failed: failed policy: tekton-chains-signed:\n# spec.containers[0].image ghcr.io\/your-username\/unsigned-image:latest\n# signature key validation failed for authority<\/code><\/pre>\n<p>El webhook de admision rechaza correctamente la imagen sin firmar. Esto cierra el ciclo: las imagenes se firman automaticamente durante la construccion, y solo las imagenes firmadas pueden ser desplegadas.<\/p>\n<h2>Limpieza<\/h2>\n<p>Cuando hayas terminado con el laboratorio, limpia los recursos:<\/p>\n<pre><code># Delete Tekton Chains\nkubectl delete -f https:\/\/storage.googleapis.com\/tekton-releases\/chains\/latest\/release.yaml\n\n# Delete Tekton Pipelines\nkubectl delete -f https:\/\/storage.googleapis.com\/tekton-releases\/pipeline\/latest\/release.yaml\n\n# Delete the policy controller\nhelm uninstall policy-controller -n cosign-system\nkubectl delete namespace cosign-system\n\n# Delete the kind cluster\nkind delete cluster --name tekton-lab<\/code><\/pre>\n<h2>Conclusiones clave<\/h2>\n<ul>\n<li><strong>Tekton Chains proporciona seguridad de la cadena de suministro sin configuracion adicional.<\/strong> Una vez instalado y configurado, firma automaticamente cada resultado de TaskRun y genera procedencia SLSA \u2014 sin necesidad de modificar los pipelines.<\/li>\n<li><strong>La procedencia SLSA conecta los artefactos con su proceso de construccion.<\/strong> La atestacion in-toto registra exactamente que fuente, pasos y herramientas produjeron un artefacto, creando una cadena de custodia auditable.<\/li>\n<li><strong>La verificacion con Cosign es sencilla.<\/strong> Un solo comando valida que una imagen fue firmada por tu instancia de Tekton Chains y que no ha sido manipulada desde entonces.<\/li>\n<li><strong>La firma sin claves elimina la gestion de claves.<\/strong> Al integrarse con Fulcio y Rekor, puedes firmar artefactos con certificados de corta duracion vinculados a la identidad del workload, eliminando la carga de rotar y asegurar claves de larga duracion.<\/li>\n<li><strong>El escaneo de vulnerabilidades como puerta del pipeline previene despliegues inseguros.<\/strong> Agregar Grype o un escaner similar como paso del pipeline asegura que solo las imagenes libres de vulnerabilidades criticas procedan a la firma y el despliegue.<\/li>\n<li><strong>El control de admision aplica la politica.<\/strong> Usar el policy-controller de Sigstore como webhook de admision de Kubernetes asegura que solo las imagenes debidamente firmadas y atestadas puedan ejecutarse en tu cluster, cerrando el ciclo de seguridad desde la construccion hasta el despliegue.<\/li>\n<\/ul>\n<h2>Proximos pasos<\/h2>\n<p>Continua fortaleciendo tus conocimientos sobre seguridad de la cadena de suministro con estas guias relacionadas:<\/p>\n<ul>\n<li><a href=\"\/es\/ci-cd-security\/artifact-provenance-attestations-slsa-in-toto\/\">Procedencia de artefactos y atestaciones: De SLSA a in-toto<\/a> \u2014 Profundizacion en el framework SLSA, niveles de procedencia y la especificacion de atestacion in-toto.<\/li>\n<li><a href=\"\/es\/ci-cd-security\/signing-verifying-container-images-sigstore-cosign\/\">Firma y verificacion de imagenes de contenedor con Sigstore y Cosign<\/a> \u2014 Guia completa sobre Cosign, Fulcio y Rekor para la firma y verificacion de imagenes de contenedor.<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Descripcion general Tekton es un potente framework de codigo abierto, nativo de Kubernetes, para crear sistemas de integracion continua y entrega continua (CI\/CD). Se ejecuta como un conjunto de Custom Resource Definitions (CRDs) en cualquier cluster de Kubernetes, permitiendote definir pipelines como YAML declarativo que son portables entre entornos. Tekton Chains es un proyecto complementario &#8230; <a title=\"Lab: Implementacion de un pipeline de construccion seguro con Tekton y Tekton Chains\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-secure-build-pipeline-tekton-tekton-chains-2\/\" aria-label=\"Leer m\u00e1s sobre Lab: Implementacion de un pipeline de construccion seguro con Tekton y Tekton Chains\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,59],"tags":[],"post_folder":[],"class_list":["post-707","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security","category-software-supply-chain"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/707","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/comments?post=707"}],"version-history":[{"count":0,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/707\/revisions"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=707"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=707"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=707"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=707"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}