Skip to content

v0.2.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 11:54
· 4 commits to main since this release

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.