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.
At a glance
| Deployment | One small service in your own project, on Cloud Run (installed by defenderctl-gcp) or GKE Autopilot, called by your load balancer through Service Extensions. Your forwarding rule, URL map, backends and DNS do not change. |
| Added latency | About 6.5 ms at the median on GKE Autopilot, 16.5 ms on Cloud Run. |
| Cost | One Cloud Run instance always on, or two small GKE pods. Google's Service Extensions pricing applies to the callout. Nothing from Cequence for the infrastructure. |
| If something fails | Your APIs stay up. If Cequence is unreachable, Defender keeps enforcing its last policies; if Defender is unreachable, the load balancer sends requests on to your backend after the 1 s callout timeout. |
| Upgrades |
defenderctl-gcp upgrade deploys a new Cloud Run revision; roll back by sending traffic to the previous one. On GKE, a rolling update. |
Deployment architecture
Defender protects APIs behind your global external Application Load
Balancer through Google's Service Extensions. A traffic extension on your
forwarding rule calls the Defender service, defender-extproc, on every request
and response, and Defender decides each request before it reaches your backend.
BEFORE
client ---> Application Load Balancer ---------------> your backends
AFTER
client ---> Application Load Balancer ---------------> your backends
|
| traffic extension (each request and response)
v
Defender service (Cloud Run or GKE, in your project)
|
+---> your Cequence tenant: policies in, analytics out,
in the background
What stays the same. Your forwarding rule, URL map, backends, certificates and DNS. The traffic extension is the only change to the load balancer. Google API Gateway works the same way behind a global external Application Load Balancer with a serverless NEG.
What Defender does with a request. A blocked request is answered by the load balancer and never reaches your backend. An allowed request is forwarded byte for byte, and your client receives the response byte for byte: Defender inspects a copy of each body, up to your tenant's capture settings, and never changes what it forwards. No request waits on Cequence.
What is sent to Cequence. For each request: its headers and URL, the response status and headers, and the inspected body copies, masked first with your tenant's Sensitive Data rules. Your traffic filters decide whether a request is reported at all.
Installation
Prerequisites
- A global external Application Load Balancer (load balancing scheme
EXTERNAL_MANAGED) in front of your APIs, with no traffic extension on its forwarding rule yet. Google allows one per forwarding rule. - The Cloud Run, Compute Engine, Network Services and Secret Manager APIs enabled in the project, and permission to use them.
- The
defender-extprocimage copied into your Artifact Registry: Cloud Run pulls only from Google registries. Cequence support gives you access to the image for your release. - Outbound HTTPS from Defender to your Cequence tenant.
- Your tenant's name, client ID and client secret.
-
gcloud, signed in to the project, on the machine you install from, withcurlandpython3. For GKE, alsokubectl.
Choose where Defender runs
| Cloud Run | GKE Autopilot | |
| Installed by | defenderctl-gcp install |
the manifests in the bundle, by hand |
| What you run | only the Cloud Run service | an Autopilot cluster; Google manages the nodes |
| Choose it when | the simplest setup matters more than about 10 ms of latency | latency matters, or you already run GKE |
1. Fill in defender.conf
In the unpacked defender-for-gcp bundle, copy defender.conf.example to
defender.conf and fill in:
CEQUENCE_TENANT=acme.1 CEQUENCE_CLIENT_ID=<client id> GCP_PROJECT=<project id> GCP_REGION=<region of your backends> GCP_FORWARDING_RULE=<your load balancer's forwarding rule> GCP_IMAGE=<region>-docker.pkg.dev/<project>/<repository>/defender-extproc:<version>
Optional: CEQUENCE_NAMESPACE names everything (default prod), so resources
are defender-<namespace>. CEQUENCE_GCP_MATCH limits which requests are sent
to Defender, for example request.path.startsWith('/api') (default: every
request). GCP_SERVICE_ACCOUNT sets the account Cloud Run runs as (default:
the project's Compute Engine default account). GCP_CALLOUT_TIMEOUT changes
the 1 s callout timeout (10ms to 10s).
2. Install on Cloud Run
With the client secret in the environment:
export CEQUENCE_CLIENT_SECRET=<client secret> ./bin/defenderctl-gcp preflight ./bin/defenderctl-gcp install
install stores the client secret in Secret Manager
(defender-<namespace>-client-secret) and creates the Cloud Run service
defender-<namespace>, its serverless NEG defender-<namespace>-neg, the
callout backend service defender-<namespace>-callout and the traffic
extension defender-<namespace>. It changes nothing until every check passes,
and refuses to replace a traffic extension or Cloud Run service it did not
create. ./bin/defenderctl-gcp render writes everything it would create to
./defender-gcp/ instead, without touching the project.
The service keeps one instance always running, reachable only from the load balancer, so it is warm to inspect the first requests.
2 (alternative). Install on GKE Autopilot
defenderctl-gcp does not install this option yet. The bundle's
gcp/gke/defender.yaml runs Defender as a Deployment of at least two pods with
an autoscaler, and a Service whose NEG gives the load balancer the pod
addresses directly. With defender.conf filled in as in step 1:
-
Render the configuration. Run
./bin/defenderctl-gcp render. Copy the JSON value ofDEFENDER_CONFIG_JSONindefender-gcp/env-vars.yamlinto a file nameddefender.json. Keepdefender-gcp/traffic-extension.yamlfor step 8. -
A cluster. An Autopilot cluster in your backends' region, or your own:
gcloud container clusters create-auto defender --region=<region>. -
A TLS certificate. Google requires TLS to GKE callout backends and does
not verify the certificate, so a self-signed one is accepted:
openssl req -x509 -newkey rsa:2048 -nodes -keyout tls.key -out tls.crt -days 365 -subj "/CN=defender". -
The configuration and secrets, in namespace
defender:kubectl create namespace defender kubectl -n defender create configmap defender-config --from-file=defender.json kubectl -n defender create secret generic defender-tenant --from-file=client-secret=<file holding the client secret> kubectl -n defender create secret tls defender-tls --cert=tls.crt --key=tls.key
-
Defender. Replace the
<...>placeholders ingcp/gke/defender.yaml(your image, and<NAMESPACE>with yourCEQUENCE_NAMESPACE), thenkubectl apply -f gcp/gke/defender.yaml. The Service creates one NEG per zone the pods run in, nameddefender-<namespace>-gke. -
A firewall rule so Google's health checks and load balancer reach the
pods: from
35.191.0.0/16and130.211.0.0/22to TCP 8443 and 8099, on the cluster's node tag (gke-<cluster>-<hash>-node, shown on the firewall rules GKE created for the cluster). -
The callout backend. An HTTP health check on port 8099, and a global
backend service named
defender-<namespace>-callout, schemeEXTERNAL_MANAGED, protocolHTTP2, with that health check. Add each zone'sdefender-<namespace>-gkeNEG to it with--balancing-mode=RATE --max-rate-per-endpoint=1000. -
The traffic extension. Import the file from step 1, which points at that
backend service:
gcloud service-extensions lb-traffic-extensions import defender-<namespace> --source=defender-gcp/traffic-extension.yaml --location=global. If gcloud refuses itsforwardAttributesfield, update gcloud.
3. Check it before you go live
- Turn diagnostics on. On Cloud Run, set
CEQUENCE_DIAGNOSTICS=yesand runinstallagain. On GKE, render withCEQUENCE_DIAGNOSTICS=yes, replace thedefender-configConfigMap and runkubectl -n defender rollout restart deployment/defender-extproc. - Allow a few minutes after an install for the load balancer to start calling
the extension, then request
/__end/statusthrough it, or runGCP_VERIFY_URL=https://<your API host> ./bin/defenderctl-gcp verify.policies_loadedshows your tenant's policy count. - Send a request that one of your policies blocks, and check it is blocked. Blocking can start a few minutes after the status page first answers: until then the load balancer forwards requests Defender would block. If the request is not blocked yet, wait a few minutes and try again.
- Turn diagnostics off again (
CEQUENCE_DIAGNOSTICS=no) the same way. The status page is readable by anyone who can reach your load balancer.
Upgrades and rollback
Each release publishes a new defender-extproc image version. Copy it into
your Artifact Registry first.
Cloud Run. Set GCP_IMAGE to the new version in defender.conf, then:
export CEQUENCE_CLIENT_SECRET=<client secret> ./bin/defenderctl-gcp upgrade ./bin/defenderctl-gcp status
upgrade is install for an existing installation: it deploys a new revision
of defender-<namespace> and reapplies the same traffic extension. Cloud Run
starts the new revision before it moves traffic to it, so the load
balancer always has a Defender to call.
To roll back, send traffic back to the previous revision:
gcloud run revisions list --service=defender-<namespace> --region=<region> gcloud run services update-traffic defender-<namespace> --region=<region> --to-revisions=<previous revision>=100
After the next upgrade, run the same command with --to-latest so traffic
follows the newest revision again. Or set GCP_IMAGE back and run upgrade.
GKE Autopilot. Change the image version and let the rolling update replace one pod at a time:
kubectl -n defender set image deployment/defender-extproc defender-extproc=<registry path>/defender-extproc:<new version> kubectl -n defender rollout status deployment/defender-extproc
To roll back: kubectl -n defender rollout undo deployment/defender-extproc.
The traffic extension and the load balancer do not change between releases.
Check /__end/status afterwards as in "Check it before you go live".
Performance and sizing
| GKE Autopilot | Cloud Run | |
| Added latency, median | about 6.5 ms | about 16.5 ms |
| Added latency, 90th percentile | about 10 ms | about 22.5 ms |
| Size | 0.5 vCPU and 512 MiB per pod; 2 to 10 pods, scaled on CPU | Cloud Run's default instance size; at least one instance, CPU always allocated |
Measured on a live global external Application Load Balancer in us-central1, at 25 requests a second of GETs and 2 KB POSTs, against the same load balancer with no extension. Defender's own decision is about a millisecond; the rest is the load balancer's round trip to it, and Cloud Run's front end adds about 10 ms of that. The 99th percentile (+25 to +58 ms across runs) was the same on every backend, so it comes from the load balancer's side of the callout. A request costs one callout's worth of time, not one per event.
Cost
Cequence charges nothing for the infrastructure. You pay Google for:
- Cloud Run: one instance always on, with CPU always allocated, plus any extra instances Cloud Run adds under load.
- GKE Autopilot: at least two pods of 0.5 vCPU and 512 MiB each (up to ten under load), plus your cluster.
- The load balancer's callout: Google's Service Extensions pricing applies.
- Secret Manager, for the one client secret.
Availability
- If Cequence is slow or unreachable, your traffic is unaffected. Defender keeps enforcing the policies it last received. It holds a bounded backlog of analytics and sends it once Cequence is reachable again; a long outage can drop the oldest.
- If the Defender service is unreachable or slow, the extension is fail open: after the 1 s callout timeout the load balancer sends the request on to your backend, uninspected. On GKE, two pods, spread across zones where possible, keep one available through a restart.
-
If you need Defender off the path at once, delete the traffic extension:
gcloud service-extensions lb-traffic-extensions delete defender-<namespace> --location=global(or rundefenderctl-gcp uninstall, which removes it first). As with an install, it can take a few minutes to take effect. - 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. - Regional and classic Application Load Balancers are not supported yet.
- Google allows one traffic extension per forwarding rule. If yours already has
one, add Defender's chain from
defenderctl-gcp renderto it (at most five chains) instead of installing. -
defenderctl-gcpinstalls the Cloud Run option only; GKE is by hand. - Analytics show
HTTP/1.1as the protocol of every request: Google's API does not yet pass the client's protocol to a traffic extension. - With API Gateway, clients can still call its
*.gateway.devaddress directly, which bypasses the load balancer.
If something is wrong
-
Requests are not inspected. Run
./bin/defenderctl-gcp status: it shows whether the extension is attached, its timeout and match, and whether the service is serving. A new extension can take a few minutes to take effect. - No policies loaded. Check the Cloud Run service's or pods' logs: Defender says there why it cannot reach your tenant (outbound access, client ID or secret).
- Every request is slower than expected. Compare with the table above. Most of the added time is the load balancer's call to Defender, so GKE Autopilot is the faster choice.
-
Contacting support.
./bin/defenderctl-gcp debugwrites a support bundle of non-secret descriptions as a tarball.
The bundle's README.md has the full reference.
Removing it
On Cloud Run:
./bin/defenderctl-gcp uninstall
It removes the traffic extension first, so traffic stops going through Defender
before anything else is deleted. The secret is kept unless you add
--delete-secret.
On GKE, delete the traffic extension first (as in "Availability"), then the
backend service, health check and firewall rule, and
kubectl delete -f gcp/gke/defender.yaml.