Repository navigation
Releases: openwaf-dev/egress
Release list
v0.2.2
Security hardening
- Bearer scheme (RFC 7235). The agent token is read from
Authorization: Bearer <token>; the scheme
is case-insensitive and one or more spaces may follow it. In config-file mode anAuthorization
header with any other scheme, or a bare token withoutBearer, now answers
401 {"error":"invalid_scheme"}instead of being compared with the agent tokens.
Breaking for a client that sent the bare token (Authorization: OWE_…): send
Authorization: Bearer OWE_…(every OpenAI/OpenRouter SDK already does). The control-plane mode is
unchanged (invalid_token). - Every
401carriesWWW-Authenticate: Bearer. - SSRF: numeric IP spellings Go does not parse as an IP (
2130706433,0x7f000001,127.1,
0177.0.0.1,127.0.0.01) are refused before any DNS lookup (403 ssrf_blocked). Hex-dotted
spellings (0x7f.1) still go to the resolver; the dial-time check refuses a private result. - Fail-fast configuration: the gateway does not start when a target's
base_urlis not an absolute
http(s)URL with a host, a target has nohost, or an agent'sallowed_targetsnames a target that
does not exist (checked only when the file defines targets). The error names the target or agent.
The error codes are listed in the README.
Image: ghcr.io/openwaf-dev/egress:0.2.2 (linux/amd64, linux/arm64). Binaries with SHA256SUMS
attached.
v0.2.1
Fixes
/healthreports the build version (egress -version) instead of a fixed0.1.0. Library users
set it withegress.WithVersion; unset, it reportsdev.
Image: ghcr.io/openwaf-dev/egress:0.2.1 (linux/amd64, linux/arm64). Binaries with SHA256SUMS
attached.
v0.2.0
OpenWAF Egress 0.2.0 adds a multi-tenant mode: the gateway can take its agents, targets and policies from
OpenWAF Cloud instead of config.json and report every refused request back. The
static config.json mode is unchanged.
OpenWAF Cloud data-plane mode
-control-plane <url> -node-id <id> -node-key <file>(orOPENWAF_CONTROL_PLANE,OPENWAF_NODE_ID,
OPENWAF_NODE_KEY;-poll-interval, 15 s by default).egress keygen -out node.keywrites a new ed25519
PKCS#8 key (mode 0600, never overwrites) and prints the public key the operator registers.- Every request to the control plane is signed with the node's own key (
openwaf-node-request/v1: timestamp,
single-use nonce, body hash). The configuration is polled withsince_serial(304 when unchanged). The last
good document stays in force while the control plane is unreachable, for at most-max-stale
(OPENWAF_MAX_STALE, 10 min by default); a 401/403 from the control plane (node revoked) withdraws it at once,
and an older serial is never applied. Without a valid document requests get503 policy_unavailableand
/healthanswers503 {"status":"starting"}(no tenant totals are published in this mode). Redirects from the
control plane are never followed. - Agents are identified by the SHA-256 of their token (constant-time comparison) and must call
/v1/agent/<their name>/proxy/<target>/…;X-OpenWAF-Agentis ignored and the caller'sAuthorizationis
never forwarded. - Provider keys: the gateway derives the KV v2 path itself (
<kv_mount>/data/tenants/<org>/egress/<target>,
keyapi_key); a path never comes from the document. A target without a stored key is refused
(502 credential_resolution_failed) instead of being called without one. - Block events (
injection_detected,data_leak_blocked,ssrf_blocked,egress_blocked,
body_too_large) of identified agents go to the control plane in batches of up to 1000, retried with the same
batch_id, from a bounded queue. They carry the detector ids or DLP match types, method, path without query and
caller address; never the request body. Method and path are normalized, and an event the control plane would
refuse is dropped alone, so one bad event never costs a batch. Protocol:
docs/control-plane.md.
Other changes
-
SSRF: the address actually dialed is checked too (DNS rebinding between the lookup and the connection gets
403 ssrf_blocked;::is refused like0.0.0.0), and neitherssrf_blockednor502 upstream_erroranswers
carry addresses or connection errors any more (they are logged on the gateway). -
token_sha256in an agent ofconfig.jsoninstead oftoken(the plain token is then not on disk); tokens
are compared in constant time. -
-trusted-proxies(OPENWAF_TRUSTED_PROXIES): behind a reverse proxy, itsX-Real-IPis the caller address of
block events. -
Go API:
Resolver(per-request policy lookup),EventSink/WithEventSink,WithTrustedProxies,
NewServerWithResolver, and thepkg/controlplaneclient. -
CI scans the whole history for secrets (gitleaks) before testing and publishing.
Install
docker pull ghcr.io/openwaf-dev/egress:0.2.0 # linux/amd64, linux/arm64
egress -versionApache-2.0. Vulnerabilities: security@openwaf.dev.
v0.1.1
Fixes
- The agent's placeholder token is no longer forwarded to the provider. For targets whose key
goes in another header (x-api-keyfor Anthropic,x-goog-api-keyfor Gemini), the agent's
Authorization: Bearer OWE_…was copied to the upstream request next to the real key. It is
now always removed. - Audio and native inline media no longer count as inspected text. JSON
"data"members of
at least 1 KiB of bare base64 (OpenAIinput_audio, GeminiinlineData/inline_data,
Anthropic base64 image and document sources) are left out of the 1 MiB inspection window,
likedata:image/…;base64,…URIs already were: a voice note or a photo sent in these formats
got413 body_too_large. Text beside them is still inspected. One linear pass: 1.6 ms for a
2 MiB body (Apple Silicon). - Longer upstream timeouts. 5 min for the provider's response headers (was 60 s, which cut
non-streaming reasoning and image generations with502) and 15 min for a whole exchange,
streamed body included (was 5 min, also the server write timeout).
Docs
- Gemini target example (
x-goog-api-key); keys in the query string (?key=) are not injected.
Image: ghcr.io/openwaf-dev/egress:0.1.1 (linux/amd64, linux/arm64). Binaries with SHA256SUMS
attached.
v0.1.0
First public release of OpenWAF Egress, a gateway between AI agents and model providers. Agents
hold a placeholder token; the gateway checks each request and only then reads the real key from
OpenBao or HashiCorp Vault and sets it on the outgoing request.
What is in it
- Credential injection: per-agent
OWE_TOKEN_…placeholders, real keys read from KV v2
(AppRole login, 5 min cache) per agent and target, never written to the agent's environment. - Target allowlist per agent; every other destination gets
403 egress_blocked. - Prompt injection: 27 deterministic detectors in English and Spanish (21 pattern detectors,
6 for hidden or encoded text). No model call.403 injection_detected. - Data leaks: Luhn-valid card numbers, PEM private keys, AWS access key IDs,
sk-keys and
GitHub tokens.403 data_leak_blocked. - SSRF: the target must resolve only to public addresses, and upstream redirects are never
followed (the3xxgoes back to the agent without the credential). - No partial inspection: inline base64 media is not inspected text; the rest must fit the
1 MiB window or the request gets413 body_too_large(bodies capped at 8 MiB). - Streaming responses pass through unchanged.
Performance
Measured on Apple Silicon (darwin/arm64): 84 µs to inspect a 4 KB chat request, 1.3 ms for
64 KB; worst case (every detector keyword present) about 97 ms per 64 KB.
Install
docker pull ghcr.io/openwaf-dev/egress:0.1.0 # linux/amd64, linux/arm64
egress -versionBinaries for Linux and macOS (amd64, arm64) are attached with SHA256SUMS.
Limits
- Plain HTTP with static agent tokens: run it on loopback or a private network, or put TLS in front.
- Detection is pattern matching: it stops known injection and exfiltration shapes, it does not
prove a request is safe.
Apache-2.0. Vulnerabilities: security@openwaf.dev.