CrowdSec WAF QuickStart for Envoy Gateway
Objectives
This quickstart shows how to deploy the CrowdSec AppSec Component in Kubernetes and protect workloads exposed through Envoy Gateway using an external authorization service.
At the end, you will have:
- CrowdSec running in-cluster with the AppSec API listening on
7422 - A CrowdSec-compatible Envoy external auth service running in the cluster. This is the service Envoy calls during external authorization checks; it queries CrowdSec LAPI and AppSec, then tells Envoy whether the request should be allowed or denied.
SecurityPolicyresources attached to yourHTTPRouteobjects. In Envoy Gateway, aSecurityPolicyis the custom resource that enables external authorization for aGatewayorHTTPRouteand points Envoy to the auth backend.HTTPRouteobjects exposing your applications. In Gateway API, anHTTPRoutedefines how HTTP requests for specific hostnames or paths are matched and forwarded to your backendService.- Virtual patching and generic AppSec rules inspecting requests before they reach your backends
Prerequisites
-
If you're new to the AppSec Component or Web Application Firewalls, start with the Introduction.
-
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/HTTPRouteresources exposing your applications.
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:
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:
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
Deploy CrowdSec with AppSec enabled
Add this to the CrowdSec values.yaml to enable the AppSec acquisition
datasource and load the default AppSec configuration:
agent:
acquisition:
- namespace: envoy-gateway-system
podName: envoy-default-*
poll_without_inotify: true
program: envoy
env:
- name: COLLECTIONS
value: yanis-kouidri/envoy
- name: DEBUG
value: "true"
appsec:
acquisitions:
- appsec_configs:
- crowdsecurity/appsec-default
labels:
type: appsec
listen_addr: 0.0.0.0:7422
path: /
source: appsec
enabled: true
env:
- name: COLLECTIONS
value: crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
lapi:
env:
- name: BOUNCER_KEY_envoy
valueFrom:
secretKeyRef:
key: BOUNCER_KEY_envoy
name: crowdsec-keys
Apply or upgrade the CrowdSec release:
helm upgrade --install crowdsec crowdsec/crowdsec \
--namespace crowdsec \
--create-namespace \
-f crowdsec-values.yaml
Verify the CrowdSec pods:
kubectl -n crowdsec get pods
kubectl -n crowdsec get svc crowdsec-service crowdsec-appsec-service
You should see:
crowdsec-lapiinRunningcrowdsec-appsecinRunningcrowdsec-serviceexposing port8080crowdsec-appsec-serviceexposing port7422
Deploy the Envoy external auth bouncer
- Helm-Based Installation
- Without Helm
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:
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:
fullnameOverride: crowdsec-envoy-bouncer
config:
bouncer:
lapiURL: http://crowdsec-service.crowdsec.svc.cluster.local:8080
apiKeySecretRef:
name: crowdsec-envoy-bouncer-secrets
key: api-key
waf:
enabled: true
appSecURL: http://crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
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.
Keep the bouncer API key in a Kubernetes secret in the namespace where your
Envoy Gateway infrastructure runs. In this example, that namespace is
envoy-gateway-system.
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 the secret:
kubectl apply -f crowdsec-envoy-bouncer-secret.yaml
Then create the bouncer objects directly:
apiVersion: apps/v1
kind: Deployment
metadata:
name: crowdsec-envoy-bouncer
namespace: envoy-gateway-system
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: crowdsec-envoy-bouncer
template:
metadata:
labels:
app.kubernetes.io/name: crowdsec-envoy-bouncer
spec:
containers:
- name: crowdsec-envoy-bouncer
image: ghcr.io/kdwils/envoy-proxy-bouncer:v0.6.0
imagePullPolicy: Always
ports:
- name: grpc
containerPort: 8080
protocol: TCP
env:
- name: ENVOY_BOUNCER_BOUNCER_ENABLED
value: "true"
- name: ENVOY_BOUNCER_BOUNCER_APIKEY
valueFrom:
secretKeyRef:
name: crowdsec-envoy-bouncer-secrets
key: api-key
- name: ENVOY_BOUNCER_BOUNCER_LAPIURL
value: http://crowdsec-service.crowdsec.svc.cluster.local:8080
- name: ENVOY_BOUNCER_WAF_ENABLED
value: "true"
- name: ENVOY_BOUNCER_WAF_APIKEY
valueFrom:
secretKeyRef:
name: crowdsec-envoy-bouncer-secrets
key: api-key
- name: ENVOY_BOUNCER_WAF_APPSECURL
value: http://crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
- name: ENVOY_BOUNCER_SERVER_GRPCPORT
value: "8080"
- name: ENVOY_BOUNCER_SERVER_HTTPPORT
value: "8081"
- name: ENVOY_BOUNCER_SERVER_LOGLEVEL
value: info
---
apiVersion: v1
kind: Service
metadata:
name: crowdsec-envoy-bouncer
namespace: envoy-gateway-system
spec:
selector:
app.kubernetes.io/name: crowdsec-envoy-bouncer
ports:
- name: grpc
port: 8080
targetPort: grpc
protocol: TCP
Apply it:
kubectl apply -f crowdsec-envoy-bouncer.yaml
This path gives you full control over the generated objects, but you must keep
the Deployment, Service, and ReferenceGrant aligned yourself.
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.
Attach them at the HTTPRoute level:
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 them:
kubectl apply -f app-securitypolicy.yaml
Allow cross-namespace references
The Helm chart can create the required ReferenceGrant for you. In the example
above, this is handled by:
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: default
to:
- group: ""
kind: Service
name: crowdsec-envoy-bouncer
If you do not let the chart manage ReferenceGrant, you must create an
equivalent ReferenceGrant manually in envoy-gateway-system.
Testing detection
Test the HTTP generic scenario
You can verify that CrowdSec is parsing Envoy logs correctly by triggering the
crowdsecurity/http-generic-test dummy scenario.
- Access your service URL with this path:
/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
curl -I http://<your-service-url>/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
- 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
Test the AppSec generic scenario
If AppSec forwarding is enabled, the same probe path also lets you verify the
crowdsecurity/appsec-generic-test dummy scenario.
- Access your service URL with this path:
/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
curl -I http://<your-service-url>/crowdsec-test-NtktlJHV4TfBSK3wvlhiOBnl
- Confirm the alert has triggered for
crowdsecurity/appsec-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/appsec-generic-test
Important Notes
Attach SecurityPolicy to HTTPRoutes, not just 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 still require ReferenceGrant
If your bouncer Service is in envoy-gateway-system and your applications
live in other namespaces, ReferenceGrant is required. The ReferenceGrant has
to correctly specify the crowdsec bouncer service.
Use failOpen: true during rollout
If you apply a fail-closed external auth policy before the bouncer is healthy,
Envoy will start rejecting traffic with 403
Check image architecture support
The community bouncer image is published for amd64. For other architectures you will need a custom build.
Next steps
You are now running the AppSec Component on your CrowdSec Security Engine.
As the next steps, you can:
- Monitor WAF alerts in the CrowdSec Console.
- Review the AppSec troubleshooting guide if you need to investigate or refine the deployment.
- Explore WAF deployment strategies, rules syntax, rule creation, and benchmarks to go further.