Skip to content

Latest commit

 

History

History
223 lines (173 loc) · 9.5 KB

File metadata and controls

223 lines (173 loc) · 9.5 KB

Wasm Policy Example Parity

Status: acceptance plan for the 1.7 Wasm line. These examples are not all implemented in 1.7.0; they are required outcomes before the 1.7 line is considered complete.

Fluxheim is not trying to embed Tcl, Lua, SPOE, or VCL interpreters. The goal is capability parity through a small typed Wasm ABI with explicit resource limits, redaction, and deterministic failure behavior.

Required Example Families

Every family below must have:

  • a documented operator-facing example;
  • a real Wasm plugin fixture checked into the repository;
  • a Fluxheim config fixture that attaches the plugin to a vhost or route;
  • a live smoke script that starts Fluxheim, sends real HTTP traffic, and validates the observable result;
  • unit tests for ABI encoding/decoding and rejection paths;
  • release-gate coverage through scripts/test_starter.py.

F5 iRules-Style Policy

Capability target:

  • conditional allow, deny, or continue decisions;
  • bounded synthetic deny/error response;
  • simple route classification from host, path, method, trusted client IP, TLS facts, and selected runtime facts;
  • no direct upstream address control and no direct TLS verification control.

Representative example:

If the request path starts with /admin and the restored client IP is outside
the allowed CIDR set, return 403 with a small synthetic response. Otherwise
continue to the configured route.

Required live test:

  • allowed client receives the origin response;
  • denied client receives the plugin's 403 response;
  • plugin timeout/trap follows the configured fail mode;
  • the plugin cannot read request bodies, filesystem paths, admin tokens, or upstream TLS secrets.

Target release: v1.7.1.

Status: the checked-in examples/wasm/irules-access-policy.wat and examples/wasm/irules-access-policy.toml pair packages the bounded access-decision subset as a runnable v1.7.9 migration example. Fluxheim owns host/path/method/trusted-client/TLS classification; the route-scoped plugin can continue, allow, or deny and cannot inspect arbitrary request state. The live listener smoke proves an unattached public request reaches origin, the attached admin request receives the plugin's 403 before origin dispatch, and a trapping plugin fails closed.

nginx Lua/OpenResty-Style Header Policy

Capability target:

  • request header mutation before upstream selection;
  • response header mutation before the response is sent;
  • bounded header count, name, and value lengths;
  • explicit redaction rules for sensitive headers.

Representative example:

Add x-policy-tier based on the request path, remove x-powered-by from the
response, and attach a short response header that identifies the selected
policy branch.

Required live test:

  • origin observes the plugin-added request header;
  • client observes the plugin-added response header;
  • oversized or forbidden header mutations are rejected;
  • Authorization, Cookie, Set-Cookie, private keys, and admin credentials are not exposed to the plugin unless a future explicit capability allows it.

Target release: v1.7.2.

Status: implemented in v1.7.2 for the bounded preview ABI. The live listener coverage is in crates/fluxheim-server/src/native_http1_route_proxy_tests/wasm.rs and proves x-policy-tier request mutation, x-fluxheim-policy-branch response mutation, x-powered-by response removal, and fail-closed rejection of forbidden header mutation IDs. The ABI remains symbolic and does not expose raw sensitive headers or bodies.

The v1.7.9 operator example is checked in as examples/wasm/openresty-header-policy.wat with a complete config fixture. The shared policy-example smoke compiles that exact source and proves the origin receives x-policy-tier: gold, the client receives x-fluxheim-policy-branch: gold, upstream x-powered-by is removed, and an unknown header mutation ID fails closed before origin dispatch.

HAProxy Lua/SPOE-Style Routing And Load-Balancer Policy

Capability target:

  • bounded typed decisions for pool choice, persistence-key choice, mirror/shadow enablement, and deny/pass/continue outcomes;
  • no arbitrary network I/O from the plugin;
  • all decisions constrained to configured routes, pools, and mirrors.

Representative example:

Choose a canary pool for requests carrying x-canary: 1, derive a persistence
key from a bounded request header, and mirror only safe GET requests to a
configured shadow route.

Required live test:

  • two local origins prove the selected pool is honored;
  • persistence key keeps repeated requests pinned as configured;
  • mirror/shadow decisions never recurse and only target configured mirrors;
  • a plugin trying to choose an unknown pool is rejected deterministically.

Target release: v1.7.3.

Status: first subset implemented in v1.7.3 for native HTTP/1 route-decision hooks. The current live test uses two local origins and a configured canary route branch selected by a bounded x-canary: 1 signal. The hook can continue, deny, or select a configured matching branch; arbitrary pool names, persistence keys, and mirror/shadow target decisions remain staged for later 1.7.x slices.

The v1.7.9 runnable mapping is checked in as examples/wasm/haproxy-spoe-routing-policy.wat with a complete config fixture. The shared live smoke compiles that source and proves configured canary route selection, unavailable-branch fail-closed behavior, selected-route policy enforcement, native load-balancer selection, managed-cookie persistence, and safe configured mirror routing. Backend addresses, arbitrary persistence keys, and mirror URLs remain outside the guest ABI.

VCL-Like Cache Policy

Capability target:

  • lookup/admission decision: bypass, pass, continue, or deny;
  • bounded cache-key component output with low-cardinality enforcement;
  • store-admission decision for TTL override, cache tags, safe response-header mutation, and rejection of unsafe responses;
  • no raw cache-object access.

Representative example:

Bypass cache for preview=true, add a bounded device-class key component,
override TTL for image responses up to the configured maximum, and tag objects
with a small plugin-provided cache tag.

Required live test:

  • preview requests bypass cache;
  • normal requests produce MISS then HIT;
  • device-class key component changes the cache key only within configured cardinality bounds;
  • plugin TTL override is clamped to route/vhost cache limits;
  • plugin cache tags are visible to purge/status tooling;
  • unsafe Set-Cookie or private responses remain uncacheable.

Target line: starts in v1.7.4 with cache lookup/pass/bypass/deny decisions and cache-store skip/deny decisions. v1.7.5 adds bounded symbolic cache-key components with live low-cardinality variant tests plus fixed-ID TTL/tag/header store metadata and symbolic response content-type inspection with live TTL expiry and stored-header HIT coverage. The checked-in examples/wasm/cache-lookup-policy.wat and examples/wasm/cache-store-policy.wat fixtures plus the examples/wasm/cache-policy.toml config template document this subset, and the native HTTP/1 test suite compiles the example sources directly. Richer store-admission mutation remains follow-up 1.7.x cache-policy work.

For v1.7.9, the example TOML is schema-validated and the shared live policy smoke proves pass versus MISS/HIT behavior, bounded device-class variants, image-only TTL/tag/stored-header mutation, TTL expiry, non-image isolation, tag-based purge through Fluxheim's cache tooling, and fail-closed unknown or duplicate mutation IDs. This is typed VCL-like capability parity; it does not embed VCL or expose raw cache objects.

Stabilization Requirements

Before 1.7 is complete:

  • all four example families must be runnable from scripts/test_starter.py;
  • the stable or deep release gate must run the applicable Wasm smokes;
  • docs must state that Fluxheim provides capability parity, not syntax compatibility with F5 iRules, Lua/OpenResty, HAProxy SPOE, or VCL;
  • unsupported host calls, unknown ABI versions, unsupported plugin phases, and invalid config combinations must fail deterministically at config load or plugin-load time;
  • every example must have a matching negative test that proves the sandbox does not expose filesystem, network, env, admin APIs, secrets, request bodies, or raw cache objects without explicit future capabilities.

v1.7.9 provides scripts/build_wasm_policy_examples.sh for deterministic source-to-module builds and digest output, while scripts/smoke_wasm_all.sh is the single orchestration entry used by both scripts/test_starter.py and the opt-in stable/deep release gate. This avoids treating multiple script paths as arguments to one launcher command and keeps human and CI coverage aligned.

v1.7.10 also exposes each family independently through wasm-irules, wasm-openresty, wasm-haproxy-spoe, and wasm-vcl test-starter entries. These focused entries invoke the same family checks used by the aggregate smoke; they are not reduced documentation-only substitutes.

The aggregate smoke also runs the arbitrary guest-ID decoder property test. This keeps symbolic host-call and guest-outcome decoding totality inside the same deep-gate evidence as the runnable examples.

The standalone scripts/smoke_wasm_policy_examples_binary.sh proof also loads the generated files through a private plugin root and exact SHA-256 pins, starts the real Fluxheim binary from a file-based config, and sends HTTP traffic through all four vhosts. The in-process listener tests remain the faster detailed regressions; both layers are required by the complete smoke.