Con containers: track (el valor por defecto) y root accesible con sudo o Docker, el Docker del job pasa por un socket que sirve el sidecar. La acción apunta DOCKER_HOST a él, así que docker, docker compose, Testcontainers y cualquier otro cliente que lea DOCKER_HOST lo usan sin cambios.
Qué hace el socket del job
- Etiquetas. Los contenedores, redes y volúmenes creados por él llevan la etiqueta
org.openwaf.egress-ci.uidcon el id del usuario del job. - Una red para el job. El sidecar crea una red bridge para el job. Un contenedor que pide el bridge por defecto de Docker recibe esa red; un contenedor que nombra su propia red (como hace Compose) la mantiene.
- Cubierto desde el primer paquete. Cada red creada por el socket entra en el bloqueo por subred en cuanto existe, antes de que se le una ningún contenedor. El DNS de un contenedor va al forwarder del sidecar y su TCP al proxy transparente, igual que los del propio job; el resto se rechaza en enforce. El tráfico entre los contenedores del job en esas redes no se restringe.
- Atribuido. La columna Via del resumen dice
containery da el nombre del contenedor. - El daemon no cambia. Cada llamada se reenvía al daemon que el runner dio al job (su
DOCKER_HOST, o/var/run/docker.sock).
docker build
La acción crea un builder del job (BuildKit en un contenedor, driver docker-container) por el socket del job y le apunta BUILDX_BUILDER. docker build y docker buildx build lo usan y cargan el resultado en el daemon, así que docker run de la imagen construida funciona como antes.
Como el builder es uno de los contenedores del job, cada paso RUN cumple la política: un RUN curl a un nombre fuera de ella se rechaza en enforce. El builder propio del daemon ejecuta los pasos en sandboxes que no se pueden distinguir por job, y por eso la acción no lo usa.
El estado de BuildKit del builder (la caché de imágenes base y capas) se conserva para el siguiente job del mismo usuario del runner, recortado a 4 GB. En los runners de GitHub cada job estrena máquina, así que empieza vacío cada vez.
La imagen del builder, moby/buildkit, viene de Docker Hub en el primer build: Docker Hub tiene que estar permitido (ver abajo).
Pulls de imágenes
Un pull por el socket del job (docker pull, docker run de una imagen que falta, Compose) se comprueba por registry antes de que el daemon descargue nada. ghcr.io/acme/app:1 se comprueba contra ghcr.io; una imagen de Docker Hub (alpine:3, docker.io/library/alpine), contra registry-1.docker.io. Un registry fuera de allow se rechaza en enforce con un 403 y el motivo registry_not_allowed. El resumen lista cada pull con su decisión.
docker pull y docker run descargan las capas a través del daemon, cuyas conexiones no pasan por el proxy: a ellas solo se les aplica la comprobación del registry. El builder del job descarga las imágenes base por su cuenta, desde dentro del bloqueo, así que esas descargas también tienen que estar permitidas. Para Docker Hub, permite:
allow:
- auth.docker.io # tokens
- registry-1.docker.io # registry (cada pull de docker.io se comprueba contra este nombre)
- production.cloudflare.docker.com # capas
- production.cloudfront.docker.comEjemplo
jobs:
build:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: openwaf-dev/egress-action@v0.3.2
with:
containers: lock-all
- run: docker build -t app:ci .
- run: docker compose up --abort-on-container-exit --exit-code-from testscontainers: track, lock-all u off
| Valor | Cuándo usarlo |
|---|---|
track | Por defecto. Los contenedores del propio job siguen su política. Los de otros jobs del mismo host solo se observan (aparecen en «flows from containers that are not this job's»). |
lock-all | El runner ejecuta un job cada vez: runners de GitHub, runners self-hosted efímeros. En enforce rechaza además el tráfico fuera de la política de cualquier otro contenedor, lo que cubre el Docker usado sin DOCKER_HOST. Nunca en un host que ejecuta varios jobs a la vez: rechazaría los contenedores de los demás jobs. |
off | Docker queda sin tocar: sin socket, sin builder y sin cobertura de contenedores. |
El seguimiento de Docker necesita root (el bloqueo), el socket del daemon y el bridge docker0. Sin ellos la acción sigue con Docker sin tocar y el resumen dice Docker off; en enforce, un socket que no arranca hace fallar el paso.
Qué no se cubre
- Una herramienta que abre
/var/run/docker.sockpor su cuenta en vez de leerDOCKER_HOST: sus contenedores no se atribuyen; conlock-all, su tráfico fuera de la política se rechaza igualmente. - Los contenedores arrancados con
--network hostquedan fuera del bloqueo salvo que se ejecuten con el usuario del job. - Las conexiones propias del daemon (los push y las descargas de un pull cuyo registry está permitido) no pasan por el proxy.
- Los destinos del rango de direcciones de Docker (
172.16.0.0/12) quedan a las reglas del runner.