Skip to content
SP StackPractices
intermediate Por StackPractices

Seguridad CI/CD — Fortalece tus Pipelines y Previene Ataques de Supply Chain

Guía práctica para asegurar pipelines CI/CD: gestión de secretos, runners de mínimo privilegio, firma de artefactos, escaneo de dependencias y defensa contra ataques de supply chain.

Nota para desarrolladores hispanohablantes: Esta guía incluye ejemplos y convenciones de nomenclatura adaptadas a equipos que trabajan en español. Cuando existen diferencias significativas en terminología técnica entre el inglés y el español, se indican explícitamente para facilitar la comunicación en equipos multiculturales.

Overview

Los pipelines CI/CD son objetivos de alto valor para ataques. Tienen acceso a código fuente, secretos, entornos de producción y credenciales de despliegue. Un pipeline comprometido puede llevar a inyección de código, brechas de datos y ataques de supply chain que afectan a cada consumidor downstream.

Esta guía cubre técnicas prácticas para fortalecer tu infraestructura CI/CD desde el origen hasta el despliegue.

When to Use

  • Manejas pipelines CI/CD que despliegan a producción
  • Quieres reducir el radio de explosión de un sistema de build comprometido
  • Necesitas cumplir con estándares de seguridad (SOC 2, ISO 27001, FedRAMP)
  • Has experimentado o quieres prevenir ataques de supply chain
  • Estás migrando de runners auto-hospedados a CI/CD cloud-native

Core Concepts

ConceptoDescripción
Ataque de Supply ChainInyectar código malicioso vía dependencias o herramientas de build comprometidas
Runner de Mínimo PrivilegioAgentes de build con acceso mínimo a secretos e infraestructura
Firma de ArtefactosVerificar criptográficamente que artefactos construidos vinieron de un pipeline de confianza
Escaneo de DependenciasDetectar automáticamente vulnerabilidades conocidas en librerías
Pipeline as CodeDefiniciones de CI/CD versionadas que enforced políticas de seguridad
SBOM (Software Bill of Materials)Inventario de todos los componentes usados en una aplicación

Step-by-Step CI/CD Security Hardening

1. Securizar Configuración de Pipeline

Trata tus definiciones de pipeline como código de producción:

# Ejemplo: Hardening de GitHub Actions
name: Secure Build
on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

# Permisos mínimos por defecto
permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      # Fijar actions a hashes de commit específicos
      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2

      # Usar OIDC para autenticación cloud en lugar de secretos de larga duración
      - name: Authenticate to AWS
        uses: aws-actions/configure-aws-credentials@e3dd6a429a4c6c8c8f55e0e0b9e8e8e8e8e8e8e8  # v4.0.2
        with:
          role-to-assume: arn:aws:iam::123456789012:role/CICD-Build-Role
          aws-region: us-east-1

      # Verificar dependencias antes de instalar
      - name: Install dependencies
        run: |
          npm ci --ignore-scripts  # Omitir scripts post-instalación
          npm audit --audit-level=moderate

Checklist de seguridad para definiciones de pipeline:

  • Fijar todas las actions de terceros a SHA de commit, no a tags de versión
  • Usar bloques permissions: con scopes mínimos requeridos
  • Nunca ejecutar CI en pull_request_target de forks no confiables
  • Deshabilitar ejecución de scripts durante instalación de dependencias (--ignore-scripts)
  • Usar reglas de protección de branch para prevenir pushes directos a main

2. Gestión de Secretos

Los secretos en CI/CD son un vector de ataque común:

# MALO: Secretos hardcodeados en archivos de pipeline
# AWS_SECRET_ACCESS_KEY=AKIA...  # NUNCA HAGAS ESTO

# BUENO: Usar gestión nativa de secretos
# GitHub Actions: secretos almacenados en settings del repositorio
# GitLab CI: variables CI/CD con flag masked
# Azure DevOps: Variable groups con tipo secret

# MEJOR: Usar OIDC para eliminar secretos de larga duración por completo
# AWS: configure-aws-credentials con role-to-assume
# GCP: workload identity federation
# Azure: managed identity + federated credentials

Mejores prácticas de secretos:

  • Rotar secretos automáticamente (cada 30-90 días)
  • Usar tokens de corta duración (TTL 1 hora) donde sea posible
  • Scopear secretos a etapas específicas de job, no al pipeline global
  • Auditar logs de acceso a secretos regularmente
  • Nunca loggear secretos (la mayoría de CI systems los enmascaran automáticamente)

3. Hardening de Runners

Tus agentes de build son tan críticos como servidores de producción:

EstrategiaDescripciónImplementación
Runners efímerosVM fresca para cada buildGitHub-hosted, GitLab SaaS runners
Aislamiento de redRestringir egress de runnerVPC, subnets privadas, sin internet
Mínimo privilegioRoles IAM mínimosRoles separados por pipeline/proyecto
Imágenes inmutablesImágenes de runner pre-hardenedPacker, custom AMI/Golden Image
Sin secretos en discoCredenciales solo en memoriaMounts tmpfs, inyección de secretos
# Ejemplo: Checklist de hardening de runner auto-hospedado
# 1. Ejecutar en VPC aislado sin egress a internet
# 2. Usar pools de runner separados por equipo/aplicación
# 3. Deshabilitar acceso sudo/root para usuario runner
# 4. Montar /tmp como noexec, nodev, nosuid
# 5. Ejecutar builds de contenedor en podman/docker rootless
# 6. Limpiar workspace entre jobs (no reusar)
# 7. Escanear imágenes de runner semanalmente por CVEs

4. Seguridad de Dependencias y Artefactos

Tus dependencias son tu eslabón más débil:

# Ejemplo: Pipeline de escaneo de dependencias en GitHub Actions
jobs:
  security-scan:
    runs-on: ubuntu-latest
    permissions:
      security-events: write
    steps:
      - uses: actions/checkout@v4

      # SAST (Static Application Security Testing)
      - name: Run CodeQL
        uses: github/codeql-action/init@v3
        with:
          languages: javascript, python
      - uses: github/codeql-action/analyze@v3

      # Escaneo de vulnerabilidades en dependencias
      - name: Run Dependabot (habilitado en settings del repo)
      # O usar Snyk/OWASP Dependency-Check

      # Escanear imagen de contenedor
      - name: Build image
        run: docker build -t myapp:${{ github.sha }} .
      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'myapp:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'

Prácticas de seguridad de artefactos:

  • Firmar todos los artefactos (cosign, Notary, Sigstore)
  • Generar y publicar SBOMs para cada release
  • Escanear imágenes de contenedor antes de push al registry
  • Verificar firmas de artefactos antes de despliegue
  • Almacenar artefactos en registries inmutables con políticas de retención

5. Seguridad de Despliegue

El paso final debe ser tan seguro como el primero:

# Ejemplo: Script de despliegue seguro
#!/bin/bash
set -euo pipefail

# 1. Verificar firma de artefacto
cosign verify \
  --key cosign.pub \
  --signature artifact.sig \
  myapp:${DEPLOY_VERSION}

# 2. Verificar que SBOM coincide con componentes esperados
sbom-diff expected.sbom generated.sbom

# 3. Ejecutar smoke tests antes de shift de tráfico
./smoke-tests.sh --target staging

# 4. Desplegar con rollback automático en falla
./deploy.sh --version ${DEPLOY_VERSION} --rollback-on-failure

# 5. Verificar salud del despliegue
./health-check.sh --target production

Reglas de seguridad de despliegue:

  • Requerir aprobación manual para despliegues a producción
  • Implementar rollback automatizado en falla de health check
  • Usar blue-green o canary deployments para limitar radio de explosión
  • Loggear todos los eventos de despliegue a traza de auditoría inmutable
  • Separar credenciales de despliegue de staging y producción

Best Practices

  • Asume que tu pipeline será comprometida. Diseña para contención, no solo prevención.
  • Usa infraestructura efímera. Runners frescos previenen malware persistente.
  • Verifica todo. Firmas, SBOMs y checksums deben ser mandatorios.
  • Minimiza permisos de pipeline. Si un job solo necesita acceso de lectura, enforzalo.
  • Monitorea comportamiento de pipeline. Alerta en conexiones outbound inesperadas o uso de credenciales.
  • Practica respuesta a incidentes. Ten un plan para rotar todos los secretos después de un compromiso.

Common Mistakes

  • Usar secretos de larga duración en CI. Migrar a OIDC o tokens de corta duración.
  • Ejecutar CI en runners auto-hospedados sin hardening. Frecuentemente tienen acceso de red más amplio que producción.
  • Confiar en workflows pull_request_target. Estos ejecutan con tokens de escritura en código no confiable.
  • No escanear dependencias. CVEs conocidas en dependencias son el vector de ataque más común.
  • Ignorar CVEs en imágenes base de contenedor. Empieza con imágenes base mínimas y hardened.

Variants

  • Seguridad en GitHub Actions: Enfocarse en permissions, pull_request vs pull_request_target, OIDC, Dependabot
  • Seguridad en GitLab CI: Variables CI/CD, permisos de jobs, tags de runner, escaneo de contenedores
  • Seguridad en Jenkins: Aislamiento de agentes, scopes de credenciales, shared libraries de pipeline
  • Cloud-native: Usar servicios de build administrados (AWS CodeBuild, Google Cloud Build) con integración IAM

FAQ

Q: ¿Cómo migro de secretos de larga duración a OIDC? Configura workload identity federation en tu proveedor cloud, luego actualiza configure-aws-credentials (o equivalente) para usar role-to-assume sin access keys.

Q: ¿Debería usar runners auto-hospedados o cloud-hosted? Los runners cloud-hosted son efímeros y aislados por defecto. Los runners auto-hospedados requieren hardening pero ofrecen más control y builds más rápidos con caching.

Q: ¿Cómo prevengo ataques de dependency confusion? Usa registries privadas con reserva de namespace, verifica firmas de paquetes, y fija versiones exactas con lock files.

Q: ¿Cuál es la configuración mínima viable de seguridad CI/CD? Habilitar Dependabot, usar OIDC para auth cloud, fijar versiones de actions, habilitar protección de branch, y escanear contenedores antes de despliegue.

Conclusion

La seguridad CI/CD es un proceso continuo, no una tarea de hardening de una sola vez. Cada componente — desde la imagen del runner hasta el script de despliegue — es una superficie de ataque potencial. Aplica defensa en profundidad, verifica cada artefacto, y asume que el compromiso ocurrirá.