This is a preview release. Do not use it in production environments.
Download the release bundle from Download Edge-Native Defender (Preview). New to Edge-Native Defender? Start with Edge-Native Defender overview.
Watch the install (3½ minutes): downloading the release, render and validate, the pieces merged into an Envoy configuration, Envoy restarted, allowed and blocked requests, and Envoy's own admin counters.
At a glance
| Deployment | One filter added to the Envoy you already run. defenderctl-envoy render writes the pieces to merge in; your listeners, routes and upstreams do not change. |
| Added latency | About 0.2 ms at the median and 1.4 ms at the 95th percentile. |
| Cost | Nothing from Cequence for the infrastructure. Plan for about 0.7 CPU core more per 1,000 requests a second and about 150 MiB more memory per Envoy. |
| If something fails | Your traffic keeps flowing. If Cequence is unreachable, Defender keeps enforcing its last policies; no request ever waits on Cequence. |
| Upgrades | Point ENVOY_WASM_PATH at the new release, render, and restart one Envoy at a time. Roll back the same way. |
Deployment architecture
Defender runs inside Envoy that you run yourself, configured with a bootstrap file, on hosts, VMs or containers. Running Envoy Gateway, Istio or kgateway? See Defender for Kubernetes Gateways.
Each request is decided on the Envoy worker thread that already holds it, from a policy set kept in that Envoy's memory. There is no extra service to size, no extra hop, and no network call on the request path. Defender scales with Envoy.
BEFORE
client ----> Envoy listener ----> router ----> your upstream
AFTER
client ----> Envoy listener ----> router ----> your upstream
|
+----------------+
| Defender | (inside Envoy)
| Wasm filter |
+----------------+
|
+---> your Cequence tenant: policies in, analytics out,
in the background
What stays the same. Your listeners, routes, upstreams and other filters. You add Defender's filter to the listeners you want protected; listeners without it are not inspected. Removing the filter undoes it.
What Defender does with a request. A blocked request is answered by Envoy and never reaches your upstream. An allowed request is forwarded unchanged: Defender inspects a copy of each body, up to your tenant's capture settings, and never cuts short or changes what it forwards. When new policies arrive, Envoy builds them in small slices between requests and swaps them in whole, so a policy change does not pause traffic.
What is sent to Cequence. For each request: its headers and URL, the client address, the response status and headers, the decision if a policy acted, and the inspected body copies. Compressed bodies (gzip, deflate, brotli, zstd) are decoded for this copy only; your traffic is never decoded. Everything is masked inside Envoy with your tenant's Sensitive Data rules before it leaves, and nothing is sent until those rules have arrived. Your traffic filters decide whether a request is reported at all. Discovery sends counts of the hosts, methods and paths seen, with no request content.
All connections are outbound HTTPS from Envoy; nothing connects in. Envoy signs
in at auth.<tenant>.cequence.cloud, checks edge.<tenant>.cequence.cloud for
policy changes every 10 seconds, and sends analytics there in batches after
your client already has its response. Envoy downloads the filter from
downloads.cequence.ai at start, unless you use a local copy.
Installation
Prerequisites
-
Envoy 1.33 or later, with WebAssembly support and the V8 runtime
(
envoy.wasm.runtime.v8). Each release is tested on Envoy 1.34. Some Envoy distributions ship without Wasm, so check your vendor's notes. You must be able to add a bootstrap extension to your Envoy configuration; settle that first if your configuration is generated for you. -
Your tenant name (for example
acme.1, exactly as Cequence gave it), client ID and client secret. The client needs the scopesapi:dataplane:traffic:writeandapi:dataplane:config:read. -
Outbound HTTPS (443) from each Envoy to
auth.<tenant>.cequence.cloud,edge.<tenant>.cequence.cloudand, unless you use a local copy of the filter,downloads.cequence.ai. Certificates and host names are checked against the standard trust store at/etc/ssl/certs/ca-certificates.crt, which the official Envoy image and most Linux distributions have. - A safe place for the secret. The client secret goes into the Envoy configuration, as there is no secret-store integration yet. Restrict the file's permissions and keep it out of source control.
- A free loopback port, 127.0.0.1:18101 by default, for Defender's check of request bodies. It is never reachable from outside the host.
-
Docker, or your own Envoy binary, on the machine where you run
defenderctl-envoy validate.
1. Fill in defender.conf
In the unpacked defender-for-envoy bundle, copy defender.conf.example to
defender.conf and set:
CEQUENCE_TENANT=acme.1 CEQUENCE_CLIENT_ID=<client id>
That is all that is required. Optional Envoy settings:
| Setting | When to set it |
ENVOY_WASM_PATH |
Already set to this release's filter on downloads.cequence.ai. Where Envoy cannot reach the internet, copy defender-envoy-<version>.wasm from the bundle to each Envoy host (check it with sha256sum -c SHA256SUMS) and give its path, such as /etc/defender/defender.wasm. |
ENVOY_INSTANCE_ID |
Names this Envoy in your tenant. Defaults to the host name; give each Envoy a different one. |
ENVOY_CLIENT_IP_HEADER |
Defaults to x-envoy-external-address. Change it only if your edge puts the client address elsewhere, such as cf-connecting-ip. |
ENVOY_DECISION_PORT |
Only if 127.0.0.1:18101 is already in use, or several Envoys share one host's network. |
2. Render and validate
export CEQUENCE_CLIENT_SECRET=<client secret> ./bin/defenderctl-envoy render ./bin/defenderctl-envoy validate
render writes five files to ./defender-envoy/ and prints where each one
goes. validate checks them with Envoy itself, without your configuration and
without the secret. Set ENVOY_BIN to your Envoy binary to check against the
version you run; otherwise it uses Docker. With a local copy of the filter, add
--wasm ./defender-envoy-<version>.wasm.
Want to see it work first? ./bin/defenderctl-envoy quickstart writes a trial
Envoy in Docker with Defender in front of a public test upstream, and
./bin/defenderctl-envoy status shows that trial and Defender's status page.
3. Merge the files into your Envoy
| File | Goes into |
singleton.yaml |
the top level of your Envoy configuration, once per Envoy. Holds the secret (written mode 0600) |
clusters.yaml |
static_resources.clusters |
decision-listener.yaml |
static_resources.listeners |
connection-manager.yaml |
the http_connection_manager of each listener you protect |
filter.yaml |
that listener's http_filters, before the router. Its last two filters go after your own ext_authz, if you have one |
Two things to get right:
-
The client address. With the default
x-envoy-external-address, that connection manager needsuse_remote_address: true, plusxff_num_trusted_hopsset to the number of proxies in front of Envoy. -
One render per Envoy. Each Envoy needs its own
ENVOY_INSTANCE_ID, so render each one's files with its own value.
To protect part of your traffic, add the filter only to those listeners, or use a route or filter-chain match so only those requests reach a chain with it.
4. Restart Envoy
Envoy reads bootstrap extensions at startup, so restart it with the new
configuration. Run envoy --mode validate -c <your envoy.yaml> first if you
like. At start, Envoy downloads the filter and logs that it is loading for a
second or two; it serves traffic once the filter has loaded.
5. Check it before you go live
- On a test Envoy, set
CEQUENCE_DIAGNOSTICS=yesindefender.conf, render, merge and restart. Then request/__end/statusthrough a protected listener:policies_loadedshows your tenant's policy count. The bundle'sREADME.mdlists every field to check, including that masking is active and analytics are being sent. - Send a request that one of your policies blocks, and check it is blocked.
Send one that should pass, and check it passes. Envoy's own counters show
both: on the admin interface,
/stats?filter=downstream_rq_[24]xxlists each listener's 2xx and 4xx counts (blocks are 4xx, under the protected listener'sstat_prefix), andwasm.defenderstats appear once the filter has loaded. - Read your tenant's Sensitive Data rules against the fields that matter to
you. A header is masked only when a rule names it, so check
authorizationand your session cookie. - Compare latency with and without Defender on a copy of your own traffic.
- Set
CEQUENCE_DIAGNOSTICS=no, render and restart again. While it is on, the status page and thex-end-echoheader are readable by anyone who can reach the listener. - Roll out to a few Envoys first and compare them with the rest. The bundle's
README.mdalso covers writing the configuration by hand.
Upgrades and rollback
Each release has its own filter URL, and Envoy loads the filter at startup, so an upgrade is a render and a restart.
- Set
ENVOY_WASM_PATHindefender.confto the URL in the new bundle'sdefender.conf.example. With a local copy, put the new.wasmbeside the old one under a new name, such as/etc/defender/defender-<version>.wasm, and pointENVOY_WASM_PATHat it. - Run the new bundle's
defenderctl-envoy renderandvalidate, and merge the new files.renderkeeps every reference to the filter in step. - Restart one Envoy, check it as in "Check it before you go live", then roll through the rest.
To roll back, set ENVOY_WASM_PATH back to the previous release's URL (or
file), render, and restart the same way.
During each restart, Envoy's own restart behaviour applies to your traffic;
drain connections from an Envoy first if you normally do. Analytics not yet
sent are lost on restart. After a restart Defender fetches your policies again
and forwards requests until they arrive, a matter of seconds. Policy changes
never need an upgrade: Defender picks them up while it runs. Changing a setting
works the same way as an upgrade: edit defender.conf, render, restart.
Performance and sizing
| Without Defender | With Defender | Added | |
| CPU per 1,000 requests a second | 0.31 core | 1.0 core | about 0.7 core |
| Envoy memory | 24 MiB | 170 MiB | about 150 MiB |
| Latency, median | 0.73 ms | 0.95 ms | about 0.2 ms |
| Latency, 95th percentile | 1.9 ms | 3.3 ms | about 1.4 ms |
Measured on a 4 vCPU host at 1,000 requests a second with two workers, a policy set of about 6,000 policies, analytics and masking on, and 1 KB responses. Cost scales with request rate; more policies, larger bodies or more Sensitive Data rules raise it, so measure with your own traffic before sizing.
When your tenant has dynamic fingerprint algorithms, computing them adds about 2 ms a request. Envoy also keeps a second copy of your policy set per worker for checking request bodies, and a bounded backlog of analytics while Cequence is slow or unreachable.
Cost
Cequence charges nothing for the infrastructure. Defender runs inside the Envoy you already run, with no extra service or host. Budget the extra CPU and memory under "Performance and sizing", and the outbound HTTPS traffic to your Cequence tenant, which is compressed.
Availability
- If Cequence is slow or unreachable, your traffic is unaffected. No request waits on Cequence: Defender keeps enforcing the policies it last received and keeps a bounded backlog of analytics, sent once Cequence is reachable again. A long outage can drop analytics beyond that backlog.
-
If Defender itself cannot decide, the request goes through. A body check
that does not answer within 0.5 seconds lets the request continue, and a
Defender that has never loaded policies forwards everything. A configuration
error stops Envoy at start, before it takes traffic, which
validatecatches first. - If you need Defender off the path at once, remove its filters from the listener and restart Envoy; traffic then bypasses it entirely. To stop blocking without a restart, change your policies in your Cequence tenant to a non-blocking action: each Envoy picks the change up within about 10 seconds and keeps reporting.
- A fresh install blocks nothing until your tenant's first policy publish arrives.
Known limits
-
blockandadd-headerpolicies are enforced.challenge,rate-limitandredirectpolicies are reported but not applied: the request is forwarded. Block pages use your tenant's templates. - Body policies check the first 16 KiB. A request with a body (not GET, HEAD or OPTIONS) waits until 16 KiB has arrived, or it ends, before its headers go upstream; the body is always passed on whole. A match after the first 16 KiB is not seen, and WebSocket, gRPC and event-stream requests are not checked on their body. Slow uploads are delayed by the wait. If such a request is blocked, your upstream has received its headers but not its body.
- Fingerprints of requests with a body use the header order Envoy passes on, not the client's, so learn fingerprint-keyed policies from traffic through Defender.
- Defender sees the request as Envoy has normalised it. For example, Envoy
drops a
Refererthat is not a valid header value; Defender keeps a copy and restores it before the request goes upstream. - The traffic-policy configuration is not fetched yet; Defender uses the client address with no allow-filters.
- Analytics not yet sent are lost if Envoy restarts, and a request that never receives a response is not reported.
- The client secret is in the Envoy configuration.
- Masking applies your tenant's Sensitive Data rules and nothing more.
- Kong Gateway does not run this filter; see Defender for Kong.
If something is wrong
| You see | Check |
| Envoy refuses to start | Run defenderctl-envoy validate. Make sure singleton.yaml went to the top level, not under http_filters. |
address in use on 127.0.0.1:18101 |
Set ENVOY_DECISION_PORT to a free port, render and restart. |
Invalid path: /etc/ssl/certs/ca-certificates.crt |
Your host keeps its CA bundle elsewhere (RHEL: /etc/pki/tls/certs/ca-bundle.crt). Ask Cequence support for the setting, render and restart. |
| No policies loaded | Outbound HTTPS to your tenant's hosts, and the client ID, secret and scopes. |
| Every client looks like one address |
use_remote_address and xff_num_trusted_hops on the connection manager, or ENVOY_CLIENT_IP_HEADER. |
The bundle's README.md has the full troubleshooting table. If you contact
support, send the /__end/status output (it holds no credentials) and Envoy's
startup log, never the configuration file that holds the secret.
Removing it
- Remove the contents of
filter.yamlfrom each listener'shttp_filtersand ofconnection-manager.yamlfrom its connection manager. - Remove what
singleton.yaml,decision-listener.yamlandclusters.yamladded, and the filter file if you copied one to the host. - Restart Envoy.
- Ask Cequence to disable the client credential, or rotate the secret.
Your listeners, routes and upstreams were never changed, so nothing else needs undoing.