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
- Axios (npm, mayo 2025)
[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.
- LiteLLM (PyPI, abril 2025)
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.
- Trivy (typosquatting, marzo 2025)
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).
- SolarWinds (2020, aún relevante)
– 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 afectada | Riesgo concreto | Ejemplo 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. |
- 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:
- Verificación de membresía:
/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"); }
- Lista blanca de workflows:
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.
- Mitigación de
pull_request_target:
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-secy@cilium/ci-structurepuedan 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)
- Audita permisos de CI/CD:
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.
- Restringe workflows por membresía:
– 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
- Implementa CODEOWNERS:
.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)
- Migrar a permisos basados en roles:
# 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.
- Usar Cosign para firmar imágenes:
# 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
- Aislar builds de PRs:
ghcr.io/tuorg/pr-{pr_number}) y borra las imágenes después de 7 días.Acciones a largo plazo (>3 meses)
- Adoptar SLSA (Supply-chain Levels for Software Artifacts):
– 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
- Auditorías automáticas de CI/CD:
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
- CNCF: Securing CI/CD for an open source project – Controlling who runs what
- Pulumi: Analyzing GitHub Actions Security Risks
- Linux.com: Supply Chain Attacks on Open Source – 2025 Report
