Skip to main content

📚 Documentation 💠 Source 💬 Discourse

AppSecSupported
ModeStream only
MetricsSupported
MTLSUnsupported
PrometheusUnsupported

CrowdSec Remediation QuickStart for Envoy Gateway

Objectives​

This quickstart shows how to deploy the Envoy CrowdSec bouncer in Kubernetes and protect workloads exposed through Envoy Gateway using external authorization.

At the end, you will have:

  • CrowdSec LAPI running in-cluster and reachable from Envoy Gateway
  • A CrowdSec-compatible Envoy external auth service running in the cluster
  • SecurityPolicy resources attached to your HTTPRoute objects
  • CrowdSec remediation decisions enforced before traffic reaches your backends

This bouncer supports the AppSec Component for real-time WAF protection.

This page focuses on remediation-only deployment. If you want Envoy Gateway with WAF and virtual patching, follow the CrowdSec WAF QuickStart for Envoy Gateway.

Prerequisites​

  1. It is assumed that you already have:
    • A working CrowdSec Security Engine installation. For a Kubernetes install quickstart, refer to /u/getting_started/installation/kubernetes.
    • A working Envoy Gateway installation with the Gateway API CRDs and an accepted GatewayClass.
    • Existing Gateway / HTTPRoute resources exposing your applications.

This integration currently relies on a community Envoy external auth bouncer, not on a first-party CrowdSec remediation component.

The upstream project used in this guide is:

  • kdwils/envoy-proxy-crowdsec-bouncer

Store the Envoy bouncer key in a Kubernetes secret​

For Envoy Gateway, a practical approach is to choose a fixed key, store it in a Kubernetes secret, and force BOUNCER_KEY_envoy from lapi.env with valueFrom.secretKeyRef.

Create or update the secret used by CrowdSec LAPI:

crowdsec-keys.yaml
apiVersion: v1
kind: Secret
metadata:
name: crowdsec-keys
namespace: crowdsec
type: Opaque
stringData:
ENROLL_KEY: "<your-existing-enroll-key>"
BOUNCER_KEY_envoy: "<choose-a-long-random-key>"

Apply it:

kubectl apply -f crowdsec-keys.yaml

Then reference BOUNCER_KEY_envoy from the CrowdSec Helm values:

crowdsec-values.yaml
lapi:
env:
- name: BOUNCER_KEY_envoy
valueFrom:
secretKeyRef:
name: crowdsec-keys
key: BOUNCER_KEY_envoy

Apply the CrowdSec release again:

helm upgrade --install crowdsec crowdsec/crowdsec \
--namespace crowdsec \
--create-namespace \
-f crowdsec-values.yaml

Verify CrowdSec LAPI access​

The Envoy bouncer only needs access to CrowdSec LAPI. Make sure the CrowdSec release exposes the LAPI service and that the bouncer key is available through lapi.env.

Verify the CrowdSec pods and services:

kubectl -n crowdsec get pods
kubectl -n crowdsec get svc crowdsec-service

You should see:

  • crowdsec-lapi in Running
  • crowdsec-service exposing port 8080

For remediation to work correctly, CrowdSec must see the real client IP in the Envoy access logs, and the bouncer must evaluate requests against that same IP.

If Envoy only logs an internal proxy, load balancer, or node IP, CrowdSec will create decisions for the wrong source and bouncing will not work as expected.

In Kubernetes, make sure the Envoy service configuration preserves source IPs, for example by setting externalTrafficPolicy: Local instead of a setup that hides the original client IP.

Deploy the Envoy bouncer​

For the Helm-based install, keep the API key in a Kubernetes Secret and reference that secret from a user-managed values.yaml file.

Create the secret holding the CrowdSec bouncer key:

crowdsec-envoy-bouncer-secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: crowdsec-envoy-bouncer-secrets
namespace: envoy-gateway-system
type: Opaque
stringData:
api-key: "<same-value-as-BOUNCER_KEY_envoy>"

Apply it:

kubectl apply -f crowdsec-envoy-bouncer-secret.yaml

Then create a values file:

envoy-bouncer-values.yaml
fullnameOverride: crowdsec-envoy-bouncer
config:
bouncer:
enabled: true
lapiURL: http://crowdsec-service.crowdsec.svc.cluster.local:8080
apiKeySecretRef:
name: crowdsec-envoy-bouncer-secrets
key: api-key
securityPolicy:
create: true
gatewayName: shared-public
gatewayNamespace: envoy-gateway-system

Install the chart:

helm install crowdsec-envoy-bouncer oci://ghcr.io/kdwils/charts/envoy-proxy-bouncer \
--namespace envoy-gateway-system \
--create-namespace \
-f envoy-bouncer-values.yaml

If you only want to deploy the bouncer and manage SecurityPolicy objects manually, omit the securityPolicy.* settings.

Verify the rollout:

kubectl -n envoy-gateway-system rollout status deploy/crowdsec-envoy-bouncer

Attach Envoy SecurityPolicy resources to HTTPRoutes​

Envoy Gateway external auth is configured through gateway.envoyproxy.io/v1alpha1 SecurityPolicy resources.

Attaching the SecurityPolicy to an HTTPRoute is usually better than attaching it to the Gateway.

It keeps the policy scoped to one application route instead of every route on the shared entrypoint, which makes rollout, debugging, and multi-application setups easier to manage.

Attach them at the HTTPRoute level:

app-securitypolicy.yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
name: crowdsec-ext-auth
namespace: "<app-namespace>"
spec:
targetRefs:
- group: gateway.networking.k8s.io
kind: HTTPRoute
name: "<app-route>"
extAuth:
failOpen: true
grpc:
backendRefs:
- group: ""
kind: Service
name: crowdsec-envoy-bouncer
namespace: envoy-gateway-system
port: 8080

Apply it:

kubectl apply -f app-securitypolicy.yaml

Allow cross-namespace references​

If your SecurityPolicy and the bouncer Service live in different namespaces, allow the reference with a ReferenceGrant:

reference-grant.yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: crowdsec-ext-auth-backend
namespace: envoy-gateway-system
spec:
from:
- group: gateway.envoyproxy.io
kind: SecurityPolicy
namespace: "<app-namespace>"
to:
- group: ""
kind: Service
name: crowdsec-envoy-bouncer

Apply it:

kubectl apply -f reference-grant.yaml

Validation​

Check that the bouncer is running:

kubectl -n envoy-gateway-system get pods
kubectl -n envoy-gateway-system logs deploy/crowdsec-envoy-bouncer

Then confirm the SecurityPolicy is accepted and attached:

kubectl get securitypolicy -A
kubectl get referencegrant -A

To validate remediation, add a temporary decision for a test IP and send a request from that source through Envoy Gateway. Envoy should deny the request once the bouncer has synchronized the decision from LAPI.

Testing detection​

You can also verify that CrowdSec is parsing Envoy logs correctly by triggering the crowdsecurity/http-generic-test dummy scenario.

  1. Access your service URL with this path: /crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
curl -I http://<your-service-url>/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
  1. Confirm the alert has triggered for crowdsecurity/http-generic-test:
kubectl exec -n crowdsec -it $(kubectl get pods -n crowdsec -l k8s-app=crowdsec -l type=lapi -o name) -- cscli alerts list -s crowdsecurity/http-generic-test

If you trigger this scenario from a private ip, you won't see it trigger as it will be dismissed by the whitelist parser.

Important Notes​

Attach SecurityPolicy to HTTPRoutes, not only to the Gateway​

For Envoy Gateway, route-level attachment is the safer pattern for this integration. Attaching the policy at the Gateway level can apply external auth to every routed application behind that listener, which increases the blast radius of a bad policy, a broken backend reference, or an unhealthy bouncer. Attaching it to individual HTTPRoutes keeps the rollout explicit and incremental: you can protect only the routes that should use CrowdSec, leave other traffic untouched, and troubleshoot one application at a time.

Cross-namespace bouncer references require ReferenceGrant​

If your bouncer Service is in envoy-gateway-system and your applications live in other namespaces, ReferenceGrant is required.

Use failOpen: true during rollout​

If you apply a fail-closed external auth policy before the bouncer is healthy, Envoy will start rejecting traffic unexpectedly.

Check image architecture support​

The community bouncer image may not be published for every architecture. If your platform is not supported, you will need a custom build.

Next steps​