Por defecto, la acción lo deja todo en la ejecución: el resumen del job y report.json. Con send-events: on también envía los destinos de cada job a tu organización de OpenWAF, donde se pueden leer por la API entre repositorios y ejecuciones.
No se guarda ningún secreto en el repositorio. El job demuestra quién es con un token OIDC de GitHub, que OpenWAF cambia por un token que solo le permite enviar los eventos de esa ejecución.
Antes de empezar
- Acceso anticipado. Egress se activa por organización; hasta entonces, el registro responde
403 feature_disabled. Escribe a support@openwaf.dev para entrar. - Un
adminde toda la organización para registrar repositorios. Para ver los repositorios registrados basta con cualquier rol de toda la organización. - Los ids de GitHub del repositorio, que atan el registro a ese repositorio: un repositorio renombrado, transferido o borrado y vuelto a crear con el mismo nombre no es el mismo repositorio y hay que registrarlo de nuevo.
gh api repos/acme/app --jq '.id, .owner.id'1. Registra el repositorio
Un repositorio de GitHub solo puede estar registrado en una organización. refs limita qué refs pueden enviar eventos (refs exactas, sin comodines, hasta 20); vacío quiere decir cualquiera.
curl -sS -X POST "https://dash.openwaf.dev/v1/orgs/$ORG/egress/ci/repositories" \
-H "Authorization: Bearer $OPENWAF_TOKEN" -H "Content-Type: application/json" \
-H "X-Change-Reason: CI egress events for acme/app" \
-d '{"repository": "acme/app", "repository_id": 123456789, "repository_owner_id": 987654, "refs": ["refs/heads/main"]}'Responde 201 con el registro (id, repository, repository_id, repository_owner_id, refs, created_at, created_by). Un GET a la misma ruta los lista en items; DELETE …/egress/ci/repositories/{id} borra uno (204), y los tokens que se le dieron dejan de valer al momento.
openwaf-cloud ctl egress ci-repos add acme/app --repository-id 123456789 --owner-id 987654 \
--ref refs/heads/main --reason "CI egress events for acme/app"
openwaf-cloud ctl egress ci-repos list
openwaf-cloud ctl egress ci-repos rm acme/app --reason "repository archived"--dry-run enseña el cambio sin hacerlo.
Los registros quedan en el registro de auditoría con su motivo.
2. Actívalo en el workflow
permissions:
contents: read
id-token: write # permite al job pedir un token OIDC a GitHub
jobs:
test:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v5
- uses: openwaf-dev/egress-action@v0.3.2
with:
send-events: "on"
- run: make testAl final del job, el resumen dice cuántos destinos se enviaron, por ejemplo 14 destinations sent to OpenWAF Egress events (https://dash.openwaf.dev).
Qué se envía
Un evento por destino del job, con la identidad de la ejecución. Un nombre que solo se resolvió, sin nada conectado, rechazado ni detectado, queda fuera.
| Parte | Campos |
|---|---|
| Ejecución | Repositorio, id e intento de la ejecución, workflow, job, SHA del commit, ref, nombre del evento, nombre del runner |
| Resumen | Modo, número de destinos, bloqueados, que se bloquearían, detecciones, consultas DNS |
| Cada destino | Host, primer puerto, vía (proxy, direct, container, udp, dns), nombre del contenedor, decisión (allow, audit, block), motivo, ids de detección, conexiones, bytes de subida y de bajada |
Nunca cuerpos de peticiones, cabeceras, URLs ni los secretos que encontró una detección.
Cómo funciona
- El paso posterior pide a GitHub un token OIDC con la audiencia
openwaf-egress-ci. Nunca lo imprime ni lo registra. - Lo envía a
POST /v1/egress/ci/tokenenopenwaf-api(por defectohttps://dash.openwaf.dev). OpenWAF comprueba la firma de GitHub, el emisor, la audiencia, la hora y que el token no se haya usado antes, y que el repositorio, su id y el id de su propietario coinciden con un registro (y la ref, cuandorefsestá puesto). Responde con un token (OWE_CI_…) válido 15 minutos y solo para ese repositorio. - Envía los eventos a
POST /v1/egress/ci/eventsen lotes de hasta 1000, con hasta tres intentos por lote. Cada lote lleva unbatch_id, así que un reintento nunca cuenta dos veces.
Un fallo nunca hace fallar el job: el resumen y el log reciben un aviso como Events not sent to OpenWAF: token exchange: 403 repository_not_registered.
| Error | Qué significa y cómo se arregla |
|---|---|
GitHub OIDC token: … (the job needs permissions: id-token: write) | Añade id-token: write a los permissions del workflow o del job. Las pull requests de forks nunca lo tienen. |
401 invalid_token | Se rechazó el token OIDC (firma, emisor, audiencia, hora o usado dos veces); o, en los eventos, caducó el token de OpenWAF. |
403 repository_not_registered | Registra el repositorio; de nuevo si se renombró, se transfirió o se volvió a crear. |
403 ref_not_allowed | La ref de la ejecución no está en los refs del registro. |
403 feature_disabled | Egress no está activado para la organización. |
403 repository_mismatch | El repositorio de la ejecución no es el del token. |
429 rate_limited, 503 unavailable | Inténtalo más tarde; el job no se ve afectado. |
Ver los eventos
Los eventos de CI se leen por la API. La vista Peticiones bloqueadas del panel y la herramienta MCP egress_events solo muestran las llamadas de tus agentes de IA que rechazó la pasarela, no los eventos de CI.
curl -sS "https://dash.openwaf.dev/v1/orgs/$ORG/egress/events?attack_type=egress_ci&limit=100" \
-H "Authorization: Bearer $OPENWAF_TOKEN"attack_type=egress_ci selecciona los eventos de CI; …/egress/events/stream admite el mismo filtro y sigue los nuevos como Server-Sent Events. En cada evento:
| Campo | Valor |
|---|---|
action | allow, log (audit: enforce lo habría rechazado) o block |
rule_name | El motivo, como not_allowed o dlp |
host | El destino (e ip cuando el destino es una dirección) |
method | La vía: proxy, direct, container, udp o dns |
path | Puerto, contenedor y contadores, como :443 container=db connections=3 bytes_up=812 bytes_down=6200 |
user_agent | ci:<propietario>/<repo>/<workflow>/<job>#<id de ejecución> |
msg | Los ids de detección |