Skip to content

Releases: openwaf-dev/egress

v0.2.2

Choose a tag to compare

@github-actions github-actions released this 28 Oct 15:42
31483b9

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 an Authorization
    header with any other scheme, or a bare token without Bearer, 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 401 carries WWW-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_url is not an absolute
    http(s) URL with a host, a target has no host, or an agent's allowed_targets names 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

Choose a tag to compare

@github-actions github-actions released this 27 Oct 12:10

Fixes

  • /health reports the build version (egress -version) instead of a fixed 0.1.0. Library users
    set it with egress.WithVersion; unset, it reports dev.

Image: ghcr.io/openwaf-dev/egress:0.2.1 (linux/amd64, linux/arm64). Binaries with SHA256SUMS
attached.

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:54

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> (or OPENWAF_CONTROL_PLANE, OPENWAF_NODE_ID,
    OPENWAF_NODE_KEY; -poll-interval, 15 s by default). egress keygen -out node.key writes 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 with since_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 get 503 policy_unavailable and
    /health answers 503 {"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-Agent is ignored and the caller's Authorization is
    never forwarded.
  • Provider keys: the gateway derives the KV v2 path itself (<kv_mount>/data/tenants/<org>/egress/<target>,
    key api_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 like 0.0.0.0), and neither ssrf_blocked nor 502 upstream_error answers
    carry addresses or connection errors any more (they are logged on the gateway).

  • token_sha256 in an agent of config.json instead of token (the plain token is then not on disk); tokens
    are compared in constant time.

  • -trusted-proxies (OPENWAF_TRUSTED_PROXIES): behind a reverse proxy, its X-Real-IP is the caller address of
    block events.

  • Go API: Resolver (per-request policy lookup), EventSink / WithEventSink, WithTrustedProxies,
    NewServerWithResolver, and the pkg/controlplane client.

  • 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 -version

Apache-2.0. Vulnerabilities: security@openwaf.dev.

v0.1.1

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:07

Fixes

  • The agent's placeholder token is no longer forwarded to the provider. For targets whose key
    goes in another header (x-api-key for Anthropic, x-goog-api-key for 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 (OpenAI input_audio, Gemini inlineData / inline_data,
    Anthropic base64 image and document sources) are left out of the 1 MiB inspection window,
    like data:image/…;base64,… URIs already were: a voice note or a photo sent in these formats
    got 413 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 with 502) 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

Choose a tag to compare

@github-actions github-actions released this 27 Oct 10:52

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 (the 3xx goes 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 gets 413 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 -version

Binaries 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.