# Docker y builds

> Cómo Egress CI aplica a los contenedores del job, docker compose, docker build y los pulls de imágenes la misma lista de permitidos que al job.

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.uid` con 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 `container` y 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:

```yaml
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.com
```

## Ejemplo

```yaml
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 tests
```

## containers: 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.sock` por su cuenta en vez de leer `DOCKER_HOST`: sus contenedores no se atribuyen; con `lock-all`, su tráfico fuera de la política se rechaza igualmente.
- Los contenedores arrancados con `--network host` quedan 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.
