{"id":646,"date":"2026-02-01T13:15:44","date_gmt":"2026-02-01T12:15:44","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=646"},"modified":"2026-03-24T18:08:04","modified_gmt":"2026-03-24T17:08:04","slug":"build-integrity-reproducible-builds-ci-cd","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/build-integrity-reproducible-builds-ci-cd\/","title":{"rendered":"Integridad de Builds y Builds Reproducibles: Gu\u00eda Pr\u00e1ctica para CI\/CD"},"content":{"rendered":"<h2>Introducci\u00f3n<\/h2>\n<p>Si no puede reproducir un build, no puede verificarlo. Esta simple verdad se encuentra en el coraz\u00f3n de la seguridad de la cadena de suministro de software. La integridad de builds garantiza que lo que usted despliega es exactamente lo que pretend\u00eda construir \u2014 nada a\u00f1adido, nada modificado, nada manipulado entre el c\u00f3digo fuente y el artefacto de producci\u00f3n.<\/p>\n<p>En los \u00faltimos a\u00f1os, los ataques a la cadena de suministro han demostrado que el proceso de build en s\u00ed mismo es un objetivo de alto valor. Los atacantes que comprometen un pipeline de build pueden inyectar c\u00f3digo malicioso en software confiable, afectando a millones de usuarios posteriores. La \u00fanica defensa confiable es hacer que los builds sean reproducibles: dados los mismos inputs, siempre se deben obtener los mismos outputs. Cuando esa garant\u00eda se cumple, cualquier desviaci\u00f3n se vuelve detectable.<\/p>\n<p>Esta gu\u00eda recorre los principios de integridad de builds y builds reproducibles, explica por qu\u00e9 son importantes para la seguridad de CI\/CD, y proporciona t\u00e9cnicas pr\u00e1cticas \u2014 desde el pinning de dependencias hasta sistemas de build herm\u00e9ticos \u2014 que puede adoptar de forma incremental en sus propios pipelines.<\/p>\n<h2>\u00bfQu\u00e9 es la Integridad de Builds?<\/h2>\n<p>La integridad de builds es la garant\u00eda de que los inputs de un build producen de forma determinista los outputs del build. En otras palabras, si toma el mismo c\u00f3digo fuente, las mismas dependencias, la misma toolchain y las mismas instrucciones de build, deber\u00eda obtener un artefacto id\u00e9ntico bit a bit cada vez, independientemente de cu\u00e1ndo o d\u00f3nde ejecute el build.<\/p>\n<h3>Por qu\u00e9 los Builds No Reproducibles son un Riesgo de Seguridad<\/h3>\n<p>Cuando los builds no son reproducibles, se pierde la capacidad de verificarlos. Si dos builds del mismo c\u00f3digo fuente producen binarios diferentes, \u00bfc\u00f3mo sabe cu\u00e1l es el correcto? \u00bfC\u00f3mo detecta si un atacante inyect\u00f3 c\u00f3digo durante el proceso de build? Simplemente no puede. La no reproducibilidad crea una niebla de guerra que los atacantes explotan.<\/p>\n<p>Considere el problema de verificaci\u00f3n: un auditor quiere confirmar que un binario publicado corresponde a su c\u00f3digo fuente publicado. Hace checkout del commit etiquetado, ejecuta el build y compara. Si el build no es reproducible, los outputs diferir\u00e1n \u2014 y el auditor no tiene forma de distinguir una diferencia leg\u00edtima (causada por un timestamp o un ordenamiento aleatorio) de una modificaci\u00f3n maliciosa.<\/p>\n<h3>La Relaci\u00f3n con los Niveles de Build de SLSA<\/h3>\n<p>El <a href=\"https:\/\/slsa.dev\" target=\"_blank\" rel=\"noopener\">framework SLSA<\/a> (Supply-chain Levels for Software Artifacts) aborda directamente la integridad de builds a trav\u00e9s de sus niveles de build track:<\/p>\n<ul>\n<li><strong>SLSA Build L1:<\/strong> El proceso de build est\u00e1 documentado y produce metadata de provenance.<\/li>\n<li><strong>SLSA Build L2:<\/strong> El build se ejecuta en un servicio alojado que genera provenance autenticado.<\/li>\n<li><strong>SLSA Build L3:<\/strong> El entorno de build est\u00e1 fortalecido, aislado y es resistente a la manipulaci\u00f3n \u2014 incluso por parte de los mantenedores del proyecto.<\/li>\n<\/ul>\n<p>Los builds reproducibles complementan SLSA proporcionando un mecanismo de verificaci\u00f3n independiente. Incluso si conf\u00eda en el servicio de build (L2\/L3), la reproducibilidad permite que cualquiera reconstruya desde el c\u00f3digo fuente y verifique que el output coincide.<\/p>\n<h3>Ejemplos del Mundo Real<\/h3>\n<p><strong>SolarWinds (2020):<\/strong> Los atacantes comprometieron el sistema de build de SolarWinds e inyectaron un backdoor en la actualizaci\u00f3n de la plataforma Orion. El c\u00f3digo malicioso se a\u00f1adi\u00f3 durante el proceso de build, por lo que el repositorio de c\u00f3digo fuente parec\u00eda limpio. Un sistema de build reproducible habr\u00eda hecho esto detectable \u2014 reconstruir desde el c\u00f3digo fuente publicado habr\u00eda producido un artefacto diferente al distribuido a los clientes.<\/p>\n<p><strong>XZ Utils (2024):<\/strong> Un sofisticado ataque a la cadena de suministro apunt\u00f3 a la biblioteca de compresi\u00f3n xz. Un mantenedor malicioso introdujo c\u00f3digo de backdoor ofuscado a trav\u00e9s de la infraestructura de pruebas del sistema de build. El c\u00f3digo inyectado fue dise\u00f1ado para comprometer la autenticaci\u00f3n SSH en sistemas Linux afectados. El ataque explot\u00f3 la complejidad del proceso de build, inyectando payload malicioso a trav\u00e9s de fixtures de prueba binarios que se procesaban durante el build. Los builds reproducibles y una revisi\u00f3n cuidadosa del comportamiento en tiempo de build habr\u00edan levantado se\u00f1ales de alerta mucho antes.<\/p>\n<h2>Fuentes de No Reproducibilidad<\/h2>\n<p>Comprender por qu\u00e9 los builds difieren entre ejecuciones es el primer paso para solucionarlos. Estas son las fuentes m\u00e1s comunes de no determinismo en los procesos de build:<\/p>\n<h3>Timestamps Embebidos en Artefactos<\/h3>\n<p>Muchas herramientas de build incrustan la fecha y hora actuales en los archivos de salida. Los archivos JAR de Java contienen timestamps en sus entradas ZIP. Los compiladores de C\/C++ pueden registrar las macros <code>__DATE__<\/code> y <code>__TIME__<\/code>. Los ejecutables PE en Windows incluyen un timestamp en sus headers. Cada vez que construye, el timestamp cambia, produciendo un output diferente.<\/p>\n<h3>Ordenamiento No Determinista de Archivos<\/h3>\n<p>Los formatos de archivo como tar y zip no garantizan un orden de archivos consistente. El orden en que los archivos se a\u00f1aden a un archivo puede depender del orden de listado de directorios del filesystem, que puede variar entre m\u00e1quinas o incluso entre ejecuciones en la misma m\u00e1quina. Esto produce archivos diferentes con contenidos id\u00e9nticos.<\/p>\n<h3>Versiones Flotantes de Dependencias<\/h3>\n<p>Si su configuraci\u00f3n de build especifica <code>express: ^4.18.0<\/code> en lugar de una versi\u00f3n exacta, podr\u00eda obtener 4.18.1 hoy y 4.18.2 ma\u00f1ana. Las dependencias sin pinning son una de las fuentes m\u00e1s comunes e impactantes de no reproducibilidad.<\/p>\n<h3>Diferencias en el Entorno de Build<\/h3>\n<p>Diferentes versiones de sistema operativo, versiones de compilador, versiones de bibliotecas del sistema, configuraciones de locale, configuraciones de timezone e incluso el n\u00famero de cores de CPU pueden afectar el output del build. Un build en Ubuntu 22.04 puede diferir de uno en Ubuntu 24.04, incluso con el mismo c\u00f3digo fuente y dependencias.<\/p>\n<h3>Descargas de Red Durante el Build<\/h3>\n<p>Los builds que descargan dependencias en tiempo de build son inherentemente no reproducibles. Un registry de paquetes podr\u00eda servir una versi\u00f3n diferente, un CDN podr\u00eda devolver contenido cacheado o actualizado, o la red podr\u00eda no estar disponible en absoluto. Cualquier build que requiera acceso a la red est\u00e1 a merced de sistemas externos.<\/p>\n<h3>Valores Aleatorios y Direcciones de Memoria<\/h3>\n<p>Algunos procesos de build incrustan UUIDs aleatorios, utilizan \u00f3rdenes de iteraci\u00f3n de hash maps no deterministas, o incluyen direcciones de memoria en su output. Los datos de profiling, informaci\u00f3n de cobertura y s\u00edmbolos de debug pueden introducir aleatoriedad en los artefactos de build.<\/p>\n<h2>Builds Herm\u00e9ticos: El Est\u00e1ndar de Oro<\/h2>\n<p>Un build herm\u00e9tico es aquel que es completamente aut\u00f3nomo: no tiene acceso a la red, todos los inputs se declaran expl\u00edcitamente y el entorno de build est\u00e1 completamente especificado. Los builds herm\u00e9ticos son el est\u00e1ndar de oro para la reproducibilidad porque eliminan categor\u00edas enteras de no determinismo por dise\u00f1o.<\/p>\n<h3>Qu\u00e9 Significa un Build Herm\u00e9tico<\/h3>\n<p>En un build herm\u00e9tico:<\/p>\n<ul>\n<li>El proceso de build no puede acceder a la red. Todas las dependencias deben ser pre-descargadas y declaradas.<\/li>\n<li>Cada input \u2014 c\u00f3digo fuente, dependencias, toolchain, configuraci\u00f3n \u2014 est\u00e1 expl\u00edcitamente listado y versionado.<\/li>\n<li>El entorno de build se define con precisi\u00f3n, hasta el sistema operativo, paquetes instalados y variables de entorno.<\/li>\n<li>El build est\u00e1 sandboxed para que no pueda leer archivos no declarados del sistema host.<\/li>\n<\/ul>\n<h3>Bazel como Sistema de Build Herm\u00e9tico<\/h3>\n<p>Bazel est\u00e1 dise\u00f1ado desde cero para builds herm\u00e9ticos y reproducibles. Hace sandbox de cada acci\u00f3n de build, declara todos los inputs y outputs expl\u00edcitamente, y cachea resultados bas\u00e1ndose en hashes de inputs en lugar de timestamps. Las funcionalidades de remote caching y remote execution de Bazel mantienen la hermeticidad incluso en entornos de build distribuidos.<\/p>\n<pre><code># Bazel WORKSPACE file: all external dependencies declared\nload(\"@bazel_tools\/\/tools\/build_defs\/repo:http.bzl\", \"http_archive\")\n\nhttp_archive(\n    name = \"com_google_protobuf\",\n    sha256 = \"a79d19dcdf9139fa4b81206e318e33d245c4c9da1ffed21c87288f9142c5f4ef\",\n    strip_prefix = \"protobuf-23.2\",\n    urls = [\"https:\/\/github.com\/protocolbuffers\/protobuf\/archive\/v23.2.tar.gz\"],\n)<\/code><\/pre>\n<h3>Docker Multi-Stage Builds con Im\u00e1genes Base Fijadas<\/h3>\n<p>Los Docker multi-stage builds pueden aproximar la hermeticidad cuando se combinan con im\u00e1genes base fijadas y dependencias pre-descargadas:<\/p>\n<pre><code># Stage 1: Build with all dependencies pre-installed\nFROM golang@sha256:2c3f3c4a1f8e4c2b7d5e1a9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b AS builder\n\nWORKDIR \/app\nCOPY go.mod go.sum .\/\nRUN go mod download\n\nCOPY . .\nRUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \\\n    go build -trimpath -ldflags='-s -w -buildid=' \\\n    -o \/app\/server .\/cmd\/server\n\n# Stage 2: Minimal runtime image\nFROM gcr.io\/distroless\/static@sha256:1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f9a0b AS runtime\nCOPY --from=builder \/app\/server \/server\nENTRYPOINT [\"\/server\"]<\/code><\/pre>\n<p>Note el uso de <code>-trimpath<\/code> para eliminar las rutas de archivos locales del binario, <code>-buildid=<\/code> para limpiar el build ID, y <code>-s -w<\/code> para eliminar la informaci\u00f3n de debug. Estos flags son esenciales para la reproducibilidad en builds de Go.<\/p>\n<h3>Lock Files<\/h3>\n<p>Los lock files son la herramienta m\u00e1s accesible para mejorar la reproducibilidad. Registran las versiones exactas resueltas de todas las dependencias, incluyendo las transitivas:<\/p>\n<ul>\n<li><strong>Node.js:<\/strong> <code>package-lock.json<\/code> o <code>yarn.lock<\/code> \u2014 siempre use <code>npm ci<\/code> en lugar de <code>npm install<\/code><\/li>\n<li><strong>Go:<\/strong> <code>go.sum<\/code> \u2014 registra hashes criptogr\u00e1ficos de todas las versiones de m\u00f3dulos<\/li>\n<li><strong>Python:<\/strong> <code>poetry.lock<\/code> o output de <code>pip-compile<\/code> \u2014 fija cada dependencia transitiva<\/li>\n<li><strong>Rust:<\/strong> <code>Cargo.lock<\/code> \u2014 siempre se hace commit para proyectos binarios<\/li>\n<\/ul>\n<p>Los lock files siempre deben ser incluidos en el control de versiones. Un build que ignora los lock files es un build que no puede reproducir.<\/p>\n<h3>Vendoring de Dependencias<\/h3>\n<p>El vendoring va m\u00e1s all\u00e1 que los lock files al almacenar el c\u00f3digo fuente real de las dependencias en su repositorio. Esto elimina cualquier dependencia de registries externos en tiempo de build:<\/p>\n<pre><code># Go: vendor all dependencies\ngo mod vendor\n\n# Build using vendored dependencies\ngo build -mod=vendor .\/cmd\/server<\/code><\/pre>\n<p>El vendoring intercambia tama\u00f1o de repositorio por confiabilidad y reproducibilidad. Es especialmente valioso para builds que deben funcionar en entornos air-gapped o para proyectos donde la reproducibilidad a largo plazo es importante.<\/p>\n<h3>Compromisos Pr\u00e1cticos: Hermeticidad vs Experiencia del Desarrollador<\/h3>\n<p>La hermeticidad completa tiene un costo. Bazel tiene una curva de aprendizaje pronunciada. El vendoring aumenta el tama\u00f1o del repositorio. Pre-descargar cada dependencia requiere infraestructura. La clave es adoptar la hermeticidad de forma incremental: comience con lock files e im\u00e1genes fijadas, luego a\u00f1ada vendoring y sandboxing a medida que sus requisitos de seguridad lo exijan.<\/p>\n<h2>Fijando Todo<\/h2>\n<p>El pinning es la pr\u00e1ctica de especificar versiones exactas e inmutables para cada componente en su build. Los tags son mutables \u2014 pueden ser movidos para apuntar a contenido diferente. Los digests y commit SHAs son inmutables. Siempre fije a referencias inmutables.<\/p>\n<h3>Fijando Im\u00e1genes Base por Digest<\/h3>\n<p>Los tags de im\u00e1genes Docker como <code>node:20<\/code> o <code>python:3.12-slim<\/code> pueden cambiar en cualquier momento. El registry puede publicar una nueva imagen con el mismo tag. Fije por digest en su lugar:<\/p>\n<pre><code># BAD: tag can change at any time\nFROM node:20-alpine\n\n# GOOD: pinned to an immutable digest\nFROM node@sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2\n\n# BETTER: tag for readability, digest for immutability\nFROM node:20-alpine@sha256:a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2<\/code><\/pre>\n<p>Puede encontrar el digest actual con:<\/p>\n<pre><code>docker inspect --format='{{index .RepoDigests 0}}' node:20-alpine<\/code><\/pre>\n<h3>Fijando GitHub Actions por SHA<\/h3>\n<p>Los tags de GitHub Actions son mutables. Una action comprometida puede publicar c\u00f3digo malicioso en un tag existente. Siempre fije al SHA completo del commit:<\/p>\n<pre><code># BAD: tag can be moved to malicious commit\n- uses: actions\/checkout@v4\n\n# GOOD: pinned to immutable commit SHA\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1<\/code><\/pre>\n<p>Use herramientas como <a href=\"https:\/\/github.com\/sethvargo\/ratchet\" target=\"_blank\" rel=\"noopener\">ratchet<\/a> o <a href=\"https:\/\/github.com\/suzuki-shunsuke\/pinact\" target=\"_blank\" rel=\"noopener\">pinact<\/a> para automatizar el pinning de SHA en sus archivos de workflow y mantenga comentarios con el tag de versi\u00f3n para legibilidad.<\/p>\n<h3>Fijando Versiones de Toolchain<\/h3>\n<p>Su toolchain de build \u2014 compilador, runtime, package manager \u2014 debe tener versiones fijadas. Use archivos de configuraci\u00f3n de version managers para declarar versiones exactas:<\/p>\n<pre><code># .tool-versions (used by asdf version manager)\nnodejs 20.11.0\npython 3.12.1\ngolang 1.22.0\nrust 1.75.0<\/code><\/pre>\n<pre><code># .nvmrc (Node version manager)\n20.11.0<\/code><\/pre>\n<pre><code># rust-toolchain.toml\n[toolchain]\nchannel = \"1.75.0\"\ncomponents = [\"rustfmt\", \"clippy\"]\ntargets = [\"x86_64-unknown-linux-gnu\"]<\/code><\/pre>\n<h3>Usando Nix para Reproducibilidad del Entorno de Build<\/h3>\n<p>Nix proporciona el enfoque m\u00e1s riguroso para la reproducibilidad del entorno. Un Nix flake declara el entorno de build completo como una funci\u00f3n de sus inputs:<\/p>\n<pre><code># flake.nix\n{\n  inputs = {\n    nixpkgs.url = \"github:NixOS\/nixpkgs\/nixos-24.05\";\n    flake-utils.url = \"github:numtide\/flake-utils\";\n  };\n  outputs = { self, nixpkgs, flake-utils }:\n    flake-utils.lib.eachDefaultSystem (system:\n      let pkgs = nixpkgs.legacyPackages.${system}; in {\n        devShells.default = pkgs.mkShell {\n          buildInputs = [ pkgs.go_1_22 pkgs.nodejs_20 pkgs.protobuf ];\n        };\n      });\n}<\/code><\/pre>\n<p>Con Nix, cada desarrollador y cada runner de CI obtiene exactamente las mismas versiones de cada herramienta, hasta las bibliotecas del sistema. El archivo <code>flake.lock<\/code> fija todo el \u00e1rbol de dependencias a revisiones espec\u00edficas de Git.<\/p>\n<h2>Verificando la Integridad de Builds<\/h2>\n<p>Lograr la reproducibilidad es solo la mitad de la batalla. Tambi\u00e9n necesita verificarla \u2014 confirmar que builds independientes desde el mismo c\u00f3digo fuente realmente producen artefactos id\u00e9nticos.<\/p>\n<h3>Comparando Builds Entre Entornos<\/h3>\n<p>La verificaci\u00f3n m\u00e1s simple es construir el mismo artefacto en dos entornos diferentes y comparar los outputs:<\/p>\n<pre><code># Build in CI\nsha256sum build\/output\/myapp.tar.gz\n# Output: a1b2c3d4... build\/output\/myapp.tar.gz\n\n# Rebuild locally from the same commit\ngit checkout v1.2.3\nmake build\nsha256sum build\/output\/myapp.tar.gz\n# Output should match: a1b2c3d4...<\/code><\/pre>\n<p>Si los hashes coinciden, el build es reproducible. Si no coinciden, necesita investigar qu\u00e9 difiere.<\/p>\n<h3>Usando diffoscope para An\u00e1lisis Profundo<\/h3>\n<p><code>diffoscope<\/code> es una herramienta esencial para diagnosticar problemas de reproducibilidad. Desempaqueta archivos recursivamente, descompila binarios y le muestra exactamente d\u00f3nde dos builds difieren:<\/p>\n<pre><code># Install diffoscope\npip install diffoscope\n\n# Compare two builds\ndiffoscope build-1\/myapp.tar.gz build-2\/myapp.tar.gz --html report.html\n\n# Compare two container images\ndiffoscope image-1.tar image-2.tar --html report.html<\/code><\/pre>\n<p>El reporte HTML muestra diferencias en cada nivel: metadata de archivos, contenidos de archivos, secciones binarias y recursos embebidos. Esto es invaluable para identificar y eliminar fuentes de no determinismo una por una.<\/p>\n<h3>Almacenando Metadata de Build<\/h3>\n<p>Incluso antes de lograr reproducibilidad completa, registrar la metadata del build proporciona trazabilidad. Capture y almacene:<\/p>\n<ul>\n<li>SHA del commit de Git y branch<\/li>\n<li>Hashes de todas las dependencias de input<\/li>\n<li>Detalles del entorno de build (versi\u00f3n del OS, versi\u00f3n de la toolchain, variables de entorno)<\/li>\n<li>Hashes de todos los artefactos de output<\/li>\n<li>Timestamps y duraci\u00f3n del build<\/li>\n<\/ul>\n<h3>SLSA Provenance<\/h3>\n<p>SLSA provenance es un formato estandarizado para metadata de build. Registra qu\u00e9 se construy\u00f3, desde qu\u00e9 c\u00f3digo fuente, usando qu\u00e9 proceso de build y en qu\u00e9 entorno. Herramientas como <a href=\"https:\/\/github.com\/slsa-framework\/slsa-github-generator\" target=\"_blank\" rel=\"noopener\">slsa-github-generator<\/a> pueden generar autom\u00e1ticamente provenance firmado para sus builds de GitHub Actions:<\/p>\n<pre><code># .github\/workflows\/release.yml\njobs:\n  build:\n    outputs:\n      digest: ${{ steps.hash.outputs.digest }}\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11\n      - run: make build\n      - id: hash\n        run: echo \"digest=$(sha256sum myapp | cut -d' ' -f1)\" >> \"$GITHUB_OUTPUT\"\n\n  provenance:\n    needs: build\n    permissions:\n      actions: read\n      id-token: write\n      contents: write\n    uses: slsa-framework\/slsa-github-generator\/.github\/workflows\/generator_generic_slsa3.yml@v1.9.0\n    with:\n      base64-subjects: ${{ needs.build.outputs.digest }}<\/code><\/pre>\n<h3>Escaneo de Im\u00e1genes de Contenedores<\/h3>\n<p>Para im\u00e1genes de contenedores, verifique la integridad inspeccionando las capas de la imagen en busca de contenido inesperado:<\/p>\n<pre><code># List all layers in an image\ndocker history myapp:latest --no-trunc\n\n# Export and inspect image contents\ndocker save myapp:latest -o image.tar\ntar -tf image.tar\n\n# Use dive to inspect layer contents interactively\ndive myapp:latest<\/code><\/pre>\n<h2>Builds Reproducibles en CI\/CD<\/h2>\n<p>Las plataformas de CI\/CD introducen sus propios desaf\u00edos de reproducibilidad. Las im\u00e1genes de runners cambian, los caches expiran y los entornos de build son ef\u00edmeros. As\u00ed es como lograr reproducibilidad en las principales plataformas.<\/p>\n<h3>GitHub Actions<\/h3>\n<p>Un workflow reproducible de GitHub Actions fija cada componente externo:<\/p>\n<pre><code>name: Reproducible Build\non:\n  push:\n    branches: [main]\n\njobs:\n  build:\n    # Pin the runner image (or use a self-hosted runner with a known image)\n    runs-on: ubuntu-22.04\n\n    steps:\n      # Pin action by SHA\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1\n\n      # Pin setup action and toolchain version\n      - uses: actions\/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0\n        with:\n          go-version: '1.22.0' # Exact version, not '1.22.x'\n\n      # Cache with hash-based key for determinism\n      - uses: actions\/cache@ab5e6d0c87105b4c9c2047343972218f562e4319 # v4.0.1\n        with:\n          path: ~\/go\/pkg\/mod\n          key: go-mod-${{ hashFiles('go.sum') }}\n\n      # Build with reproducibility flags\n      - run: |\n          CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \\\n          go build -trimpath -ldflags='-s -w -buildid=' \\\n          -o myapp .\/cmd\/server\n\n      # Record artifact hash\n      - run: sha256sum myapp >> checksums.txt\n\n      - uses: actions\/upload-artifact@5d5d22a31266ced268874388b861e4b58bb5c2f3 # v4.3.1\n        with:\n          name: build-artifacts\n          path: |\n            myapp\n            checksums.txt<\/code><\/pre>\n<h3>GitLab CI<\/h3>\n<p>En GitLab CI, use im\u00e1genes Docker fijas para los runners y fije todas las dependencias:<\/p>\n<pre><code># .gitlab-ci.yml\nvariables:\n  # Use a specific image digest for the build environment\n  BUILD_IMAGE: golang@sha256:2c3f3c4a1f8e4c2b7d5e1a9f8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0b\n\nstages:\n  - build\n  - verify\n\nbuild:\n  stage: build\n  image: $BUILD_IMAGE\n  script:\n    - go mod download\n    - CGO_ENABLED=0 GOOS=linux GOARCH=amd64\n      go build -trimpath -ldflags='-s -w -buildid='\n      -o myapp .\/cmd\/server\n    - sha256sum myapp | tee checksums.txt\n  artifacts:\n    paths:\n      - myapp\n      - checksums.txt\n  cache:\n    key:\n      files:\n        - go.sum\n    paths:\n      - \/go\/pkg\/mod\n\nverify-reproducibility:\n  stage: verify\n  image: $BUILD_IMAGE\n  script:\n    - go mod download\n    - CGO_ENABLED=0 GOOS=linux GOARCH=amd64\n      go build -trimpath -ldflags='-s -w -buildid='\n      -o myapp-verify .\/cmd\/server\n    - sha256sum myapp-verify\n    - diff <(sha256sum myapp | cut -d' ' -f1) <(sha256sum myapp-verify | cut -d' ' -f1)<\/code><\/pre>\n<h3>Usando Nix en CI para Reproducibilidad Completa<\/h3>\n<p>Nix proporciona las garant\u00edas de reproducibilidad m\u00e1s fuertes en CI al especificar toda la clausura del build:<\/p>\n<pre><code># GitHub Actions with Nix\nname: Nix Build\non: push\n\njobs:\n  build:\n    runs-on: ubuntu-22.04\n    steps:\n      - uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11\n      - uses: cachix\/install-nix-action@ba0dd844c9180cbf77aa557a09b7b0d890fbd0fb # v26\n        with:\n          nix_path: nixpkgs=channel:nixos-24.05\n      - run: nix build .#myapp\n      - run: sha256sum result\/bin\/myapp<\/code><\/pre>\n<p>Con Nix, el archivo <code>flake.lock<\/code> fija la versi\u00f3n exacta de cada paquete en la clausura del build. Dos desarrolladores ejecutando <code>nix build<\/code> en diferentes m\u00e1quinas obtendr\u00e1n un output id\u00e9ntico, porque todo el grafo de dependencias \u2014 incluyendo el compilador de C, las bibliotecas del sistema y cada dependencia transitiva \u2014 est\u00e1 precisamente especificado.<\/p>\n<h3>Builds Reproducibles de Im\u00e1genes de Contenedores<\/h3>\n<p>Construir im\u00e1genes de contenedores reproducibles requiere cuidado especial. El <code>docker build<\/code> tradicional incrusta timestamps en cada capa. Herramientas como <a href=\"https:\/\/github.com\/GoogleContainerTools\/kaniko\" target=\"_blank\" rel=\"noopener\">kaniko<\/a>, <a href=\"https:\/\/github.com\/moby\/buildkit\" target=\"_blank\" rel=\"noopener\">BuildKit<\/a> y <a href=\"https:\/\/github.com\/google\/ko\" target=\"_blank\" rel=\"noopener\">ko<\/a> ofrecen mejor reproducibilidad:<\/p>\n<pre><code># Using BuildKit with reproducible output\nDOCKER_BUILDKIT=1 docker build \\\n  --build-arg SOURCE_DATE_EPOCH=$(git log -1 --format=%ct) \\\n  --output type=docker,rewrite-timestamp=true \\\n  -t myapp:latest .<\/code><\/pre>\n<p>La variable de entorno <code>SOURCE_DATE_EPOCH<\/code> es un <a href=\"https:\/\/reproducible-builds.org\/specs\/source-date-epoch\/\" target=\"_blank\" rel=\"noopener\">mecanismo estandarizado<\/a> para indicar a las herramientas de build que usen un timestamp fijo en lugar de la hora actual. Muchas herramientas lo respetan, incluyendo GCC, dpkg, tar y zip.<\/p>\n<h2>Cuando la Reproducibilidad Perfecta No Es Factible<\/h2>\n<p>La reproducibilidad bit a bit es el ideal, pero no siempre es alcanzable \u2014 y eso est\u00e1 bien. Lo que importa es entender d\u00f3nde se encuentra en el espectro de reproducibilidad y aplicar controles compensatorios donde sea necesario.<\/p>\n<h3>No Reproducibilidad Aceptable<\/h3>\n<p>Cierta no reproducibilidad es inofensiva. Un timestamp de build en un archivo de metadata que nunca se ejecuta no afecta la seguridad del artefacto. Una entrada de log registrando cu\u00e1ndo se ejecut\u00f3 el build no es un riesgo de seguridad. La distinci\u00f3n clave es si el elemento no reproducible est\u00e1 en el artefacto mismo (riesgoso) o solo en metadata junto a \u00e9l (aceptable).<\/p>\n<h3>Reproducibilidad \"Suficientemente Buena\"<\/h3>\n<p>Para muchos equipos, el objetivo pr\u00e1ctico es la reproducibilidad funcional: los mismos inputs producen el mismo output funcional. Los binarios pueden diferir en timestamps embebidos o s\u00edmbolos de debug, pero se comportan de forma id\u00e9ntica. Este nivel de reproducibilidad es alcanzable con herramientas y pr\u00e1cticas est\u00e1ndar:<\/p>\n<ul>\n<li>Fijar todas las versiones de dependencias con lock files<\/li>\n<li>Fijar im\u00e1genes base por digest<\/li>\n<li>Fijar CI actions por SHA<\/li>\n<li>Usar versiones fijas de toolchain<\/li>\n<li>Eliminar timestamps donde sea posible<\/li>\n<\/ul>\n<h3>Controles Compensatorios<\/h3>\n<p>Cuando la reproducibilidad completa no es factible, los controles compensatorios proporcionan garant\u00edas alternativas:<\/p>\n<ul>\n<li><strong>Firma de c\u00f3digo:<\/strong> Firme criptogr\u00e1ficamente sus artefactos para que los consumidores puedan verificar que provienen de su sistema de build.<\/li>\n<li><strong>SLSA provenance:<\/strong> Genere y publique metadata de provenance que registre los inputs del build, el entorno y el proceso.<\/li>\n<li><strong>Software Bill of Materials (SBOM):<\/strong> Publique una lista completa de componentes en su artefacto para que los consumidores sepan exactamente lo que est\u00e1n recibiendo.<\/li>\n<li><strong>Retenci\u00f3n de logs de build:<\/strong> Almacene logs de build completos para an\u00e1lisis forense si se sospecha un compromiso.<\/li>\n<li><strong>Builds multi-party:<\/strong> Haga que m\u00faltiples partes independientes construyan desde el mismo c\u00f3digo fuente y comparen resultados.<\/li>\n<\/ul>\n<h3>El Espectro de Reproducibilidad<\/h3>\n<p>Piense en la reproducibilidad como un espectro, no como un estado binario:<\/p>\n<ul>\n<li><strong>Nivel 0 \u2014 Nada:<\/strong> Sin pinning de versiones, sin lock files, los builds dependen de lo que sea m\u00e1s reciente. Aqu\u00ed es donde la mayor\u00eda de los proyectos comienzan.<\/li>\n<li><strong>Nivel 1 \u2014 Dependencias fijadas:<\/strong> Lock files en el repositorio, las dependencias son versiones fijas. Los builds son mayormente reproducibles.<\/li>\n<li><strong>Nivel 2 \u2014 Entorno fijado:<\/strong> Las versiones de toolchain e im\u00e1genes base est\u00e1n fijadas. El entorno de build est\u00e1 controlado.<\/li>\n<li><strong>Nivel 3 \u2014 Builds herm\u00e9ticos:<\/strong> Sin acceso a la red durante el build. Todos los inputs declarados expl\u00edcitamente. Fuertes garant\u00edas de reproducibilidad.<\/li>\n<li><strong>Nivel 4 \u2014 Reproducible bit a bit:<\/strong> Builds independientes producen artefactos id\u00e9nticos. Verificaci\u00f3n completa posible.<\/li>\n<\/ul>\n<p>Cada nivel se construye sobre el anterior. Pasar del Nivel 0 al Nivel 1 es frecuentemente la mejora de mayor impacto que puede hacer, y requiere un esfuerzo m\u00ednimo.<\/p>\n<h2>Conclusi\u00f3n<\/h2>\n<p>Los builds reproducibles son la base de la confianza en la cadena de suministro. Sin ellos, est\u00e1 confiando ciegamente en que su sistema de build no ha sido comprometido \u2014 una fe que los clientes de SolarWinds, los usuarios de XZ Utils y muchos otros han aprendido que est\u00e1 fuera de lugar.<\/p>\n<p>La buena noticia es que no necesita lograr la perfecci\u00f3n desde el primer d\u00eda. Comience con lo b\u00e1sico:<\/p>\n<ul>\n<li><strong>Haga commit de sus lock files<\/strong> y use <code>npm ci<\/code> en lugar de <code>npm install<\/code>.<\/li>\n<li><strong>Fije sus im\u00e1genes base por digest<\/strong> en cada Dockerfile.<\/li>\n<li><strong>Fije sus CI actions por SHA<\/strong> en cada archivo de workflow.<\/li>\n<li><strong>Fije las versiones de su toolchain<\/strong> con <code>.tool-versions<\/code>, <code>rust-toolchain.toml<\/code> o similar.<\/li>\n<\/ul>\n<p>Luego a\u00f1ada hermeticidad de forma incremental: haga vendoring de sus dependencias, use Nix o Bazel para aislamiento de builds, elimine timestamps de sus artefactos, y configure jobs de verificaci\u00f3n que reconstruyan y comparen.<\/p>\n<p>Cada paso en el espectro de reproducibilidad hace sus builds m\u00e1s confiables, su cadena de suministro m\u00e1s auditable y su software m\u00e1s seguro. En un mundo donde los sistemas de build son un vector de ataque primario, los builds reproducibles no son opcionales \u2014 son esenciales.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducci\u00f3n Si no puede reproducir un build, no puede verificarlo. Esta simple verdad se encuentra en el coraz\u00f3n de la seguridad de la cadena de suministro de software. La integridad de builds garantiza que lo que usted despliega es exactamente lo que pretend\u00eda construir \u2014 nada a\u00f1adido, nada modificado, nada manipulado entre el c\u00f3digo fuente &#8230; <a title=\"Integridad de Builds y Builds Reproducibles: Gu\u00eda Pr\u00e1ctica para CI\/CD\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/build-integrity-reproducible-builds-ci-cd\/\" aria-label=\"Leer m\u00e1s sobre Integridad de Builds y Builds Reproducibles: Gu\u00eda Pr\u00e1ctica para CI\/CD\">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-646","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\/646","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=646"}],"version-history":[{"count":1,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/646\/revisions"}],"predecessor-version":[{"id":658,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/646\/revisions\/658"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=646"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=646"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=646"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=646"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}