Egress CI hace visible el tráfico de salida de un job y lo limita a una lista. Encarece la exfiltración por parte de una dependencia, un script de build o una acción comprometidos, y enseña el intento en el resumen. No es un sandbox: lee lo que no cubre antes de confiar en él.
Cobertura
| Tráfico | Cubierto | Cómo |
|---|---|---|
Herramientas que respetan HTTPS_PROXY/HTTP_PROXY | Sí | El proxy: se comprueban el nombre y el SNI |
| Herramientas que ignoran las variables de proxy, el propio runner | Con el bloqueo | El proxy transparente: nombre por SNI o Host, comprobado contra la dirección |
| DNS | Con el bloqueo | El forwarder: registrado, NXDOMAIN para los nombres fuera de la política en enforce |
| UDP, ICMP, IPv6 | Con el bloqueo | Solo hacia direcciones de nombres permitidos (IPv4); el resto se rechaza en enforce |
Los contenedores del job y los pasos de docker build | Con el seguimiento de Docker | Igual que el job, por contenedor |
| Pulls de imágenes por el socket del job | Con el seguimiento de Docker | Se comprueba el registry |
| Pasos anteriores a la acción | No |
Límites conocidos
- Root puede deshacerlo. El job tiene el mismo root que usa la acción: en los runners de GitHub todos los jobs tienen
sudo, y en los self-hosted el grupo docker equivale a root. Un paso que se proponga quitar el bloqueo puede hacerlo. - Solo los pasos posteriores a la acción. Ponla justo después de
actions/checkout. - El contenido HTTPS no se ve salvo que el host esté en
inspect, y la inspección exige que la herramienta confíe en la CA de la ejecución. Un secreto enviado por HTTPS a un host permitido no se detecta. - Los hosts permitidos pueden recibir datos. Un registry de paquetes o un almacén de objetos que permitas puede recibir una subida. Permite nombres exactos y deja fuera las credenciales de escritura de los jobs que no las necesitan.
- Docker sin
DOCKER_HOST. Una herramienta que abre/var/run/docker.sockpor su cuenta no se atribuye; conlock-all, el tráfico de sus contenedores fuera de la política se rechaza igualmente. Los contenedores con--network hostquedan fuera del bloqueo salvo que se ejecuten con el usuario del job. - Las conexiones propias del daemon de Docker (los push, las descargas de capas de un pull cuyo registry está permitido) no pasan por el proxy.
- El rango de direcciones de Docker (
172.16.0.0/12) queda a las reglas del propio runner y está enNO_PROXY. - Solo IPv4 para poner nombre a las conexiones TCP; el IPv6 del job se rechaza en enforce.
- Sin root (un runner self-hosted sin
sudosin contraseña ni Docker), solo se cubreHTTPS_PROXY.lock: "on"lo convierte en un error. - Solo Linux, x64 y arm64. Sin runners macOS ni Windows.
- Un job por usuario del runner a la vez en un host.
Lo que cuesta
La acción arranca el sidecar antes de los pasos del job y lo para en el paso posterior; la línea del log OpenWAF Egress CI: proxy …, started in … ms y el resumen dan los tiempos de cada ejecución. Las conexiones por los proxies añaden un salto local. Con el seguimiento de Docker, docker build se ejecuta en el builder del job en vez de en el del daemon; en los runners que empiezan limpios en cada job su caché empieza vacía.
Modelo de seguridad
- El sidecar se ejecuta como root y los pasos del job como el usuario del runner; el bloqueo solo afecta a los sockets de ese usuario y a las redes Docker del job.
- Los binarios están en
dist/de la acción, construidos de forma reproducible desde su código (la CI lo comprueba); sus SHA-256 están endist/SHA256SUMSy se comprueban antes de cada ejecución. - Los tokens nunca van en una línea de órdenes ni en un log: el token de GitHub le llega a
curlpor la entrada estándar y los tokens OIDC se quedan en la memoria del paso posterior. - La clave de la CA de la ejecución nunca sale de la memoria del sidecar.
- El resumen y
report.jsoncontienen nombres, puertos, contadores e ids de detección, nunca cuerpos ni los secretos encontrados.
Las vulnerabilidades se informan a security@openwaf.dev, como explica el SECURITY.md (enlace externo) del repositorio.