Introducción

El último año demostró que la cadena de suministro del software open source sigue siendo un blanco atractivo. En mayo de 2025, el paquete axios en npm fue comprometido y distribuyó un RAT (Remote Access Trojan) camuflado en releases legítimas. Semanas antes, el paquete LiteLLM en PyPI fue hijackeado para exfiltrar variables de entorno. Estos incidentes no son aislados: en 2020, el ataque a SolarWinds demostró cómo un compromiso en el sistema de build puede escalar a 18.000 organizaciones, incluyendo agencias federales de EE.UU. y la OTAN. El malware permaneció 8 meses sin ser detectado.

Para proyectos que operan infraestructura crítica —como Cilium, que se ejecuta en el kernel de millones de pods de Kubernetes— el riesgo de un compromiso en CI/CD no es teórico. Un ataque exitoso podría propagarse desde un pipeline de build hasta la imagen final, afectando clusters enteros. La pregunta clave es: ¿quién puede ejecutar qué en tu pipeline y con qué permisos?

En este artículo, desglosamos las estrategias que equipos como Cilium implementan para responder esa pregunta, con ejemplos concretos de GitHub Actions, Kubernetes (EKS) y Docker, y cómo mitigar vectores como pull_request_target.

Qué ocurrió

Incidentes recientes que expusieron fallas en CI/CD

  1. Axios (npm, mayo 2025)
Vulnerabilidad: Un atacante comprometió la cuenta de un mantenedor y publicó una versión maliciosa de [email protected].

Impacto: El RAT se ejecutaba al importar el paquete, permitiendo exfiltración de datos y ejecución de código arbitrario en entornos de desarrollo.

Vector: Abuso de permisos de publicación en npm. La versión maliciosa tenía 1.2M de descargas antes de ser removida.

  1. LiteLLM (PyPI, abril 2025)
Vulnerabilidad: El paquete fue modificado para ejecutar os.environ y enviar los valores a un servidor remoto.

Impacto: Variables de entorno con credenciales de CI/CD y servicios cloud fueron comprometidas. El ataque duró 36 horas antes de ser detectado.

  1. Trivy (typosquatting, marzo 2025)
Vulnerabilidad: Se publicaron forks con nombres similares (ej: trivy-scanner en lugar de aquasecurity/trivy) que incluían miners de criptomonedas.

Impacto: 1.8K descargas en 48 horas. El ataque explotaba errores tipográficos en comandos de instalación (go install vs go get).

  1. SolarWinds (2020, aún relevante)
Vulnerabilidad: Ataque a la cadena de suministro upstream. Los atacantes modificaron el código fuente de Orion, que luego fue distribuido como actualización legítima.

Impacto: 18.000 organizaciones afectadas, incluyendo Microsoft y agencias gubernamentales. El malware (SUNBURST) permaneció 225 días sin ser detectado.

Estos casos comparten un patrón: el compromiso ocurrió en un eslabón antes de la distribución final, ya sea en el sistema de build, en la gestión de dependencias o en los permisos de publicación. Para proyectos que usan Kubernetes o Docker, el blast radius puede incluir miles de pods o imágenes comprometidas distribuidas a través de registries como ECR o Docker Hub.

Impacto para DevOps / Infraestructura / Cloud / Seguridad

Riesgos operativos y técnicos

Área afectadaRiesgo concretoEjemplo de impacto
**Seguridad**Ejecución de código malicioso en pipelines con permisos elevados.Un atacante ejecuta BLOCK23 desde CI y obtiene acceso a pods con secrets.
**Infraestructura**Abuso de minutos de CI/CD en entornos cloud.**GitHub Actions** cobra por minuto usado; un atacante podría agotar el presupuesto.
**Cloud**Imágenes Docker con backdoors distribuidas a través de registries públicos.Una imagen en **ECR** con un *cryptominer* se despliega en **EKS**.
**SRE**Falta de trazabilidad en pipelines.No se puede auditar quién ejecutó qué workflow en los últimos 6 meses.
### Datos clave del riesgo
  • Según CNCF, el 68% de los ataques a la cadena de suministro en 2025 explotaron fallas en permisos de CI/CD (fuente: CNCF Blog 2026).
  • En GitHub Actions, el 42% de los workflows mal configurados permiten ejecución arbitraria desde PRs no verificados (análisis de Pulumi sobre 2.3K repositorios públicos).
  • Un pull_request_target mal configurado puede escalar privilegios si el workflow ejecuta scripts desde el branch del PR. Ejemplo:
  # MAL: Ejecuta código del PR sin restricciones
  - uses: actions/checkout@v4
  - run: ./build.sh  # script del PR puede ser malicioso
  

Para equipos que operan Kubernetes, el riesgo se amplifica: un pipeline comprometido puede generar imágenes con CVE-2024-3094 (ej: backdoor en liblzma) que luego se despliegan en clusters.

Detalles técnicos

1. Control de quién puede disparar workflows: el caso de Ariane (GitHub Actions)

Cilium implementa Ariane, un bot escrito en Rust que filtra quién puede ejecutar workflows en GitHub Actions. Los pasos clave son:

  1. Verificación de membresía:
– Solo miembros de la organización pueden comentar /test o /ci-eks en PRs.

– La verificación se hace contra la API de GitHub:

     // Pseudocódigo de Ariane
     let commenter = event.comment.user.login;
     let is_member = github_api.is_org_member(commenter, "cilium");
     if !is_member { return Err("No autorizado"); }
     
  1. Lista blanca de workflows:
– Solo se permiten disparar workflows enumerados en una lista estática (allowed_workflows.yaml):
     # allowed_workflows.yaml
     allowed_workflows:
       - ci-test-k8s
       - ci-build-image
       - conformance-eks
     

– Si un usuario intenta ejecutar un workflow no listado (ej: ci-deploy-prod), el bot lo ignora.

  1. Mitigación de pull_request_target:
– Cilium evita pull_request_target donde sea posible. Cuando es necesario (ej: para construir imágenes Docker), el workflow se divide en dos pasos:
     # workflow seguro: sin pull_request_target
     jobs:
       build-image:
         steps:
           - name: Checkout base branch (revisado)
             uses: actions/checkout@v4
             with:
               ref: ${{ github.base_ref }}  # branch principal, revisado
               path: ./base

           - name: Checkout PR branch (solo para build)
             uses: actions/checkout@v4
             with:
               ref: ${{ github.head_ref }}
               path: ./pr

           - name: Build con contexto seguro
             run: |
               docker build -t ghcr.io/cilium/image:pr-${{ github.event.pull_request.number }} ./pr
               # El PR no ejecuta scripts, solo provee contexto de build
     

Crítica: Herramientas como CodeQL o Snyk pueden marcar este patrón como «vulnerable», pero en este contexto está diseñado para limitar el blast radius.

2. CODEOWNERS y aislamiento de cambios en CI

Para evitar que un atacante modifique la configuración de CI/CD, Cilium usa:

  • CODEOWNERS para que solo @cilium/github-sec y @cilium/ci-structure puedan modificar archivos en .github/:
  # .github/CODEOWNERS
  /workflows/          @cilium/github-sec @cilium/ci-structure
  /auto-approve.yaml   @cilium/cilium-maintainers
  
  • Revisión obligatoria: Cualquier cambio en CI debe ser aprobado por al menos 2 miembros del equipo de seguridad.

3. Dependencias y aislamiento en pipelines

Aunque este artículo se centra en permisos, es relevante mencionar que Cilium también:

  • Pinea dependencias en Go (go mod vendor) para evitar cambios maliciosos en módulos.
  • SHA-pinea acciones en workflows:
  # Ejemplo seguro: SHA fija
  - uses: actions/checkout@8e5e7e5ab8b3aa1fe3f6206d7c304643c4a3deb  # v4.1.1
  
  • Usa Cosign para firmar imágenes:
  cosign sign --key cosign.key ghcr.io/cilium/image:pr-${PR_NUMBER}
  

Qué deberían hacer los administradores y equipos técnicos

Acciones inmediatas (sin cambios en infraestructura)

  1. Audita permisos de CI/CD:
– En GitHub Actions, revisa quién tiene permisos para ejecutar workflows:
     gh api repos/{owner}/{repo}/actions/permissions --jq '.allowed_actions'
     

Deshabilita pull_request_target donde no sea estrictamente necesario. Si lo usas, implementa el patrón de dos checkouts como en Cilium.

  1. Restringe workflows por membresía:
– Crea un bot simple (en Python, Go o TypeScript) que valide:

– Que el comentario provenga de un miembro de la organización.

– Que el workflow esté en una lista blanca.

– Ejemplo mínimo en GitHub Actions:

     # .github/workflows/ci-check.yml
     on:
       issue_comment:
         types: [created]

     jobs:
       validate:
         runs-on: ubuntu-latest
         steps:
           - name: Verificar membresía
             run: |
               if ! gh api orgs/{org}/members/{commenter} >/dev/null; then
                 echo "No autorizado"
                 exit 1
               fi
     
  1. Implementa CODEOWNERS:
– Crea un archivo .github/CODEOWNERS y asigna a un equipo de seguridad (ej: @tuorg/security-ci) como dueño de .github/**.

Bloquea cambios en workflows sin revisión de ese equipo.

Acciones a mediano plazo (1-3 meses)

  1. Migrar a permisos basados en roles:
– En Kubernetes (EKS), usa IRSA (IAM Roles for Service Accounts) para que los pods de CI tengan permisos mínimos:
     # serviceaccount.yaml
     apiVersion: v1
     kind: ServiceAccount
     metadata:
       name: ci-runner
       annotations:
         eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/ci-runner-role
     

Restringe permisos con OPA (Open Policy Agent) o Kyverno.

  1. Usar Cosign para firmar imágenes:
– Firma todas las imágenes generadas en CI con Cosign y verifica en el despliegue:
     # En el pipeline
     cosign sign --yes ghcr.io/tuorg/image:${GITHUB_SHA}

     # En el despliegue (Kubernetes)
     cosign verify ghcr.io/tuorg/image:${GITHUB_SHA} --key cosign.pub
     
  1. Aislar builds de PRs:
– Si necesitas construir imágenes desde PRs, usa un registry temporal (ej: ghcr.io/tuorg/pr-{pr_number}) y borra las imágenes después de 7 días.

Acciones a largo plazo (>3 meses)

  1. Adoptar SLSA (Supply-chain Levels for Software Artifacts):
– Implementa SLSA Level 3 con:

Builds reproducibles (usando Bazel o Earthly).

Provenance con Sigstore (SLSA Level 3 requiere provenance firmada).

– Ejemplo con Earthly:

     FROM alpine
     DO +build
     SAVE ARTIFACT /out/binary AS LOCAL build/binary
     
  1. Auditorías automáticas de CI/CD:
– Usa Checkov o Terrascan para escanear workflows de GitHub Actions:
     checkov -f .github/workflows/
     

Conclusión

Los incidentes de Axios, LiteLLM y SolarWinds demostraron que un compromiso en CI/CD puede escalar rápidamente, especialmente en proyectos open source con miles de usuarios. Las estrategias de control de acceso, CODEOWNERS y aislamiento de builds que implementan equipos como Cilium no son magia: son patrones repetibles que cualquier equipo puede adoptar hoy.

El primer paso es eliminar permisos implícitos. Si un workflow puede ser ejecutado por cualquier usuario con acceso a un repo, estás un paso más cerca de un SolarWinds 2.0. La segunda capa es aislar el código que se ejecuta: que los PRs no ejecuten scripts, que las imágenes se firmen, y que los permisos en Kubernetes sean mínimos.

Este artículo es la primera parte de una serie. En Parte 2, cubriremos cómo endurecer dependencias (SHA-pinning, vendoring, actualizaciones automáticas). En Parte 3, abordaremos aislamiento de credenciales, verificación de releases y los gaps que aún quedan por cerrar.

Fuentes

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *