{"id":620,"date":"2026-01-30T22:17:45","date_gmt":"2026-01-30T21:17:45","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=620"},"modified":"2026-03-24T18:45:30","modified_gmt":"2026-03-24T17:45:30","slug":"ci-cd-execution-models-trust-assumptions-security-guide","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/ci-cd-execution-models-trust-assumptions-security-guide\/","title":{"rendered":"Modelos de Ejecuci\u00f3n CI\/CD y Supuestos de Confianza: Gu\u00eda de Seguridad"},"content":{"rendered":"<h2>Introducci\u00f3n<\/h2>\n<p>Los pipelines CI\/CD se encuentran entre los componentes m\u00e1s privilegiados de cualquier organizaci\u00f3n de software moderna. Clonan c\u00f3digo fuente, acceden a secrets, construyen artefactos y despliegan en producci\u00f3n \u2014 frecuentemente con m\u00ednima supervisi\u00f3n humana. Sin embargo, a pesar de este extraordinario nivel de acceso, los modelos de confianza que sustentan estos pipelines rara vez se hacen expl\u00edcitos.<\/p>\n<p>Cuando un pipeline se ejecuta, responde impl\u00edcitamente una cadena de preguntas de seguridad: \u00bfQui\u00e9n activ\u00f3 esta ejecuci\u00f3n? \u00bfQu\u00e9 c\u00f3digo se est\u00e1 ejecutando? \u00bfQu\u00e9 identidad asume el pipeline? \u00bfA qu\u00e9 recursos puede acceder? En la mayor\u00eda de las organizaciones, estas preguntas son respondidas por configuraciones predeterminadas en lugar de decisiones de seguridad deliberadas.<\/p>\n<p>Esta gu\u00eda mapea c\u00f3mo funcionan los diferentes modelos de ejecuci\u00f3n CI\/CD, d\u00f3nde se asume la confianza frente a d\u00f3nde se verifica, y c\u00f3mo fortalecer sus pipelines contra los patrones de ataque del mundo real que explotan estas brechas. Ya sea que utilice GitHub Actions, GitLab CI u otra plataforma, las din\u00e1micas de confianza subyacentes son universales \u2014 y comprenderlas es esencial para proteger su cadena de suministro de software.<\/p>\n<h2>\u00bfQu\u00e9 es un Modelo de Ejecuci\u00f3n CI\/CD?<\/h2>\n<p>Un modelo de ejecuci\u00f3n CI\/CD define el ciclo de vida completo de c\u00f3mo se activa el c\u00f3digo del pipeline, d\u00f3nde se ejecuta f\u00edsicamente, qu\u00e9 identidad asume durante la ejecuci\u00f3n y a qu\u00e9 recursos puede acceder. Es, en esencia, la arquitectura de seguridad de su capa de automatizaci\u00f3n.<\/p>\n<p>Todo modelo de ejecuci\u00f3n debe responder cuatro preguntas fundamentales:<\/p>\n<ul>\n<li><strong>Trigger:<\/strong> \u00bfQu\u00e9 evento inicia el pipeline y qui\u00e9n o qu\u00e9 est\u00e1 autorizado para causar ese evento?<\/li>\n<li><strong>Entorno:<\/strong> \u00bfD\u00f3nde se ejecuta el c\u00f3digo del pipeline \u2014 en qu\u00e9 infraestructura, con qu\u00e9 sistema operativo y con qu\u00e9 grado de aislamiento?<\/li>\n<li><strong>Identidad:<\/strong> \u00bfQu\u00e9 credenciales, tokens o cuentas de servicio posee el pipeline en ejecuci\u00f3n?<\/li>\n<li><strong>Acceso:<\/strong> \u00bfA qu\u00e9 sistemas downstream, secrets, registries y objetivos de despliegue puede llegar el pipeline?<\/li>\n<\/ul>\n<p>La forma en que se responden estas preguntas var\u00eda dr\u00e1sticamente entre los entornos de ejecuci\u00f3n:<\/p>\n<h3>Runners Alojados en SaaS<\/h3>\n<p>Plataformas como GitHub Actions (runners alojados por GitHub) y los runners compartidos de GitLab.com proporcionan m\u00e1quinas virtuales ef\u00edmeras gestionadas por el proveedor de CI\/CD. Cada job normalmente obtiene una VM nueva que se destruye despu\u00e9s de la ejecuci\u00f3n. La plataforma gestiona los parches, el aislamiento y el ciclo de vida. La contrapartida es que usted conf\u00eda en las garant\u00edas de aislamiento del proveedor \u2014 no puede inspeccionar ni controlar la infraestructura subyacente.<\/p>\n<h3>Self-Hosted Runners<\/h3>\n<p>Las organizaciones despliegan sus propios agentes runner en infraestructura que controlan \u2014 VMs, bare metal o pods de Kubernetes. Esto proporciona control total sobre el entorno de ejecuci\u00f3n, pero traslada la responsabilidad del aislamiento, los parches y la gesti\u00f3n de credenciales enteramente al operador. Un self-hosted runner mal configurado es uno de los vectores m\u00e1s comunes para el movimiento lateral en ataques CI\/CD.<\/p>\n<h3>Ejecuci\u00f3n Containerizada<\/h3>\n<p>Muchos pipelines ejecutan jobs dentro de contenedores, ya sea en infraestructura self-hosted o en cl\u00fasteres de Kubernetes gestionados. La ejecuci\u00f3n basada en contenedores proporciona aislamiento a nivel de proceso y entornos reproducibles, pero los contenedores no son fronteras de seguridad de la misma manera que las VMs. El acceso compartido al kernel, los vol\u00famenes montados y la exposici\u00f3n del Docker socket pueden socavar el modelo de aislamiento.<\/p>\n<h3>Ejecuci\u00f3n Serverless y Bajo Demanda<\/h3>\n<p>Algunos sistemas CI\/CD modernos (como AWS CodeBuild o ciertas configuraciones de Buildkite) levantan c\u00f3mputo completamente bajo demanda para cada job. Estos modelos ofrecen fuertes garant\u00edas de aislamiento ya que cada ejecuci\u00f3n obtiene una instancia de c\u00f3mputo dedicada y de corta duraci\u00f3n, pero introducen complejidad en torno al bootstrapping de credenciales y el control de acceso a la red.<\/p>\n<p>Comprender qu\u00e9 modelo utiliza su organizaci\u00f3n \u2014 y las propiedades de seguridad que proporciona y las que no \u2014 es la base para razonar sobre la confianza en CI\/CD.<\/p>\n<h2>Fronteras de Confianza en CI\/CD<\/h2>\n<p>Una frontera de confianza existe donde el control pasa de una entidad o sistema a otro. En CI\/CD, hay varias fronteras de confianza cr\u00edticas, y las fallas en cualquiera de ellas pueden llevar a un compromiso total del pipeline.<\/p>\n<h3>Del Repositorio de C\u00f3digo Fuente al Trigger del Pipeline<\/h3>\n<p>La primera frontera de confianza est\u00e1 entre el repositorio de c\u00f3digo y el mecanismo de trigger del pipeline. Cuando un desarrollador hace push de un commit o abre un pull request, la plataforma CI\/CD decide si ejecutar un pipeline y c\u00f3mo hacerlo. La pregunta cr\u00edtica es: <strong>\u00bfqui\u00e9n puede activar la ejecuci\u00f3n del pipeline y puede controlar qu\u00e9 c\u00f3digo ejecuta el pipeline?<\/strong><\/p>\n<p>En muchas configuraciones, cualquier persona que pueda abrir un pull request \u2014 incluyendo contribuidores externos a repositorios p\u00fablicos \u2014 puede activar la ejecuci\u00f3n del pipeline. Si la definici\u00f3n del pipeline proviene de la rama del PR, el contribuidor efectivamente controla el c\u00f3digo que se ejecuta en su entorno CI.<\/p>\n<h3>De la Definici\u00f3n del Pipeline al Entorno de Ejecuci\u00f3n<\/h3>\n<p>La segunda frontera de confianza separa la definici\u00f3n del pipeline (el archivo YAML, el Jenkinsfile, el script de build) del entorno donde se ejecuta. Las preguntas clave incluyen: \u00bfTiene el runner acceso a la red? \u00bfPuede el pipeline instalar software arbitrario? \u00bfPuede modificar el propio runner para futuros jobs?<\/p>\n<p>En runners compartidos o persistentes, una definici\u00f3n de pipeline maliciosa podr\u00eda instalar un backdoor que persiste a trav\u00e9s de ejecuciones de jobs posteriores \u2014 afectando repositorios y equipos completamente diferentes.<\/p>\n<h3>Del Entorno de Ejecuci\u00f3n a Secrets y Credenciales<\/h3>\n<p>Los pipelines necesitan credenciales para realizar trabajo \u00fatil: tokens de API, claves de proveedores cloud, contrase\u00f1as de registries, claves de firma. La frontera de confianza entre el entorno de ejecuci\u00f3n y el almac\u00e9n de secrets determina a qu\u00e9 puede acceder un pipeline comprometido. El acceso excesivamente amplio a secrets es una de las configuraciones err\u00f3neas m\u00e1s comunes y peligrosas en CI\/CD.<\/p>\n<h3>De la Salida del Build al Objetivo de Despliegue<\/h3>\n<p>La frontera de confianza final est\u00e1 entre lo que el pipeline produce (una imagen de contenedor, un binario, un plan de Terraform) y el sistema donde se despliega esa salida. Si la identidad del pipeline que construye un artefacto es la misma que lo despliega en producci\u00f3n, no hay separaci\u00f3n de funciones. Un solo paso de build comprometido puede llevar directamente a un compromiso de producci\u00f3n.<\/p>\n<h3>Mapeando las Zonas de Confianza<\/h3>\n<p>Conceptualmente, un pipeline CI\/CD atraviesa cuatro zonas de confianza:<\/p>\n<pre><code>Zona 1: Control de C\u00f3digo Fuente (Estaciones de trabajo de desarrolladores, ramas, PRs)\n   \u2193 [Frontera de trigger]\nZona 2: Definici\u00f3n del Pipeline (YAML\/config parseado por la plataforma CI)\n   \u2193 [Frontera de ejecuci\u00f3n]\nZona 3: Entorno de Ejecuci\u00f3n (Runner, contenedor, VM \u2014 con secrets)\n   \u2193 [Frontera de despliegue]\nZona 4: Objetivos de Despliegue (Producci\u00f3n, staging, registries, APIs cloud)\n<\/code><\/pre>\n<p>Cada flecha representa una frontera de confianza. Los controles de seguridad deben existir en cada transici\u00f3n: reglas de protecci\u00f3n de ramas en la frontera de trigger, aislamiento del runner en la frontera de ejecuci\u00f3n, credenciales con alcance limitado en la frontera de secrets, y aprobaciones de despliegue en la frontera de despliegue.<\/p>\n<h2>Modelo de Ejecuci\u00f3n de GitHub Actions<\/h2>\n<p>GitHub Actions es una de las plataformas CI\/CD m\u00e1s ampliamente adoptadas, y su modelo de ejecuci\u00f3n tiene varias caracter\u00edsticas de confianza \u00fanicas que vale la pena comprender en profundidad.<\/p>\n<h3>Runners Alojados por GitHub vs Self-Hosted<\/h3>\n<p>Los runners alojados por GitHub son VMs ef\u00edmeras aprovisionadas por GitHub para cada job. Se ejecutan en infraestructura de Azure, se destruyen despu\u00e9s de que cada job se completa y proporcionan un fuerte aislamiento entre jobs. Los self-hosted runners, por el contrario, son m\u00e1quinas que usted registra con GitHub. Persisten entre jobs, pueden acumular estado y \u2014 cr\u00edticamente \u2014 cualquier repositorio en la organizaci\u00f3n con acceso al runner puede ejecutar c\u00f3digo en \u00e9l.<\/p>\n<p>Para los self-hosted runners, GitHub advierte expl\u00edcitamente: <strong>no utilice self-hosted runners con repositorios p\u00fablicos.<\/strong> Cualquier fork puede enviar un pull request que active un workflow, y ese workflow se ejecuta en su infraestructura con su acceso a la red.<\/p>\n<h3>Permisos y Alcance del GITHUB_TOKEN<\/h3>\n<p>Cada ejecuci\u00f3n de workflow recibe un <code>GITHUB_TOKEN<\/code> autom\u00e1tico con permisos limitados al repositorio. Por defecto, este token tiene permisos amplios de lectura\/escritura sobre el contenido del repositorio, paquetes, issues y m\u00e1s. La clave <code>permissions<\/code> le permite restringir este token solo a lo necesario:<\/p>\n<pre><code>permissions:\n  contents: read\n  packages: write\n  id-token: write   # Para federaci\u00f3n OIDC\n<\/code><\/pre>\n<p>Establecer los permisos de nivel superior en <code>read-all<\/code> o incluso vac\u00edo (<code>{}<\/code>) y luego otorgar permisos espec\u00edficos por job es un paso de hardening cr\u00edtico. Sin esto, cualquier paso comprometido en cualquier job tiene acceso de escritura a su repositorio.<\/p>\n<h3>Workflows de Fork PRs: pull_request vs pull_request_target<\/h3>\n<p>Esta es una de las fronteras de confianza m\u00e1s peligrosas en GitHub Actions. El evento <code>pull_request<\/code> ejecuta la definici\u00f3n del workflow desde la rama head del PR \u2014 lo que significa que el contribuidor controla el c\u00f3digo del workflow \u2014 pero, de manera cr\u00edtica, <strong>no<\/strong> tiene acceso a los secrets del repositorio. El evento <code>pull_request_target<\/code> ejecuta el workflow desde la rama <strong>base<\/strong> (la rama main de su repositorio) pero <strong>s\u00ed<\/strong> tiene acceso a los secrets.<\/p>\n<p>El peligro surge cuando los workflows de <code>pull_request_target<\/code> hacen checkout del c\u00f3digo de la rama head del PR:<\/p>\n<pre><code># PELIGROSO: pull_request_target con checkout expl\u00edcito del c\u00f3digo del PR\non: pull_request_target\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n        with:\n          ref: ${{ github.event.pull_request.head.sha }}\n      # Esto ahora ejecuta C\u00d3DIGO NO CONFIABLE con acceso a SECRETS\n      - run: npm install && npm test\n<\/code><\/pre>\n<p>Este patr\u00f3n le da a un atacante la capacidad de ejecutar c\u00f3digo arbitrario con acceso a los secrets de su repositorio. Es el ejemplo can\u00f3nico de Poisoned Pipeline Execution en GitHub Actions.<\/p>\n<h3>Reusable Workflows y Delegaci\u00f3n de Confianza<\/h3>\n<p>Los reusable workflows le permiten centralizar la l\u00f3gica del pipeline en un repositorio compartido y llamarlo desde otros repositorios. Cuando se invoca un reusable workflow, se ejecuta con los permisos y secrets del workflow <strong>que lo llama<\/strong>. Esto crea una cadena de delegaci\u00f3n de confianza: usted conf\u00eda en que el c\u00f3digo del reusable workflow (en otro repositorio) manejar\u00e1 sus secrets de manera responsable.<\/p>\n<p>Fije los reusable workflows a un SHA de commit espec\u00edfico, no a una rama o tag:<\/p>\n<pre><code>jobs:\n  deploy:\n    uses: my-org\/shared-workflows\/.github\/workflows\/deploy.yml@a1b2c3d4e5f6\n    secrets: inherit\n<\/code><\/pre>\n<h3>Reglas de Protecci\u00f3n de Environments<\/h3>\n<p>Los Environments de GitHub proporcionan una frontera de confianza cr\u00edtica para los workflows de despliegue. Puede configurar revisores requeridos, temporizadores de espera y restricciones de rama en los environments. Cuando un job hace referencia a un environment, debe satisfacer las reglas de protecci\u00f3n antes de que los secrets asociados con ese environment est\u00e9n disponibles:<\/p>\n<pre><code>jobs:\n  deploy-production:\n    runs-on: ubuntu-latest\n    environment:\n      name: production\n      url: https:\/\/example.com\n    steps:\n      - name: Deploy\n        run: .\/deploy.sh\n        env:\n          AWS_ACCESS_KEY_ID: ${{ secrets.PROD_AWS_KEY }}\n<\/code><\/pre>\n<p>Esto asegura que, incluso si se activa un workflow, las credenciales de producci\u00f3n no se exponen sin aprobaci\u00f3n humana.<\/p>\n<h2>Modelo de Ejecuci\u00f3n de GitLab CI<\/h2>\n<p>GitLab CI tiene un modelo de ejecuci\u00f3n diferente con sus propias caracter\u00edsticas de confianza, particularmente en torno al alcance de los runners y la protecci\u00f3n de variables.<\/p>\n<h3>Shared Runners vs Group Runners vs Project Runners<\/h3>\n<p>GitLab ofrece tres niveles de alcance para runners. Los <strong>shared runners<\/strong> (en GitLab.com, son gestionados por GitLab) est\u00e1n disponibles para todos los proyectos. Los <strong>group runners<\/strong> est\u00e1n disponibles para todos los proyectos dentro de un grupo de GitLab. Los <strong>project runners<\/strong> est\u00e1n dedicados a un solo proyecto. El alcance determina el radio de explosi\u00f3n de un runner comprometido \u2014 un compromiso de shared runner afecta a todos los proyectos, mientras que un compromiso de project runner se contiene a un solo proyecto.<\/p>\n<p>Para cargas de trabajo sensibles, siempre prefiera runners espec\u00edficos del proyecto con el etiquetado apropiado:<\/p>\n<pre><code>deploy-production:\n  stage: deploy\n  tags:\n    - production-runner\n    - isolated\n  script:\n    - .\/deploy.sh\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"\n<\/code><\/pre>\n<h3>Protected Branches y Protected Variables<\/h3>\n<p>El mecanismo de protected variables de GitLab es un control de confianza clave. Las variables marcadas como \u00abprotected\u00bb solo se exponen a pipelines que se ejecutan en ramas protegidas o tags protegidos. Esto significa que un pipeline activado por un merge request desde una rama de feature \u2014 o peor, desde un fork \u2014 no tendr\u00e1 acceso a las variables protegidas.<\/p>\n<p>Este es el mecanismo principal de GitLab para prevenir la exposici\u00f3n de secrets a c\u00f3digo no confiable:<\/p>\n<pre><code># En .gitlab-ci.yml, las variables protegidas solo est\u00e1n disponibles en ramas protegidas\ndeploy:\n  stage: deploy\n  script:\n    - echo \"Deploying with $PRODUCTION_API_KEY\"\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\"  # main es una rama protegida\n  environment:\n    name: production\n<\/code><\/pre>\n<h3>Alcance y Limitaciones del CI_JOB_TOKEN<\/h3>\n<p>Cada job de GitLab CI recibe un <code>CI_JOB_TOKEN<\/code>, un token generado autom\u00e1ticamente con alcance al proyecto. Por defecto, este token puede acceder a recursos de otros proyectos, lo que crea una relaci\u00f3n de confianza impl\u00edcita. GitLab le permite restringir el acceso del <code>CI_JOB_TOKEN<\/code> configurando una lista de permitidos de proyectos a los que se puede acceder \u2014 un paso de hardening cr\u00edtico que limita el movimiento lateral si un pipeline es comprometido.<\/p>\n<p>En la configuraci\u00f3n de su proyecto bajo <strong>CI\/CD \u2192 Token Access<\/strong>, restrinja el alcance del token solo a los proyectos con los que su pipeline genuinamente necesita interactuar.<\/p>\n<h3>Merge Request Pipelines y Fronteras de Confianza<\/h3>\n<p>GitLab distingue entre pipelines de rama y pipelines de merge request. Los pipelines de merge request se ejecutan en el contexto del merge request y tienen acceso a un conjunto limitado de variables predefinidas. Para pipelines activados por merge requests desde forks, GitLab no expone variables protegidas ni secrets a nivel de proyecto \u2014 esta es una frontera de confianza intencional.<\/p>\n<p>Sin embargo, los pipelines que se ejecutan sobre el <strong>resultado fusionado<\/strong> (el <code>merge_request_event<\/code> con pipelines de resultados fusionados habilitados) a\u00fan ejecutan el c\u00f3digo del fork. Si su definici\u00f3n de pipeline permite la ejecuci\u00f3n de c\u00f3digo arbitrario y el job tiene acceso a secrets a trav\u00e9s de variables no protegidas, esto a\u00fan puede ser explotado.<\/p>\n<h2>Fallas Comunes en los Supuestos de Confianza<\/h2>\n<p>Comprender los modelos de ejecuci\u00f3n es importante, pero el verdadero valor viene de reconocer los patrones que llevan al compromiso. Estas son las fallas en los supuestos de confianza que aparecen repetidamente en las brechas CI\/CD del mundo real.<\/p>\n<h3>Poisoned Pipeline Execution (PPE)<\/h3>\n<p>Poisoned Pipeline Execution ocurre cuando un atacante puede modificar la definici\u00f3n del pipeline que se ejecuta en un contexto privilegiado. Esta es la clase m\u00e1s prevalente de vulnerabilidad CI\/CD. Ocurre cuando:<\/p>\n<ul>\n<li>Un pull request activa un workflow que usa la versi\u00f3n del archivo del pipeline del PR<\/li>\n<li>Ese workflow tiene acceso a secrets o credenciales de despliegue<\/li>\n<li>No hay una puerta de revisi\u00f3n o aprobaci\u00f3n entre el PR y la ejecuci\u00f3n del pipeline<\/li>\n<\/ul>\n<p>El atacante modifica el YAML del pipeline (o un script que este llama) para exfiltrar secrets, inyectar backdoors en los artefactos de build, o pivotar hacia sistemas internos.<\/p>\n<h3>Asumir Aislamiento del Runner en Infraestructura Compartida<\/h3>\n<p>Cuando m\u00faltiples equipos o proyectos comparten runners \u2014 especialmente self-hosted runners \u2014 frecuentemente existe una suposici\u00f3n impl\u00edcita de aislamiento que en realidad no existe. Un job ejecut\u00e1ndose en un self-hosted runner compartido puede ser capaz de:<\/p>\n<ul>\n<li>Leer archivos dejados por jobs anteriores (credenciales en cach\u00e9, artefactos de build)<\/li>\n<li>Acceder al Docker socket e inspeccionar o modificar otros contenedores<\/li>\n<li>Alcanzar recursos de red interna disponibles para el host del runner<\/li>\n<li>Instalar backdoors persistentes en el runner para futuros jobs<\/li>\n<\/ul>\n<h3>Cuentas de Servicio con Permisos Excesivos<\/h3>\n<p>Un patr\u00f3n preocupantemente com\u00fan es otorgar a la cuenta de servicio CI\/CD acceso administrativo amplio \u2014 \u00absolo para que las cosas funcionen\u00bb. Un rol IAM de AWS con <code>AdministratorAccess<\/code>, una cuenta de servicio de Kubernetes con <code>cluster-admin<\/code>, o una cuenta de SQL en la nube con privilegios de DBA. Cuando cualquier paso del pipeline se ve comprometido, el atacante hereda todos estos permisos.<\/p>\n<h3>Confianza Impl\u00edcita en Actions y Templates de Terceros<\/h3>\n<p>Usar GitHub Actions de la comunidad o templates de GitLab CI significa ejecutar c\u00f3digo de otra persona en su pipeline con sus secrets. Cuando referencia <code>uses: some-org\/some-action@v2<\/code>, est\u00e1 confiando en que:<\/p>\n<ul>\n<li>El c\u00f3digo de la action no es malicioso<\/li>\n<li>Los mantenedores de la action no han sido comprometidos<\/li>\n<li>El tag <code>v2<\/code> no ha sido movido para apuntar a c\u00f3digo diferente<\/li>\n<li>Las dependencias de la action son confiables<\/li>\n<\/ul>\n<p>Las referencias de tags son mutables. Un atacante que comprometa el repositorio de una action puede mover el tag <code>v2<\/code> a un commit malicioso, y cada pipeline que referencia ese tag ejecutar\u00e1 el nuevo c\u00f3digo en su pr\u00f3xima ejecuci\u00f3n.<\/p>\n<h3>Confusi\u00f3n de Identidad entre Build-Time y Deploy-Time<\/h3>\n<p>Muchos pipelines usan una sola identidad (cuenta de servicio, rol IAM o token) tanto para construir como para desplegar. Esta confusi\u00f3n significa que un compromiso durante la fase de build \u2014 que maneja c\u00f3digo no confiable \u2014 da acceso directo a los objetivos de despliegue. La identidad de build solo deber\u00eda poder producir artefactos. Una identidad de deploy separada y m\u00e1s restringida deber\u00eda usarse para desplegar esos artefactos en producci\u00f3n.<\/p>\n<h2>Fortalecimiento de los Supuestos de Confianza<\/h2>\n<p>Con el modelo de amenazas claro, aqu\u00ed est\u00e1n las mitigaciones concretas que alinean los controles con las fronteras de confianza.<\/p>\n<h3>Condiciones de Trigger Expl\u00edcitas y Filtros de Rama<\/h3>\n<p>Nunca permita triggers de pipeline sin restricciones. Limite qu\u00e9 eventos pueden activar qu\u00e9 workflows, y aseg\u00farese de que los pipelines privilegiados solo se ejecuten en ramas de confianza:<\/p>\n<pre><code># GitHub Actions: restringir despliegue solo a la rama main\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n    # Solo activar en PRs dirigidos a main; el c\u00f3digo del PR se ejecuta sin secrets\n\njobs:\n  deploy:\n    if: github.event_name == 'push' && github.ref == 'refs\/heads\/main'\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\/checkout@v4\n      - run: .\/deploy.sh\n<\/code><\/pre>\n<pre><code># GitLab CI: usar rules para restringir jobs sensibles\ndeploy-production:\n  stage: deploy\n  script:\n    - .\/deploy.sh\n  rules:\n    - if: $CI_COMMIT_BRANCH == \"main\" && $CI_PIPELINE_SOURCE != \"merge_request_event\"\n      when: manual\n      allow_failure: false\n  environment:\n    name: production\n<\/code><\/pre>\n<h3>Permisos M\u00ednimos de Token<\/h3>\n<p>Aplique el principio de m\u00ednimo privilegio a cada token en su pipeline. En GitHub Actions, establezca permisos predeterminados restrictivos y otorgue permisos espec\u00edficos por job:<\/p>\n<pre><code># Establecer valores predeterminados restrictivos a nivel de workflow\npermissions: read-all\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      packages: write\n    steps:\n      - uses: actions\/checkout@v4\n      - run: npm ci && npm run build\n\n  deploy:\n    needs: build\n    runs-on: ubuntu-latest\n    permissions:\n      contents: read\n      id-token: write  # Solo para OIDC, sin escritura al repositorio\n    environment: production\n    steps:\n      - run: .\/deploy.sh\n<\/code><\/pre>\n<p>En GitLab, restrinja el alcance del <code>CI_JOB_TOKEN<\/code> en la configuraci\u00f3n del proyecto y use variables protegidas exclusivamente para credenciales sensibles.<\/p>\n<h3>Runners Ef\u00edmeros y Aislados<\/h3>\n<p>Siempre que sea posible, use runners ef\u00edmeros que se crean nuevos para cada job y se destruyen inmediatamente despu\u00e9s. Esto elimina los ataques basados en persistencia y la fuga de datos entre jobs. Para entornos self-hosted, herramientas como el <a href=\"https:\/\/github.com\/actions\/actions-runner-controller\" target=\"_blank\" rel=\"noopener\">Actions Runner Controller<\/a> (ARC) de GitHub para Kubernetes o el runner con autoescalado de GitLab en AWS\/GCP pueden aprovisionar pods o VMs de runner ef\u00edmeros para cada job.<\/p>\n<p>Propiedades clave de una configuraci\u00f3n de runner fortalecida:<\/p>\n<ul>\n<li>Sin almacenamiento persistente entre jobs<\/li>\n<li>Sin Docker socket compartido<\/li>\n<li>Segmentaci\u00f3n de red limitando el acceso solo a los endpoints requeridos<\/li>\n<li>Sin capacidad para que el job modifique la configuraci\u00f3n del propio runner<\/li>\n<\/ul>\n<h3>Fijar Actions e Im\u00e1genes por SHA<\/h3>\n<p>Las referencias mutables (nombres de ramas, tags como <code>v2<\/code>) pueden ser cambiadas por mantenedores upstream \u2014 o atacantes. Fijar a un SHA de commit espec\u00edfico asegura que el c\u00f3digo exacto que revis\u00f3 es lo que se ejecuta en su pipeline:<\/p>\n<pre><code># En lugar de esto (tag mutable):\n- uses: actions\/checkout@v4\n\n# Use esto (SHA inmutable):\n- uses: actions\/checkout@b4ffde65f46336ab88eb53be808477a3936bae11  # v4.1.1\n<\/code><\/pre>\n<p>El mismo principio aplica a las im\u00e1genes de contenedores. Use digests de im\u00e1genes en lugar de tags:<\/p>\n<pre><code># En lugar de:\nimage: node:20-alpine\n\n# Use:\nimage: node@sha256:a1b2c3d4e5f6...  # fijar a un digest espec\u00edfico\n<\/code><\/pre>\n<p>Herramientas como Dependabot y Renovate pueden crear PRs autom\u00e1ticamente para actualizar los SHAs fijados cuando se lanzan nuevas versiones, as\u00ed obtiene tanto seguridad como mantenibilidad.<\/p>\n<h3>Separaci\u00f3n de Identidades de Build y Deploy<\/h3>\n<p>Implemente identidades distintas para las fases de build y deploy. La identidad de build deber\u00eda tener:<\/p>\n<ul>\n<li>Acceso de lectura al c\u00f3digo fuente<\/li>\n<li>Acceso de escritura al almacenamiento de artefactos (container registry, bucket S3)<\/li>\n<li>Sin acceso a entornos de producci\u00f3n<\/li>\n<\/ul>\n<p>La identidad de deploy deber\u00eda tener:<\/p>\n<ul>\n<li>Acceso de lectura al almacenamiento de artefactos<\/li>\n<li>Acceso de escritura al objetivo de despliegue espec\u00edfico<\/li>\n<li>Sin acceso al c\u00f3digo fuente ni la capacidad de activar builds<\/li>\n<\/ul>\n<p>Use federaci\u00f3n OIDC donde sea posible para eliminar por completo las credenciales de larga duraci\u00f3n. Tanto GitHub Actions como GitLab CI soportan tokens OIDC que pueden intercambiarse por credenciales de corta duraci\u00f3n del proveedor cloud:<\/p>\n<pre><code># GitHub Actions OIDC con AWS\njobs:\n  deploy:\n    permissions:\n      id-token: write\n      contents: read\n    steps:\n      - uses: aws-actions\/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502\n        with:\n          role-to-assume: arn:aws:iam::123456789012:role\/deploy-production\n          aws-region: us-east-1\n<\/code><\/pre>\n<pre><code># GitLab CI OIDC con AWS\ndeploy:\n  stage: deploy\n  id_tokens:\n    AWS_TOKEN:\n      aud: https:\/\/gitlab.com\n  script:\n    - >\n      STS_CREDENTIALS=$(aws sts assume-role-with-web-identity\n      --role-arn arn:aws:iam::123456789012:role\/deploy-production\n      --web-identity-token $AWS_TOKEN\n      --role-session-name \"gitlab-ci-${CI_JOB_ID}\")\n    - export AWS_ACCESS_KEY_ID=$(echo $STS_CREDENTIALS | jq -r '.Credentials.AccessKeyId')\n    - .\/deploy.sh\n<\/code><\/pre>\n<h2>Conclusi\u00f3n<\/h2>\n<p>Todo pipeline CI\/CD tiene un modelo de confianza. La pregunta es si ese modelo de confianza fue dise\u00f1ado intencionalmente o surgi\u00f3 accidentalmente de configuraciones predeterminadas y soluciones r\u00e1pidas.<\/p>\n<p>El modelo de ejecuci\u00f3n que elija \u2014 alojado en SaaS, self-hosted, containerizado o serverless \u2014 determina las propiedades de seguridad base de su pipeline. Pero el modelo de ejecuci\u00f3n por s\u00ed solo no es suficiente. La confianza debe ser expl\u00edcitamente delimitada en cada transici\u00f3n: del c\u00f3digo fuente al trigger, del trigger a la ejecuci\u00f3n, de la ejecuci\u00f3n a los secrets, y del build al despliegue.<\/p>\n<p>Los patrones cubiertos en esta gu\u00eda \u2014 Poisoned Pipeline Execution, abuso de runners compartidos, identidades con permisos excesivos, referencias mutables de actions y la confusi\u00f3n de identidades build\/deploy \u2014 no son te\u00f3ricos. Son las t\u00e9cnicas reales utilizadas en ataques a la cadena de suministro del mundo real, desde el compromiso de SolarWinds hasta la brecha de Codecov y m\u00e1s all\u00e1.<\/p>\n<p>Comience mapeando sus fronteras de confianza actuales. Identifique d\u00f3nde la confianza se asume en lugar de verificarse. Luego aplique las medidas de fortalecimiento de forma sistem\u00e1tica: restrinja triggers, minimice permisos, a\u00edsle runners, fije dependencias y separe identidades. Trate su pipeline CI\/CD con el mismo rigor que aplica a su infraestructura de producci\u00f3n \u2014 porque en la pr\u00e1ctica, <em>es<\/em> la puerta de entrada de su infraestructura de producci\u00f3n.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Introducci\u00f3n Los pipelines CI\/CD se encuentran entre los componentes m\u00e1s privilegiados de cualquier organizaci\u00f3n de software moderna. Clonan c\u00f3digo fuente, acceden a secrets, construyen artefactos y despliegan en producci\u00f3n \u2014 frecuentemente con m\u00ednima supervisi\u00f3n humana. Sin embargo, a pesar de este extraordinario nivel de acceso, los modelos de confianza que sustentan estos pipelines rara vez &#8230; <a title=\"Modelos de Ejecuci\u00f3n CI\/CD y Supuestos de Confianza: Gu\u00eda de Seguridad\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/ci-cd-execution-models-trust-assumptions-security-guide\/\" aria-label=\"Leer m\u00e1s sobre Modelos de Ejecuci\u00f3n CI\/CD y Supuestos de Confianza: Gu\u00eda de Seguridad\">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],"tags":[],"post_folder":[],"class_list":["post-620","post","type-post","status-publish","format-standard","hentry","category-ci-cd-security"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/620","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=620"}],"version-history":[{"count":1,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/620\/revisions"}],"predecessor-version":[{"id":629,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/620\/revisions\/629"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=620"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=620"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=620"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=620"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}