# Referencia de egress.yml y de las entradas

> Cada clave del fichero de política de Egress CI, cada entrada y salida de la acción, las variables que deja al job y el informe que escribe.

## egress.yml

La política es un fichero YAML del repositorio, `.github/egress.yml` salvo que la entrada `policy` indique otro.

```yaml
version: 1
mode: enforce
presets: [github-actions]
allow:
  - proxy.golang.org
  - "*.npmjs.org"
  - registry.example.com:5000
  - ci-cache.example.com:*
inspect:
  - api.staging.example.com
```

| Clave | Por defecto | Significado |
|---|---|---|
| `version` | `1` | Versión del formato. Solo existe la `1`. |
| `mode` | `audit` | `audit`: todo pasa y se informa de lo que enforce rechazaría. `enforce`: se rechaza lo que no está en `allow`. La entrada `mode` manda sobre ella. |
| `presets` | ninguno | Listas con nombre que se mantienen con la acción. La única es `github-actions`. |
| `allow` | ninguno | Destinos a los que pueden llegar el job y sus contenedores. |
| `inspect` | ninguno | Hosts cuyo TLS termina el proxy para revisar los cuerpos de las peticiones en busca de secretos. Ver [Inspección y DLP](https://developers.openwaf.dev/es/egress/ci/inspect-and-dlp/). |

Una clave desconocida es un error: una errata como `alow:` hace fallar el paso de la acción en vez de no permitir nada sin avisar. La acción comprueba el fichero antes de arrancar nada.

### Reglas de allow

| Regla | Coincide con |
|---|---|
| `proxy.golang.org` | Ese nombre, puertos 80 y 443 |
| `*.npmjs.org` | Cualquier nombre por debajo de `npmjs.org` (`registry.npmjs.org`, `a.b.npmjs.org`), no `npmjs.org` en sí; puertos 80 y 443 |
| `registry.example.com:5000` | Ese nombre, solo el puerto 5000 |
| `registry.example.com:80,5000` | Ese nombre, puertos 80 y 5000 |
| `ci-cache.example.com:*` | Ese nombre, cualquier puerto |
| `203.0.113.10` | Esa dirección IPv4, puertos 80 y 443 |

Los nombres no distinguen mayúsculas y el punto final se ignora. El comodín solo vale como primera etiqueta (`*.example.com`).

El job recibe las respuestas DNS de un nombre permitido; el bloqueo solo deja pasar los flujos que no son TCP (UDP como QUIC) hacia las direcciones de esas respuestas. Los [pulls de imágenes](https://developers.openwaf.dev/es/egress/ci/docker/#image-pulls) se comprueban contra el nombre del registry en cualquier puerto.

### El preset github-actions

`github-actions` es la lista que GitHub publica para los runners de Actions en el campo `domains.actions` de [`api.github.com/meta`](https://api.github.com/meta): el servicio del runner, el checkout, la API, la caché de Actions, los artefactos y los logs, con cada cuenta de almacenamiento de Azure por su nombre exacto (nunca `*.blob.core.windows.net`, donde cualquiera puede crear una cuenta). La acción añade `raw.githubusercontent.com`, donde las acciones `setup-*` leen sus manifiestos de versiones.

La acción descarga la lista al empezar con la entrada `token` y la guarda 6 horas en `~/.cache/egress-ci/` del usuario del runner. Si la descarga falla, usa una copia incluida en la acción. El resumen del job dice cuál se usó.

## Entradas

| Entrada | Por defecto | Significado |
|---|---|---|
| `policy` | `.github/egress.yml` | Ruta del fichero de política. |
| `mode` | vacío | `audit` o `enforce`; manda sobre el `mode` del fichero. Sirve para probar un fichero en audit en las pull requests y aplicarlo en `main`. |
| `lock` | `auto` | El bloqueo nftables para las conexiones que ignoran el proxy. `auto`: activo si el job llega a root con `sudo` o Docker, inactivo si no. `on`: como `auto`, pero el paso falla si no se llega a root. `off`: solo se usa `HTTPS_PROXY`. En enforce, un bloqueo que no se puede instalar hace fallar el paso. |
| `containers` | `track` | `track`: el Docker del job pasa por su propio socket y sus contenedores y builds cumplen la política. `lock-all`: además rechaza en enforce el tráfico fuera de la política de cualquier otro contenedor (runners que ejecutan un job cada vez). `off`: Docker se deja como está. Ver [Docker y builds](https://developers.openwaf.dev/es/egress/ci/docker/). |
| `no-proxy` | vacío | Entradas extra de `NO_PROXY`, separadas por comas, para hosts a los que las herramientas con proxy deben ir directas. Con el bloqueo activo, esas conexiones pasan igualmente por el proxy transparente y por la política. |
| `token` | `${{ github.token }}` | Token con el que se lee `api.github.com/meta`. Se le pasa a `curl` por la entrada estándar, nunca en la línea de órdenes. |
| `send-events` | `off` | `on`: envía los destinos, bloqueos y detecciones del job a los eventos de Egress de tu organización. Ver [Enviar los eventos a OpenWAF](https://developers.openwaf.dev/es/egress/ci/send-events/). |
| `openwaf-api` | `https://dash.openwaf.dev` | API de OpenWAF que usa `send-events`. Tiene que ser `https://`. |

| Salida | Significado |
|---|---|
| `proxy` | La URL del proxy, `http://127.0.0.1:PUERTO`. |

## Lo que la acción deja al job

| Variable | Valor |
|---|---|
| `HTTPS_PROXY`, `HTTP_PROXY`, `https_proxy`, `http_proxy` | La URL del proxy. |
| `NO_PROXY`, `no_proxy` | `localhost,127.0.0.1,::1,172.16.0.0/12,.local,.localhost,.test`, tu `no-proxy` y `.blob.core.windows.net` cuando el bloqueo está activo (la caché de Actions pasa entonces por el proxy transparente, limitada igualmente a las cuentas exactas de GitHub). |
| `EGRESS_CI_STATE` | El directorio de estado del sidecar, `$RUNNER_TEMP/egress-ci` (ahí está el registro de eventos, `events.jsonl`). |
| `DOCKER_HOST`, `BUILDX_BUILDER` | Con el seguimiento de Docker: el socket de Docker del job y el builder del job. |
| `NODE_EXTRA_CA_CERTS`, `SSL_CERT_FILE`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `GIT_SSL_CAINFO` | Con `inspect`: la CA de la ejecución, sola o añadida al bundle del sistema. |

El sidecar escucha en `127.0.0.1` (y en la dirección del bridge de Docker para los contenedores) en tres puertos por debajo del rango efímero, fijos por usuario del runner: el proxy, el forwarder DNS (el puerto siguiente) y el proxy transparente (el de después).

## Decisiones y motivos

Cada conexión, consulta DNS y pull de imagen recibe una decisión: `allow`, `audit` (fuera de la política, se deja pasar porque el modo es audit) o `block`. Una rechazada lleva un motivo:

| Motivo | Por qué |
|---|---|
| `not_allowed` | El nombre, o el puerto para ese nombre, no está en `allow`. |
| `sni_mismatch` | Un `CONNECT` a un nombre permitido llevaba un SNI de TLS de un nombre fuera de la política. |
| `name_ip_mismatch` | Una conexión nombraba un host permitido pero iba a una dirección que ese nombre no resuelve (domain fronting). |
| `dlp` | La petición llevaba un secreto (ver [Inspección y DLP](https://developers.openwaf.dev/es/egress/ci/inspect-and-dlp/)). |
| `registry_not_allowed` | Un pull de imagen de un registry fuera de la política. |
| `private_destination` | Loopback, link-local (como la dirección de metadatos de la nube) o multicast, rechazados en los dos modos. |

En enforce, el proxy responde `403` con la cabecera `X-OpenWAF-Egress: blocked` (o `dlp`), el DNS responde `NXDOMAIN`, el socket de Docker responde `403` y el resto de conexiones recibe un reset.

## El informe

El paso posterior añade el informe al resumen del job e imprime `report.json` en un grupo del log llamado `egress-ci report.json`. Contiene nombres, puertos, contadores e ids de detección, nunca cuerpos de peticiones.

| Campo de report.json | Significado |
|---|---|
| `meta` | Versión, modo, fichero de política y su SHA-256, estado del bloqueo y de Docker, de dónde salió la lista de GitHub, horas de inicio y fin, los contenedores del job. |
| `destinations` | Una entrada por host: `ports`, `via`, `containers`, `connections`, `bytes_up`, `bytes_down`, `allowed`, `blocked`, `audited`, `dns_refused`, `reasons`, `detections`. |
| `blocked`, `would_block`, `dns_refused`, `detections`, `dns_queries` | Totales. |
| `suggested_allow` | Nombres fuera de la política, listos para `allow`. |
| `image_pulls` | Pulls por el socket del job, con su decisión. |
| `other_containers` | Flujos de contenedores del runner que no son de este job. |
| `unattributed_addresses` | Flujos que no son TCP hacia direcciones que ninguna respuesta DNS nombró. |
