{"id":643,"date":"2026-03-01T20:21:31","date_gmt":"2026-03-01T19:21:31","guid":{"rendered":"https:\/\/secure-pipelines.com\/?p=643"},"modified":"2026-07-08T22:42:40","modified_gmt":"2026-07-08T21:42:40","slug":"network-filesystem-restrictions-ci-cd-build-environments","status":"publish","type":"post","link":"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/network-filesystem-restrictions-ci-cd-build-environments\/","title":{"rendered":"Restricciones de Red y Sistema de Archivos para Entornos de Build CI\/CD"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Los pipelines CI\/CD se encuentran entre las cargas de trabajo m\u00e1s privilegiadas de cualquier organizaci\u00f3n. Descargan c\u00f3digo fuente, obtienen dependencias, acceden a secretos y hacen push de artefactos a registros de producci\u00f3n. Sin embargo, en muchos entornos, los procesos de build que hay detr\u00e1s de estos pipelines se ejecutan con acceso de red sin restricciones y permisos totales sobre el sistema de archivos, una combinaci\u00f3n que representa una de las brechas m\u00e1s explotables de la entrega de software moderna.<\/p>\n\n<p class=\"wp-block-paragraph\">Cuando un entorno de build puede alcanzar cualquier direcci\u00f3n IP y escribir en cualquier ruta del disco, una \u00fanica dependencia comprometida o un pull request malicioso pueden exfiltrar secretos, manipular artefactos o establecer puertas traseras persistentes. Esta gu\u00eda cubre t\u00e9cnicas pr\u00e1cticas para restringir el acceso de red y al sistema de archivos en los entornos de build CI\/CD, desde las NetworkPolicies de Kubernetes hasta los sistemas de build herm\u00e9ticos.<\/p>\n\n<h2 class=\"wp-block-heading\">Por qu\u00e9 los entornos de build sin restricciones son peligrosos<\/h2>\n\n<p class=\"wp-block-paragraph\">Antes de adentrarnos en las soluciones, conviene entender las amenazas concretas que crean los entornos de build sin restricciones. Estos riesgos no son te\u00f3ricos: se han explotado en ataques a la cadena de suministro del mundo real.<\/p>\n\n<h3 class=\"wp-block-heading\">Exfiltraci\u00f3n de datos<\/h3>\n\n<p class=\"wp-block-paragraph\">Los entornos de build suelen tener acceso a secretos: claves de API, credenciales de registro, claves de firma y tokens de despliegue. Si un proceso de build tiene acceso de red saliente sin restricciones, una dependencia comprometida puede enviar esos secretos a un servidor controlado por el atacante. Esto puede ocurrir mediante un script <code>postinstall<\/code> malicioso en un paquete de npm, una dependencia de PyPI comprometida o incluso un target elaborado en un Makefile. Sin restricciones de red, no hay ninguna barrera entre el secreto y el endpoint del atacante.<\/p>\n\n<h3 class=\"wp-block-heading\">Ataques a la cadena de suministro<\/h3>\n\n<p class=\"wp-block-paragraph\">Un atacante que puede ejecutar c\u00f3digo arbitrario durante un build puede modificar los artefactos de salida. Si el sistema de archivos es escribible sin restricciones, los binarios compilados, las im\u00e1genes de contenedor o los manifiestos de despliegue pueden manipularse despu\u00e9s del paso de build leg\u00edtimo pero antes de que el artefacto se haga push. Esta es la esencia de muchos ataques a la cadena de suministro: el c\u00f3digo fuente parece limpio, pero el artefacto entregado est\u00e1 envenenado.<\/p>\n\n<h3 class=\"wp-block-heading\">Movimiento lateral<\/h3>\n\n<p class=\"wp-block-paragraph\">Los entornos de build que comparten red con otra infraestructura (bases de datos, APIs internas, servicios de metadatos de la nube) proporcionan al atacante un punto de pivote. Un job de build comprometido puede escanear las redes internas, acceder a los endpoints de metadatos de las instancias de la nube (como <code>169.254.169.254<\/code>) y escalar desde un contexto CI\/CD hasta un acceso m\u00e1s amplio a la infraestructura.<\/p>\n\n<h2 class=\"wp-block-heading\">Restricciones de red<\/h2>\n\n<p class=\"wp-block-paragraph\">El control de mayor impacto que puedes implementar es restringir el acceso de red saliente desde los entornos de build. Los builds necesitan descargar dependencias y hacer push de artefactos, pero rara vez necesitan acceso a internet sin restricciones.<\/p>\n\n<h3 class=\"wp-block-heading\">NetworkPolicy de Kubernetes para los pods de runner<\/h3>\n\n<p class=\"wp-block-paragraph\">Si ejecutas runners CI\/CD en Kubernetes (por ejemplo, usando Actions Runner Controller o el ejecutor de Kubernetes de GitLab), los recursos NetworkPolicy te dan un control de grano fino sobre el acceso de red a nivel de pod. Una pol\u00edtica bien dise\u00f1ada deniega todo el egress por defecto y luego permite \u00fanicamente los endpoints espec\u00edficos que el build necesita.<\/p>\n\n<pre><code>apiVersion: networking.k8s.io\/v1\nkind: NetworkPolicy\nmetadata:\n  name: ci-runner-netpol\n  namespace: ci-runners\nspec:\n  podSelector:\n    matchLabels:\n      app: ci-runner\n  policyTypes:\n    - Egress\n  egress:\n    # Allow DNS resolution\n    - to:\n        - namespaceSelector: {}\n      ports:\n        - protocol: UDP\n          port: 53\n        - protocol: TCP\n          port: 53\n    # Allow access to container registry\n    - to:\n        - ipBlock:\n            cidr: 10.0.50.0\/24\n      ports:\n        - protocol: TCP\n          port: 443\n    # Allow access to artifact storage\n    - to:\n        - ipBlock:\n            cidr: 10.0.60.0\/24\n      ports:\n        - protocol: TCP\n          port: 443\n    # Deny everything else by omission<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Esta pol\u00edtica permite que los pods de runner resuelvan DNS, alcancen el registro de contenedores y accedan al almacenamiento de artefactos; nada m\u00e1s. Cualquier otra conexi\u00f3n saliente se descarta. Si usas un plugin CNI que admite NetworkPolicy (Calico, Cilium o Weave Net), esto surte efecto de inmediato al aplicarse.<\/p>\n\n<p class=\"wp-block-paragraph\">Para un control m\u00e1s granular, la <code>CiliumNetworkPolicy<\/code> de Cilium admite reglas basadas en DNS, lo que te permite especificar nombres de dominio en lugar de bloques de IP:<\/p>\n\n<pre><code>apiVersion: cilium.io\/v2\nkind: CiliumNetworkPolicy\nmetadata:\n  name: ci-runner-cilium-policy\n  namespace: ci-runners\nspec:\n  endpointSelector:\n    matchLabels:\n      app: ci-runner\n  egress:\n    - toEndpoints:\n        - matchLabels:\n            io.kubernetes.pod.namespace: kube-system\n            k8s-app: kube-dns\n      toPorts:\n        - ports:\n            - port: \"53\"\n              protocol: ANY\n    - toFQDNs:\n        - matchName: \"ghcr.io\"\n        - matchName: \"registry.npmjs.org\"\n        - matchName: \"pypi.org\"\n      toPorts:\n        - ports:\n            - port: \"443\"\n              protocol: TCP<\/code><\/pre>\n\n<h3 class=\"wp-block-heading\">Docker &#8211;network=none<\/h3>\n\n<p class=\"wp-block-paragraph\">Para los pasos de build basados en Docker que no deber\u00edan necesitar ning\u00fan acceso de red (compilaci\u00f3n, an\u00e1lisis est\u00e1tico, pruebas unitarias), puedes eliminar por completo el acceso de red ejecutando el contenedor con <code>--network=none<\/code>:<\/p>\n\n<pre><code>docker run --network=none \\\n  --rm \\\n  -v \"$(pwd)\/src:\/workspace:ro\" \\\n  -v \"$(pwd)\/output:\/output\" \\\n  my-build-image:latest \\\n  make build<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Con <code>--network=none<\/code>, el contenedor no tiene ninguna interfaz de red, ni siquiera loopback en algunas configuraciones. Este es el aislamiento de red m\u00e1s fuerte que puedes lograr para un paso de build. La clave es estructurar tu pipeline de modo que la descarga de dependencias ocurra en una etapa (con acceso de red limitado) y el build propiamente dicho ocurra en una etapa separada y sin red.<\/p>\n\n<h3 class=\"wp-block-heading\">Reglas de firewall para runners autoalojados<\/h3>\n\n<p class=\"wp-block-paragraph\">Si usas runners autoalojados en VMs en lugar de contenedores, las reglas de firewall a nivel de host proporcionan una protecci\u00f3n equivalente. En Linux, las reglas de <code>iptables<\/code> o <code>nftables<\/code> pueden restringir el tr\u00e1fico saliente de la cuenta de usuario que ejecuta los jobs de CI:<\/p>\n\n<pre><code># Allow DNS\niptables -A OUTPUT -m owner --uid-owner ci-runner -p udp --dport 53 -j ACCEPT\niptables -A OUTPUT -m owner --uid-owner ci-runner -p tcp --dport 53 -j ACCEPT\n\n# Allow HTTPS to specific registries\niptables -A OUTPUT -m owner --uid-owner ci-runner -p tcp --dport 443 \\\n  -d registry.example.com -j ACCEPT\niptables -A OUTPUT -m owner --uid-owner ci-runner -p tcp --dport 443 \\\n  -d ghcr.io -j ACCEPT\n\n# Deny all other outbound traffic from the CI runner\niptables -A OUTPUT -m owner --uid-owner ci-runner -j DROP<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Este enfoque funciona bien cuando ejecutas el agente de CI bajo una cuenta de usuario dedicada y necesitas permitir que el propio sistema host mantenga una conectividad m\u00e1s amplia para la gesti\u00f3n y las actualizaciones.<\/p>\n\n<h3 class=\"wp-block-heading\">Listas de permitidos para registros y APIs<\/h3>\n\n<p class=\"wp-block-paragraph\">Sea cual sea el mecanismo de aplicaci\u00f3n, el principio es el mismo: denegar el egress por defecto y luego permitir en la lista \u00fanicamente lo que el build realmente necesita. Una lista de permitidos t\u00edpica incluye el registro de paquetes (npm, PyPI, Maven Central), el registro de contenedores (Docker Hub, GHCR, ECR), la API de la plataforma CI\/CD (para las actualizaciones de estado y las subidas de artefactos) y, posiblemente, un proxy o mirror que t\u00fa controles. Todo lo dem\u00e1s deber\u00eda bloquearse. Usa un proxy o mirror interno para las dependencias siempre que sea posible: reduce la lista de permitidos a un \u00fanico endpoint y te da cach\u00e9 y registro de auditor\u00eda de forma gratuita.<\/p>\n\n<h2 class=\"wp-block-heading\">Restricciones del sistema de archivos<\/h2>\n\n<p class=\"wp-block-paragraph\">Las restricciones de red impiden que los datos salgan del entorno de build. Las restricciones del sistema de archivos impiden las modificaciones no autorizadas dentro de \u00e9l. Juntas, forman una s\u00f3lida postura de defensa en profundidad.<\/p>\n\n<h3 class=\"wp-block-heading\">Sistema de archivos ra\u00edz de solo lectura<\/h3>\n\n<p class=\"wp-block-paragraph\">Ejecutar los contenedores de build con un sistema de archivos ra\u00edz de solo lectura impide que cualquier proceso modifique la imagen base. Esto bloquea una clase de ataques en la que el c\u00f3digo malicioso modifica los binarios del sistema, instala puertas traseras o altera las configuraciones de las herramientas de build a nivel del sistema.<\/p>\n\n<p class=\"wp-block-paragraph\">En Docker, usa el flag <code>--read-only<\/code>:<\/p>\n\n<pre><code>docker run --read-only \\\n  --tmpfs \/tmp:rw,noexec,nosuid,size=512m \\\n  --tmpfs \/workspace\/build:rw,size=2g \\\n  -v \"$(pwd)\/src:\/workspace\/src:ro\" \\\n  my-build-image:latest \\\n  make build<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">En Kubernetes, establece el security context en la especificaci\u00f3n del pod:<\/p>\n\n<pre><code>apiVersion: v1\nkind: Pod\nmetadata:\n  name: ci-build-pod\nspec:\n  containers:\n    - name: build\n      image: my-build-image:latest\n      securityContext:\n        readOnlyRootFilesystem: true\n        runAsNonRoot: true\n        allowPrivilegeEscalation: false\n      volumeMounts:\n        - name: build-tmp\n          mountPath: \/tmp\n        - name: build-output\n          mountPath: \/workspace\/build\n        - name: source\n          mountPath: \/workspace\/src\n          readOnly: true\n  volumes:\n    - name: build-tmp\n      emptyDir:\n        medium: Memory\n        sizeLimit: 512Mi\n    - name: build-output\n      emptyDir:\n        sizeLimit: 2Gi\n    - name: source\n      configMap:\n        name: source-code<\/code><\/pre>\n\n<h3 class=\"wp-block-heading\">tmpfs para los artefactos de build<\/h3>\n\n<p class=\"wp-block-paragraph\">Cuando el sistema de archivos ra\u00edz es de solo lectura, los builds necesitan espacio escribible para los archivos temporales, las cach\u00e9s y los artefactos de salida. Usa montajes <code>tmpfs<\/code> (respaldados por RAM) o vol\u00famenes <code>emptyDir<\/code> (en Kubernetes) para estas rutas. Esto tiene la ventaja a\u00f1adida de que todos los artefactos de build se limpian autom\u00e1ticamente cuando el contenedor termina: no persisten datos obsoletos entre builds.<\/p>\n\n<p class=\"wp-block-paragraph\">Monta <code>tmpfs<\/code> con opciones restrictivas siempre que sea posible: <code>noexec<\/code> impide la ejecuci\u00f3n de binarios escritos en los directorios temporales (bloqueando un vector de ataque habitual), <code>nosuid<\/code> previene los ataques de bit SUID y los l\u00edmites de <code>size<\/code> impiden que un build descontrolado agote la memoria del host.<\/p>\n\n<h3 class=\"wp-block-heading\">Impedir escrituras en rutas sensibles<\/h3>\n\n<p class=\"wp-block-paragraph\">M\u00e1s all\u00e1 del sistema de archivos ra\u00edz, hay rutas espec\u00edficas que merecen protecci\u00f3n adicional. Monta el c\u00f3digo fuente como solo lectura para impedir que el build modifique sus propias entradas. Aseg\u00farate de que <code>\/etc<\/code>, <code>\/usr<\/code> y <code>\/var<\/code> no sean escribibles. Si el build necesita escribir en un directorio home (para la configuraci\u00f3n de herramientas), proporciona un montaje escribible dedicado en lugar de hacer escribible todo el directorio home. Bloquea el acceso a los sockets de Docker, los tokens de cuentas de servicio de Kubernetes y los archivos de credenciales de la nube no mont\u00e1ndolos en absoluto en los contenedores de build.<\/p>\n\n<h2 class=\"wp-block-heading\">Builds herm\u00e9ticos<\/h2>\n\n<p class=\"wp-block-paragraph\">El est\u00e1ndar de referencia para la seguridad del entorno de build es el build herm\u00e9tico: un build que no tiene ning\u00fan acceso de red y usa \u00fanicamente entradas declaradas expl\u00edcitamente y descargadas previamente. Los builds herm\u00e9ticos eliminan clases enteras de ataques a la cadena de suministro porque el proceso de build no puede descargar c\u00f3digo que no se haya especificado y verificado expl\u00edcitamente.<\/p>\n\n<h3 class=\"wp-block-heading\">El patr\u00f3n de build herm\u00e9tico<\/h3>\n\n<p class=\"wp-block-paragraph\">Un pipeline de build herm\u00e9tico suele tener dos fases. En la primera fase (la fase de resoluci\u00f3n\/descarga), las dependencias se descargan de fuentes aprobadas, sus checksums se verifican contra un lockfile y se almacenan en una cach\u00e9 local o en un directorio vendored. Esta fase requiere un acceso de red limitado. En la segunda fase (la fase de build), la compilaci\u00f3n o el ensamblaje propiamente dichos ocurren con cero acceso de red. Todas las entradas provienen del sistema de archivos local: el c\u00f3digo fuente y las dependencias descargadas previamente.<\/p>\n\n<pre><code># Phase 1: Fetch dependencies (limited network)\ndocker run --network=ci-restricted \\\n  -v \"$(pwd):\/workspace\" \\\n  my-build-image:latest \\\n  sh -c \"cd \/workspace && npm ci --ignore-scripts\"\n\n# Phase 2: Build (no network)\ndocker run --network=none \\\n  --read-only \\\n  --tmpfs \/tmp:rw,noexec,size=512m \\\n  -v \"$(pwd):\/workspace:ro\" \\\n  -v \"$(pwd)\/dist:\/dist\" \\\n  my-build-image:latest \\\n  sh -c \"cd \/workspace && npm run build && cp -r build\/* \/dist\/\"<\/code><\/pre>\n\n<h3 class=\"wp-block-heading\">Bazel y los builds herm\u00e9ticos<\/h3>\n\n<p class=\"wp-block-paragraph\">Bazel est\u00e1 dise\u00f1ado en torno a la hermeticidad. Con <code>--sandbox_default_allow_network=false<\/code>, Bazel bloquea el acceso de red durante las acciones de build por defecto. Las dependencias se declaran en los archivos <code>WORKSPACE<\/code> o <code>MODULE.bazel<\/code> con hashes SHA-256 expl\u00edcitos, y Bazel las descarga en una fase separada antes de que comience el build. Si una dependencia no coincide con su hash declarado, el build falla.<\/p>\n\n<pre><code># In .bazelrc\nbuild --sandbox_default_allow_network=false\nbuild --incompatible_strict_action_env\nfetch --repository_cache=\/shared\/bazel-cache\/repos<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Esto hace que los builds de Bazel sean reproducibles y resistentes a los ataques de dependency confusion. Cada entrada est\u00e1 direccionada por contenido y verificada.<\/p>\n\n<h3 class=\"wp-block-heading\">Nix y los builds reproducibles<\/h3>\n\n<p class=\"wp-block-paragraph\">Nix adopta un enfoque similar. Cada derivaci\u00f3n de build especifica sus entradas mediante un hash de contenido, y el sandbox de build de Nix bloquea el acceso de red por defecto. El comando <code>nix-build<\/code> descarga todas las fuentes en el Nix store (verificando los hashes) y luego ejecuta el build en un entorno aislado sin red y con un sistema de archivos m\u00ednimo. Esto garantiza que los builds sean reproducibles: las mismas entradas siempre producen la misma salida.<\/p>\n\n<h2 class=\"wp-block-heading\">Implementaci\u00f3n pr\u00e1ctica<\/h2>\n\n<p class=\"wp-block-paragraph\">Veamos c\u00f3mo implementar estas restricciones en plataformas CI\/CD concretas.<\/p>\n\n<h3 class=\"wp-block-heading\">GitHub Actions con Actions Runner Controller (ARC) + NetworkPolicy<\/h3>\n\n<p class=\"wp-block-paragraph\">Si usas <a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-ephemeral-self-hosted-runners-actions-runner-controller-2\/\">Actions Runner Controller<\/a> para ejecutar GitHub Actions en Kubernetes, puedes aplicar NetworkPolicies directamente a los pods de runner. ARC crea pods con etiquetas predecibles, lo que facilita apuntarlos con pol\u00edticas.<\/p>\n\n<pre><code>apiVersion: actions.summerwind.dev\/v1alpha1\nkind: RunnerDeployment\nmetadata:\n  name: secure-runner\n  namespace: ci-runners\nspec:\n  replicas: 3\n  template:\n    metadata:\n      labels:\n        app: ci-runner\n        security-tier: restricted\n    spec:\n      containers:\n        - name: runner\n          securityContext:\n            readOnlyRootFilesystem: true\n            runAsNonRoot: true\n            allowPrivilegeEscalation: false\n            capabilities:\n              drop:\n                - ALL\n          volumeMounts:\n            - name: work\n              mountPath: \/runner\/_work\n            - name: tmp\n              mountPath: \/tmp\n      volumes:\n        - name: work\n          emptyDir:\n            sizeLimit: 10Gi\n        - name: tmp\n          emptyDir:\n            medium: Memory\n            sizeLimit: 1Gi\n---\napiVersion: networking.k8s.io\/v1\nkind: NetworkPolicy\nmetadata:\n  name: secure-runner-netpol\n  namespace: ci-runners\nspec:\n  podSelector:\n    matchLabels:\n      app: ci-runner\n  policyTypes:\n    - Egress\n    - Ingress\n  ingress: []\n  egress:\n    - to:\n        - namespaceSelector: {}\n      ports:\n        - protocol: UDP\n          port: 53\n    - to:\n        - ipBlock:\n            cidr: 0.0.0.0\/0\n      ports:\n        - protocol: TCP\n          port: 443<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Esta configuraci\u00f3n deniega todo el tr\u00e1fico de ingress (los runners no deber\u00edan aceptar conexiones entrantes) y limita el egress a DNS y HTTPS. Para uso en producci\u00f3n, sustituye el CIDR <code>0.0.0.0\/0<\/code> por rangos de IP espec\u00edficos para la API de GitHub, tu registro de contenedores y tu almac\u00e9n de artefactos.<\/p>\n\n<h3 class=\"wp-block-heading\">GitLab CI con la configuraci\u00f3n del runner<\/h3>\n\n<p class=\"wp-block-paragraph\">El ejecutor de Kubernetes de GitLab admite la configuraci\u00f3n del security context en el <code>config.toml<\/code> del runner. Puedes establecer directamente el sistema de archivos de solo lectura y otras restricciones:<\/p>\n\n<pre><code># config.toml for GitLab Runner (Kubernetes executor)\n[[runners]]\n  name = \"secure-k8s-runner\"\n  executor = \"kubernetes\"\n  [runners.kubernetes]\n    namespace = \"ci-runners\"\n    image = \"alpine:latest\"\n    privileged = false\n    allow_privilege_escalation = false\n    [runners.kubernetes.pod_security_context]\n      run_as_non_root = true\n      run_as_user = 1000\n    [runners.kubernetes.build_container_security_context]\n      read_only_root_filesystem = true\n      allow_privilege_escalation = false\n      [runners.kubernetes.build_container_security_context.capabilities]\n        drop = [\"ALL\"]\n    [runners.kubernetes.volumes]\n      [[runners.kubernetes.volumes.empty_dir]]\n        name = \"build-tmp\"\n        mount_path = \"\/tmp\"\n        medium = \"Memory\"\n        size_limit = \"512Mi\"\n      [[runners.kubernetes.volumes.empty_dir]]\n        name = \"build-workspace\"\n        mount_path = \"\/builds\"\n        size_limit = \"5Gi\"<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Combina esto con una NetworkPolicy aplicada al namespace <code>ci-runners<\/code> y tendr\u00e1s en marcha tanto las restricciones del sistema de archivos como las de red.<\/p>\n\n<h3 class=\"wp-block-heading\">Restricciones de Docker-in-Docker<\/h3>\n\n<p class=\"wp-block-paragraph\">Docker-in-Docker (DinD) se usa habitualmente para construir im\u00e1genes de contenedor en CI. Tambi\u00e9n es uno de los patrones m\u00e1s arriesgados porque normalmente requiere el modo privilegiado. Si debes usar DinD, aplica estas restricciones:<\/p>\n\n<pre><code># Use rootless DinD instead of privileged mode\nservices:\n  dind:\n    image: docker:24-dind-rootless\n    environment:\n      - DOCKER_TLS_CERTDIR=\/certs\n    volumes:\n      - dind-certs:\/certs\/client\n      - dind-data:\/var\/lib\/docker\n\n# When running builds inside DinD, pass network and filesystem restrictions\ndocker --host tcp:\/\/dind:2376 --tlsverify \\\n  run --network=none --read-only \\\n  --tmpfs \/tmp:rw,noexec,size=256m \\\n  --security-opt=no-new-privileges \\\n  my-build-image:latest make build<\/code><\/pre>\n\n<p class=\"wp-block-paragraph\">Mejor a\u00fan, sustituye DinD por herramientas que no necesiten ning\u00fan demonio de Docker. <code>kaniko<\/code>, <code>buildah<\/code> y <code>ko<\/code> pueden construir im\u00e1genes de contenedor sin acceso privilegiado, y funcionan bien con sistemas de archivos de solo lectura y redes restringidas.<\/p>\n\n<h2 class=\"wp-block-heading\">Monitorizaci\u00f3n y auditor\u00eda<\/h2>\n\n<p class=\"wp-block-paragraph\">Las restricciones solo son \u00fatiles si sabes cu\u00e1ndo se est\u00e1n probando o eludiendo. La monitorizaci\u00f3n completa el panorama de seguridad.<\/p>\n\n<h3 class=\"wp-block-heading\">Detectar conexiones de red inesperadas<\/h3>\n\n<p class=\"wp-block-paragraph\">Usa Hubble de Cilium, los flow logs de Calico o Falco para detectar conexiones de red que tu pol\u00edtica deber\u00eda haber bloqueado (o conexiones a destinos inusuales en puertos permitidos). Configura alertas para cualquier consulta DNS a dominios que no est\u00e9n en tu lista de permitidos, conexiones salientes a puertos no est\u00e1ndar, conexiones a rangos de IP conocidos como maliciosos y cualquier tr\u00e1fico de egress desde pods que deber\u00edan tener <code>--network=none<\/code>.<\/p>\n\n<pre><code># Falco rule: detect unexpected outbound connections from CI runners\n- rule: CI Runner Unexpected Outbound Connection\n  desc: Detect network connections from CI runner pods to non-approved destinations\n  condition: >\n    evt.type in (connect, sendto) and\n    container and\n    k8s.ns.name = \"ci-runners\" and\n    not (fd.sip in (approved_registry_ips) or fd.sport = 53)\n  output: >\n    Unexpected outbound connection from CI runner\n    (command=%proc.cmdline connection=%fd.name container=%container.name\n    pod=%k8s.pod.name namespace=%k8s.ns.name)\n  priority: WARNING\n  tags: [network, ci-cd, supply-chain]<\/code><\/pre>\n\n<h3 class=\"wp-block-heading\">Auditar el acceso al sistema de archivos<\/h3>\n\n<p class=\"wp-block-paragraph\">Monitoriza las escrituras en el sistema de archivos de los contenedores de build para detectar modificaciones inesperadas. El <code>auditd<\/code> de Linux puede vigilar rutas espec\u00edficas, y Falco puede detectar escrituras en ubicaciones sensibles. Las rutas clave que hay que monitorizar incluyen <code>\/etc<\/code> y <code>\/usr<\/code> (nunca deber\u00edan escribirse en un build), la ruta del socket de Docker, las rutas de los tokens de cuentas de servicio de Kubernetes y cualquier ruta que contenga credenciales o claves de firma.<\/p>\n\n<p class=\"wp-block-paragraph\">Si usas sistemas de archivos ra\u00edz de solo lectura, cualquier intento de escritura en una ruta protegida genera un error: registra estos errores y alerta sobre ellos. Indican o bien un build mal configurado, o bien un posible ataque.<\/p>\n\n<h2 class=\"wp-block-heading\">Compromisos y experiencia de desarrollo<\/h2>\n\n<p class=\"wp-block-paragraph\">Las restricciones estrictas de red y sistema de archivos crean inevitablemente fricci\u00f3n. Comprender y gestionar los compromisos es fundamental para una adopci\u00f3n exitosa.<\/p>\n\n<h3 class=\"wp-block-heading\">Velocidad del build<\/h3>\n\n<p class=\"wp-block-paragraph\">Los builds herm\u00e9ticos requieren que todas las dependencias se descarguen previamente, lo que a\u00f1ade una etapa al pipeline. Sin embargo, esto tambi\u00e9n significa que las dependencias pueden almacenarse en cach\u00e9 de forma agresiva. En la pr\u00e1ctica, muchos equipos descubren que los builds herm\u00e9ticos son en realidad m\u00e1s r\u00e1pidos porque la tasa de aciertos de cach\u00e9 es mucho mayor cuando la resoluci\u00f3n de dependencias es determinista. Usa una cach\u00e9 compartida (una cach\u00e9 remota de Bazel, una cach\u00e9 binaria de Nix o una simple cach\u00e9 HTTP para las dependencias vendored) para amortizar el coste entre builds.<\/p>\n\n<h3 class=\"wp-block-heading\">Experiencia de desarrollo<\/h3>\n\n<p class=\"wp-block-paragraph\">Los desarrolladores se encontrar\u00e1n con fallos cuando los builds intenten acceder a endpoints de red bloqueados o escribir en rutas de solo lectura. Los buenos mensajes de error son esenciales. Envuelve tus pasos de build en scripts que capturen los errores de permisos y los fallos de red, y luego emitan mensajes accionables que expliquen por qu\u00e9 se bloque\u00f3 el acceso y c\u00f3mo solucionar el problema (normalmente a\u00f1adiendo una dependencia al lockfile o cambiando la ruta de salida).<\/p>\n\n<p class=\"wp-block-paragraph\">Considera implementar un despliegue gradual: empieza con un modo de monitorizaci\u00f3n (registra las violaciones pero no bloquea) y luego pasa a la aplicaci\u00f3n. Esto da a los equipos tiempo para actualizar sus configuraciones de build sin romper todos los pipelines a la vez.<\/p>\n\n<h3 class=\"wp-block-heading\">Depuraci\u00f3n<\/h3>\n\n<p class=\"wp-block-paragraph\">Depurar los fallos de build en un entorno restringido es m\u00e1s dif\u00edcil cuando no puedes instalar herramientas adicionales ni alcanzar servicios externos. Proporciona un \u00abmodo de depuraci\u00f3n\u00bb que relaje las restricciones para una ejecuci\u00f3n de pipeline espec\u00edfica y disparada manualmente (nunca para las ejecuciones automatizadas en la rama principal). Registra que se us\u00f3 el modo de depuraci\u00f3n y qui\u00e9n lo dispar\u00f3. Nunca permitas que el modo de depuraci\u00f3n eluda las restricciones en los builds de artefactos de producci\u00f3n.<\/p>\n\n<h2 class=\"wp-block-heading\">Poni\u00e9ndolo todo junto<\/h2>\n\n<p class=\"wp-block-paragraph\">Este es un resumen del enfoque por capas para asegurar los entornos de build CI\/CD:<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Capa 1 \u2014 Restricciones de red:<\/strong> denegar el egress por defecto con listas de permitidos para registros y APIs. Usa NetworkPolicy de Kubernetes, <code>--network=none<\/code> de Docker o reglas de firewall a nivel de host seg\u00fan tu infraestructura de runners.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Capa 2 \u2014 Restricciones del sistema de archivos:<\/strong> sistema de archivos ra\u00edz de solo lectura, tmpfs para las rutas escribibles con l\u00edmites de tama\u00f1o y noexec, c\u00f3digo fuente montado como solo lectura.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Capa 3 \u2014 Builds herm\u00e9ticos:<\/strong> separa la resoluci\u00f3n de dependencias del build. Ejecuta la fase de build con cero acceso de red y solo con entradas descargadas previamente y verificadas por hash.<\/p>\n\n<p class=\"wp-block-paragraph\"><strong>Capa 4 \u2014 Monitorizaci\u00f3n:<\/strong> detecta y alerta sobre las violaciones de pol\u00edtica, las conexiones inesperadas y los intentos de modificaci\u00f3n del sistema de archivos.<\/p>\n\n<p class=\"wp-block-paragraph\">Ninguna capa por s\u00ed sola es suficiente. Las restricciones de red sin controles del sistema de archivos siguen permitiendo la manipulaci\u00f3n de artefactos. Las restricciones del sistema de archivos sin controles de red siguen permitiendo la exfiltraci\u00f3n. Los builds herm\u00e9ticos sin monitorizaci\u00f3n te dejan ciego ante los intentos de ataque. Las capas se refuerzan mutuamente.<\/p>\n\n<h2 class=\"wp-block-heading\">Gu\u00edas relacionadas<\/h2>\n\n<p class=\"wp-block-paragraph\">Para saber m\u00e1s sobre c\u00f3mo asegurar tu pipeline CI\/CD, consulta estas gu\u00edas relacionadas:<\/p>\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/build-integrity-reproducible-builds-ci-cd\/\">Integridad del build y builds reproducibles en CI\/CD<\/a> \u2014 cubre el cumplimiento de SLSA, la verificaci\u00f3n de builds reproducibles y la procedencia de artefactos.<\/li>\n\n\n\n<li><a href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/lab-ephemeral-self-hosted-runners-actions-runner-controller-2\/\">Laboratorio: runners autoalojados ef\u00edmeros con Actions Runner Controller<\/a> \u2014 gu\u00eda pr\u00e1ctica para desplegar ARC en Kubernetes con pods de runner ef\u00edmeros y de un solo uso.<\/li>\n<\/ul>\n\n<p class=\"wp-block-paragraph\">Empieza por las restricciones de red: ofrecen el mayor impacto con el menor esfuerzo de implementaci\u00f3n. Despu\u00e9s, a\u00f1ade las restricciones del sistema de archivos y avanza hacia los builds herm\u00e9ticos a medida que aumenta la madurez de tu pipeline. Cada capa que a\u00f1ades hace que los ataques a la cadena de suministro sean significativamente m\u00e1s dif\u00edciles de ejecutar.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Los pipelines CI\/CD se encuentran entre las cargas de trabajo m\u00e1s privilegiadas de cualquier organizaci\u00f3n. Descargan c\u00f3digo fuente, obtienen dependencias, acceden a secretos y hacen push de artefactos a registros de producci\u00f3n. Sin embargo, en muchos entornos, los procesos de build que hay detr\u00e1s de estos pipelines se ejecutan con acceso de red sin restricciones &#8230; <a title=\"Restricciones de Red y Sistema de Archivos para Entornos de Build CI\/CD\" class=\"read-more\" href=\"https:\/\/secure-pipelines.com\/es\/ci-cd-security\/network-filesystem-restrictions-ci-cd-build-environments\/\" aria-label=\"Leer m\u00e1s sobre Restricciones de Red y Sistema de Archivos para Entornos de Build CI\/CD\">Leer m\u00e1s<\/a><\/p>\n","protected":false},"author":1,"featured_media":1339,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[55,58],"tags":[],"post_folder":[],"class_list":["post-643","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ci-cd-security","category-pipeline-hardening"],"_links":{"self":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/643","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=643"}],"version-history":[{"count":6,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/643\/revisions"}],"predecessor-version":[{"id":1592,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/posts\/643\/revisions\/1592"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media\/1339"}],"wp:attachment":[{"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/media?parent=643"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/categories?post=643"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/tags?post=643"},{"taxonomy":"post_folder","embeddable":true,"href":"https:\/\/secure-pipelines.com\/es\/wp-json\/wp\/v2\/post_folder?post=643"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}