GitHub Actions se ha convertido en una de las plataformas de CI/CD más ampliamente adoptadas. Su flexibilidad, su estrecha integración con los repositorios de GitHub y su rico ecosistema la hacen atractiva para equipos de todos los tamaños.
Al mismo tiempo, los GitHub Actions runners han surgido como una superficie de ataque crítica en los ataques modernos a la cadena de suministro de software.
Los runners ejecutan código no confiable, manejan secretos sensibles y a menudo operan con amplios permisos en repositorios, registros de artefactos y entornos cloud.
Este artículo explica cómo funcionan los GitHub Actions runners, por qué son frecuentemente atacados y cómo diseñar arquitecturas de runners que reduzcan significativamente el riesgo sin afectar los flujos de trabajo de los desarrolladores.
¿Qué es un GitHub Actions runner?
Un runner es el entorno de ejecución donde realmente se ejecutan los workflows de GitHub Actions. Cada job en un workflow se ejecuta en un runner.
Desde una perspectiva de seguridad, los runners son el componente más sensible de la arquitectura de GitHub Actions porque:
- Ejecutan código arbitrario desde los workflows
- Procesan contenido del repositorio y dependencias
- Acceden a secretos y tokens
- Producen artefactos de compilación
Si un runner se ve comprometido, el atacante podría:
- Exfiltrar secretos
- Modificar los resultados de compilación
- Persistir entre jobs o workflows
- Moverse lateralmente hacia otros sistemas
Tipos de GitHub Actions runners
Comprender la seguridad de los runners comienza por comprender los tipos de runners. GitHub Actions soporta múltiples modelos de runners, cada uno con diferentes características de confianza y riesgo.
GitHub-hosted runners
Los GitHub-hosted runners son gestionados por GitHub y proporcionados como máquinas virtuales efímeras. Cada job normalmente se ejecuta en una VM nueva que se destruye después de la ejecución.
Características de seguridad:
- Efímeros por defecto
- Fuerte aislamiento entre jobs
- Sin estado persistente entre ejecuciones
- Personalización limitada
Desde el punto de vista de la seguridad, los GitHub-hosted runners proporcionan una base sólida para cargas de trabajo no confiables como los pull requests.
Sin embargo, no están libres de riesgo. La exposición de secretos, el uso indebido de permisos y la lógica maliciosa en workflows aún pueden llevar a un compromiso.
Self-hosted runners
Los self-hosted runners se ejecutan en infraestructura gestionada por la organización: VMs, servidores físicos o contenedores.
Se utilizan frecuentemente para:
- Acceder a recursos internos
- Utilizar herramientas o entornos personalizados
- Optimizar rendimiento o costes
Desde una perspectiva de seguridad, los self-hosted runners introducen riesgos significativos:
- Persistencia entre jobs
- Estado compartido entre workflows
- Acceso de red a sistemas internos
- Mayor radio de impacto si se comprometen
Sin un aislamiento fuerte, un solo job comprometido puede afectar futuras compilaciones u otros repositorios.
Ephemeral self-hosted runners
Los ephemeral self-hosted runners combinan la flexibilidad de los self-hosted runners con los beneficios de seguridad de la efimeralidad.
Cada job se ejecuta en un runner recién aprovisionado que se destruye después de completarse.
Este modelo reduce significativamente:
- El riesgo de persistencia
- La contaminación entre jobs
- El compromiso prolongado
Desde el punto de vista de la seguridad, los ephemeral self-hosted runners son fuertemente preferidos sobre los runners persistentes.
Por qué los runners son un objetivo principal de ataque
Los runners se sitúan en la intersección de entradas no confiables y operaciones privilegiadas. Esto los convierte en objetivos atractivos para los atacantes.
Los runners ejecutan código no confiable
Los workflows pueden ejecutar código proveniente de:
- Pull requests
- Feature branches
- Dependencias
- Third-party actions
Cualquiera de estos puede ser influenciado por un atacante. Si código no confiable se ejecuta en un runner con secretos o permisos elevados, el compromiso es inmediato.
Los runners a menudo tienen acceso a secretos
Los secretos se exponen comúnmente a los runners a través de variables de entorno.
Si las condiciones del workflow están mal configuradas, los secretos pueden ser accesibles para:
- Pull requests de repositorios forked
- Contribuidores no confiables
- Third-party actions maliciosas
Una vez que un secreto se expone, a menudo es difícil detectarlo o contenerlo.
Los runners pueden influir en artefactos y releases
Los runners compilan y empaquetan artefactos de software.
Si un atacante modifica la salida de compilación, el artefacto resultante puede distribuirse con plena confianza en los sistemas posteriores.
Sin verificaciones de integridad de artefactos, puede no haber forma de detectar el compromiso.
Escenarios comunes de ataque relacionados con runners
El modelado de amenazas de los runners revela patrones de ataque recurrentes.
Escenario 1: Abuso de pull requests
Un atacante envía un pull request que modifica la lógica del workflow o introduce pasos de compilación maliciosos.
Si el workflow expone secretos o tokens privilegiados a las compilaciones de PR, el atacante puede exfiltrar credenciales.
Este ataque no requiere ninguna vulnerabilidad, solo una mala configuración de confianza.
Escenario 2: Compromiso de third-party actions
Los workflows utilizan frecuentemente third-party actions.
Si una action se ve comprometida en origen o se referencia sin fijarla a un commit específico, el atacante puede inyectar comportamiento malicioso en los workflows.
El runner ejecuta la action con los mismos permisos que el código de workflow propio.
Escenario 3: Compromiso de self-hosted runners persistentes
En self-hosted runners persistentes, un atacante puede:
- Instalar backdoors
- Modificar herramientas locales
- Envenenar cachés
- Persistir entre jobs
Los workflows futuros pueden ejecutar sin saberlo código controlado por el atacante.
Escenario 4: Movimiento lateral a través de la infraestructura de runners
Los self-hosted runners a menudo tienen acceso de red a sistemas internos o entornos cloud.
Un runner comprometido puede utilizarse como punto de pivote para atacar otros servicios internos.
Principios de arquitectura de seguridad para runners
Asegurar los GitHub Actions runners es un problema arquitectónico, no un problema de checklist.
La seguridad efectiva de los runners se basa en unos pocos principios fundamentales.
1. Tratar los runners como entornos de ejecución no confiables
Los runners ejecutan entradas no confiables por diseño.
Nunca deben ser implícitamente confiables, incluso si se ejecutan dentro de infraestructura interna.
Los secretos, el acceso de red y los privilegios deben ser limitados en consecuencia.
2. Preferir la efimeralidad sobre la limpieza
Intentar «limpiar» un runner comprometido no es fiable.
Destruir y recrear runners después de cada job proporciona una garantía de seguridad mucho más fuerte.
Los runners efímeros eliminan la persistencia y reducen significativamente el tiempo de permanencia del atacante.
3. Aplicar el principio de mínimo privilegio a nivel de job
Cada job debe recibir solo los permisos que necesita.
Esto incluye:
- Permisos del repositorio
- Alcances de tokens
- Identidades cloud
Evitar otorgar permisos de escritura globales o por defecto.
4. Separar cargas de trabajo confiables y no confiables
Las compilaciones de pull requests, las contribuciones externas y el código no confiable deben ejecutarse en entornos separados de los jobs confiables de release o deployment.
Esta separación puede aplicarse mediante:
- Diferentes pools de runners
- Diferentes workflows
- Diferentes conjuntos de permisos
5. Reducir la superficie de ataque de los runners
Minimizar lo que está disponible en los runners:
- Deshabilitar herramientas innecesarias
- Restringir el acceso de red saliente
- Limitar el acceso de escritura al sistema de archivos
- Evitar cachés de larga duración
Una superficie de ataque más pequeña limita las opciones del atacante.
Mejores prácticas para asegurar los GitHub Actions runners
Las siguientes prácticas abordan los riesgos más comunes de los runners sin cambiar fundamentalmente los flujos de trabajo de los desarrolladores.
Usar GitHub-hosted runners para código no confiable
Para pull requests y contribuciones externas, los GitHub-hosted runners proporcionan un fuerte aislamiento y efimeralidad automática.
Evitar exponer secretos a estos jobs a menos que sea estrictamente necesario.
Adoptar ephemeral self-hosted runners para cargas de trabajo sensibles
Si se requieren self-hosted runners, hacerlos efímeros.
Cada job debe:
- Aprovisionar un runner nuevo
- Ejecutar un solo job
- Ser destruido inmediatamente después
Este modelo reduce drásticamente el riesgo de persistencia.
Bloquear permisos de forma explícita
Utilizar el modelo de permisos granulares de GitHub.
Definir permisos a nivel de workflow o job en lugar de depender de los valores por defecto.
Revisar los cambios de permisos con la misma atención que los cambios de código.
Fijar y revisar third-party actions
Siempre fijar las actions a un commit SHA específico.
Evitar usar actions que:
- No tengan mantenimiento
- Carezcan de transparencia
- Ejecuten lógica inesperada
Tratar las actions como parte de tu cadena de suministro.
Proteger los secretos de forma agresiva
Evitar exponer secretos a workflows activados por:
- Repositorios forked
- Pull requests no confiables
Preferir credenciales de corta duración y acceso basado en identidad sobre secretos estáticos.
Validar las salidas de compilación
No asumir que los artefactos producidos por los runners son confiables.
Utilizar:
- Firma de artefactos
- Atestaciones de procedencia
- Puertas de verificación antes del deployment
Esto asegura que los runners comprometidos no puedan envenenar silenciosamente los releases.
Dónde encaja la seguridad de los runners en una estrategia más amplia de CI/CD
La seguridad de los runners es una parte de la seguridad del pipeline, pero no puede funcionar de forma aislada.
Debe combinarse con:
- Modelado de amenazas de CI/CD
- Límites de confianza del pipeline
- Aplicación de políticas
- Verificación de integridad de artefactos
Sin estos elementos, incluso los runners bien asegurados pueden producir resultados no confiables.
Conclusión
Los GitHub Actions runners son una poderosa primitiva de automatización, pero también representan un entorno de ejecución de alto riesgo.
Ejecutan código no confiable, manejan credenciales sensibles y producen artefactos en los que los sistemas posteriores confían.
Asegurar los runners requiere decisiones arquitectónicas: efimeralidad, aislamiento, mínimo privilegio y separación explícita de niveles de confianza.
Las organizaciones que tratan a los runners como entornos de ejecución desechables y no confiables, en lugar de infraestructura confiable, están mucho mejor posicionadas para defenderse contra los ataques modernos de CI/CD y cadena de suministro.