# CrowdSec WAF QuickStart for Traefik

## Objectives[​](#objectives "Direct link to Objectives")

The goal of this quickstart is to set up the [AppSec Component](https://docs.crowdsec.net/docs/next/appsec/intro.md#introduction) to safeguard web applications running behind the [Traefik](https://doc.traefik.io/traefik/) reverse proxy. We'll deploy a [set of rules](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-virtual-patching) designed to block [well-known attacks](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-generic-rules) and [currently exploited vulnerabilities](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-virtual-patching). Additionally, we'll show how to monitor these alerts through the [Console](https://app.crowdsec.net/).

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

1. If you're new to the [AppSec Component](https://docs.crowdsec.net/docs/next/appsec/intro.md#introduction) or **W**eb **A**pplication **F**irewalls, start with the [Introduction](https://docs.crowdsec.net/docs/next/appsec/intro.md#introduction) for a better understanding.

2. It's assumed that you have already installed:

   * **CrowdSec [Security Engine](https://docs.crowdsec.net/docs/next/intro.md)**: for installation, refer to the [QuickStart guide](https://docs.crowdsec.net/u/getting_started/installation/linux.md). The AppSec Component, which analyzes HTTP requests, is enabled via the [AppSec acquisition datasource](https://docs.crowdsec.net/docs/next/log_processor/data_sources/appsec.md).
   * Traefik Plugin **[Remediation Component](https://docs.crowdsec.net/u/bouncers/intro.md)**: Thanks to [maxlerebourg](https://github.com/maxlerebourg) and team, there is a [Traefik Plugin](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) that blocks requests directly in Traefik.

info

Before starting, ensure you are using the [Traefik Plugin](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) and **NOT** the older and deprecated [traefik-crowdsec-bouncer](https://app.crowdsec.net/hub/author/fbonalair/remediation-components/traefik-crowdsec-bouncer) as it hasn't received updates to use the new AppSec Component.

warning

This guide will assume you already have a working Traefik setup using the Traefik Plugin. If you need help setting up Traefik, refer to the [official documentation](https://doc.traefik.io/traefik/getting-started/) and the [Traefik Plugin](https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin) documentation.

Already did the base setup?

If you already completed the [General Setup](https://docs.crowdsec.net/docs/next/appsec/quickstart/general_setup.md) (collections + acquisition), skip to [Remediation Component Setup](#remediation-component-setup).

## AppSec Component Setup[​](#appsec-component-setup "Direct link to AppSec Component Setup")

### Collection installation[​](#collection-installation "Direct link to Collection installation")

To begin setting up the AppSec Component, the initial step is to install a relevant set of rules.

We will utilize the [crowdsecurity/appsec-virtual-patching](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-virtual-patching) collection, which offers a wide range of rules aimed at identifying and preventing the exploitation of known vulnerabilities.

This collection is regularly updated to include protection against newly discovered vulnerabilities. Upon installation, it receives automatic daily updates to ensure your protection is always current.

We also install the [crowdsecurity/appsec-generic-rules](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-generic-rules) collection. This collection contains detection scenarios for generic attack vectors. It provides some protection in cases where specific scenarios for vulnerabilities do not exist (yet).

info

You can always view the content of a [collection on the hub](https://app.crowdsec.net/hub/author/crowdsecurity/collections/appsec-virtual-patching)

* Docker
* Docker Compose
* Kubernetes (Helm)

SHCOPY

```
## This command should be used when you are persisting /etc/crowdsec/ on the host
docker exec -it crowdsec cscli collections install crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules
```

This command installs the needed AppSec Hub configuration items.

values.yaml

YAMLvalues.yamlCOPY

```
services:
  crowdsec:
    environment:
      - 'COLLECTIONS=crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules'
```

warning

Please note the spaces between the collection names (hence why the quotes are needed).

This compose configuration file adds the required Hub configuration items.

Please add this in your `values.yaml` for your CrowdSec release.

values.yaml

YAMLvalues.yamlCOPY

```
appsec:
  env:
    - name: COLLECTIONS
      value: "[...] crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules [...]"
```

warning

Please note the spaces between the collection names (hence why the double quotes are needed).

note

If your `values.yaml` does not already configure the CrowdSec **agent** (via `agent.acquisition` or `agent.additionalAcquisition`), you must explicitly disable it — otherwise the Helm chart will fail with `No acquisition or additionalAcquisition configured`:

YAMLCOPY

```
agent:
  enabled: false
```

If you are running a full CrowdSec stack (agent + LAPI + AppSec), configure `agent.acquisition` with your actual log sources instead.

Now you can apply it with:

SHCOPY

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

This `values.yaml` modification adds the required Hub configuration items.

Those needed hub configuration items are:

* The [*AppSec Rules*](https://docs.crowdsec.net/docs/next/appsec/rules_syntax.md) contain the definition of malicious requests to be matched and stopped.
* The [*AppSec Configuration*](https://docs.crowdsec.net/docs/next/appsec/configuration.md#appsec-configuration-files) links together a set of rules to provide a coherent set.
* The CrowdSec Parser and CrowdSec Scenario(s) are used to detect and remediate persistent attacks.

Once you have updated Compose or installed via the command line, restart the container. Before you do, set up the acquisition for the AppSec Component.

### Setup the Acquisition[​](#setup-the-acquisition "Direct link to Setup the Acquisition")

You now need to set up the acquisition for AppSec. The steps depend on how you run CrowdSec. For the complete acquisition reference, see the [AppSec datasource](https://docs.crowdsec.net/docs/next/log_processor/data_sources/appsec.md).

* Docker
* Docker Compose
* Kubernetes (Helm)

In the directory where you persist configuration files, create an `appsec.yaml` file and mount it into the container.

**Steps**

1. Change to the directory where you ran the `docker run` or `docker compose` command.
2. Create a file named `appsec.yaml` in this directory.
3. Add the following content:

appsec.yaml

YAMLappsec.yamlCOPY

```
appsec_configs:
  - crowdsecurity/appsec-default
labels:
  type: appsec
listen_addr: 0.0.0.0:7422
source: appsec
```

Because CrowdSec runs inside a container, set listen\_addr to 0.0.0.0 instead of 127.0.0.1 so it can accept connections from outside the container.

Edit your docker run command to mount the file:

If a crowdsec container is already running, stop/remove it before re-running with the updated mounts.

SHCOPY

```
docker run -d --name crowdsec \
  -v /path/to/original:/etc/crowdsec \
  -v ./appsec.yaml:/etc/crowdsec/acquis.d/appsec.yaml \
  crowdsecurity/crowdsec
```

In the directory where you persist configuration files, create an appsec.yaml file and mount it into the container.

**Steps**

1. Change to the directory where you ran the docker compose (or docker run) command.
2. Create a file named appsec.yaml in this directory.
3. Add the following content to the `appsec.yaml`:

appsec.yaml

YAMLappsec.yamlCOPY

```
appsec_configs:
  - crowdsecurity/appsec-default
labels:
  type: appsec
listen_addr: 0.0.0.0:7422
source: appsec
```

Because CrowdSec runs in a container, set listen\_addr to 0.0.0.0 (not 127.0.0.1) so it listens on the container’s network interface.

Mount the file in your Compose service:

TEXTCOPY

```
services:
  crowdsec:
    volumes:
      - /path/to/original:/etc/crowdsec   # or a named volume
      - ./appsec.yaml:/etc/crowdsec/acquis.d/appsec.yaml
```

Once you have updated the compose file to include the volume mount and the updated environment variable, you can restart the container.

SHCOPY

```
docker compose down crowdsec
docker compose rm crowdsec
docker compose up -d crowdsec
```

note

The previous compose commands presume the container is named `crowdsec`. If you have named the container something else, you will need to replace `crowdsec` with the name of your container.

With Kubernetes the acquisition setup is done via `values.yaml`. Add the following to your CrowdSec `values.yaml`:

values.yaml

YAMLvalues.yamlCOPY

```
agent:
  enabled: false   # required if you have no agent.acquisition configured; replace with your log sources for a full stack
appsec:
  acquisitions:
    - appsec_configs:
        - crowdsecurity/appsec-default
      labels:
        type: appsec
      listen_addr: 0.0.0.0:7422
      path: /
      source: appsec
  enabled: true
```

Then apply with:

SHCOPY

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

## Remediation Component Setup[​](#remediation-component-setup "Direct link to Remediation Component Setup")

As stated previously this guide already presumes you have the Traefik Plugin installed. If you do not have the Traefik Plugin installed, please refer to the [official documentation](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin) for installation instructions.

### Configuration[​](#configuration "Direct link to Configuration")

Depending on how you configured the Traefik Plugin, you will need to update the configuration to include the AppSec configuration.

warning

Currently AppSec does not support mTLS authentication for the AppSec Component. If you have mTLS enabled, and wish to use the AppSec Component, you can define seperate middlewares for the AppSec Component.

* Traefik dynamic configuration
* Traefik middleware (Kubernetes)

If you have defined a dynamic configuration file for Traefik, you can add the following configuration to the file.

traefik\_dynamic.yaml

YAMLtraefik\_dynamic.yamlCOPY

```
# Dynamic configuration
http:
  routers:
    my-router:
      rule: host(`whoami.localhost`)
      service: service-foo
      entryPoints:
        - web
      middlewares:
        - crowdsec

  services:
    service-foo:
      loadBalancer:
        servers:
          - url: http://127.0.0.1:5000
  
  middlewares:
    crowdsec:
      plugin:
        bouncer:
          enabled: true
          crowdsecAppsecEnabled: true
          crowdsecAppsecHost: crowdsec:7422
          crowdsecAppsecFailureBlock: true
          crowdsecAppsecUnreachableBlock: true
          crowdsecLapiKey: <your-shared-traefik-bouncer-key>
```

Instead if you define the configuration using labels on the containers you can add the following labels to the Traefik Plugin container.

YAMLCOPY

```
  labels:
      - "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
      - "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecAppsecEnabled=true"
      - "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecAppsecHost=crowdsec:7422"
      - "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecLapiKey=<your-shared-traefik-bouncer-key>"
```

For Kubernetes, use the same secret management pattern as in the [Traefik bouncer setup](https://docs.crowdsec.net/u/bouncers/traefik.md#store-the-traefik-bouncer-key-in-a-kubernetes-secret): store the shared bouncer key in Kubernetes secrets and reference it from both CrowdSec and Traefik.

Two secrets are needed because CrowdSec and Traefik run in different namespaces:

* In the `crowdsec` namespace, CrowdSec LAPI reads `BOUNCER_KEY_traefik` from the `crowdsec-keys` secret.
* In the `traefik` namespace, Traefik mounts the same key from the `crowdsec-bouncer-key` secret as a file.

Both secrets must contain the same `BOUNCER_KEY_traefik` value. If you already created them for the base bouncer setup, you can reuse them here.

If you haven't created the bouncer key yet, generate one with:

SHCOPY

```
kubectl exec -n crowdsec deploy/crowdsec-lapi -c crowdsec-lapi -- cscli bouncers add traefik -o raw
```

Copy the printed key — you will use it as `<your-shared-traefik-bouncer-key>` below.

Create or update the secrets:

crowdsec-keys.yaml

YAMLcrowdsec-keys.yamlCOPY

```
apiVersion: v1
kind: Secret
metadata:
  name: crowdsec-keys
  namespace: crowdsec
type: Opaque
stringData:
  ENROLL_KEY: "<your-existing-enroll-key>"
  BOUNCER_KEY_traefik: "<your-shared-traefik-bouncer-key>"
---
apiVersion: v1
kind: Secret
metadata:
  name: crowdsec-bouncer-key
  namespace: traefik
type: Opaque
stringData:
  BOUNCER_KEY_traefik: "<your-shared-traefik-bouncer-key>"
```

Apply it:

SHCOPY

```
kubectl apply -f crowdsec-keys.yaml
```

Then make sure the CrowdSec Helm values reference `BOUNCER_KEY_traefik` from the `crowdsec-keys` secret:

crowdsec-values.yaml

YAMLcrowdsec-values.yamlCOPY

```
lapi:
  env:
    - name: BOUNCER_KEY_traefik
      valueFrom:
        secretKeyRef:
          name: crowdsec-keys
          key: BOUNCER_KEY_traefik
```

Apply the CrowdSec release again:

SHCOPY

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

Then configure Traefik to mount the `crowdsec-bouncer-key` secret and reference it with `crowdsecLapiKeyFile`.

Use a Traefik values file like this:

traefik-values.yaml

YAMLtraefik-values.yamlCOPY

```
experimental:
  plugins:
    bouncer:
      moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
      version: v1.4.5
volumes:
  - name: crowdsec-bouncer-key
    mountPath: /etc/traefik/crowdsec
    type: secret
    secretName: crowdsec-bouncer-key
```

note

The Traefik Helm chart uses a read-only root filesystem by default. The plugin loader needs a writable directory to cache downloaded plugins. Add an `emptyDir` volume for `/plugins-storage` alongside the secret volume above:

traefik-values.yaml (addition)

YAMLtraefik-values.yaml (addition)COPY

```
deployment:
  additionalVolumes:
    - name: plugins-storage
      emptyDir: {}
additionalVolumeMounts:
  - name: plugins-storage
    mountPath: /plugins-storage
```

Without this, Traefik will fail to start with `unable to create directory /plugins-storage/sources: read-only file system`.

Then create a Traefik Middleware resource:

SHCOPY

```
kubectl apply -f traefik-middleware.yaml
```

note

The `spec.plugin.<key>` in the Middleware **must match** the key you registered under `experimental.plugins.<key>` in Traefik's configuration — not the module name (`crowdsec-bouncer-traefik-plugin`). Since the `traefik-values.yaml` above registers the plugin under the key `bouncer`, use `bouncer:` here.

YAMLCOPY

```
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: crowdsec
  namespace: traefik
spec:
  plugin:
    bouncer:
      enabled: true
      crowdsecMode: stream
      crowdsecLapiScheme: http
      crowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
      crowdsecLapiKeyFile: /etc/traefik/crowdsec/BOUNCER_KEY_traefik
      httpTimeoutSeconds: 60
      forwardedheaderstrustedips:
        - 10.0.0.0/8
        - 192.168.0.0/16
        - 203.0.113.0/24
        - 2001:db8::/32
      crowdsecAppsecEnabled: true
      crowdsecAppsecHost: crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
      crowdsecAppsecFailureBlock: true
      crowdsecAppsecUnreachableBlock: true
```

How is the AppSec hostname derived?

The Helm chart creates a Service named `<release>-appsec-service` in the namespace where CrowdSec is installed. With the default release name `crowdsec` in namespace `crowdsec`, the in-cluster DNS name is:

TEXTCOPY

```
crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
```

If you used a different release name or namespace, adjust accordingly: `<release>-appsec-service.<namespace>.svc.cluster.local:7422`.

Less secure alternative: define the Traefik bouncer key inline with `crowdsecLapiKey` instead of mounting `crowdsecLapiKeyFile`

YAMLCOPY

```
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: crowdsec
  namespace: traefik
spec:
  plugin:
    bouncer:
      enabled: true
      crowdsecMode: stream
      crowdsecLapiScheme: http
      crowdsecLapiHost: crowdsec-service.crowdsec.svc.cluster.local:8080
      crowdsecLapiKey: <your-shared-traefik-bouncer-key>
      httpTimeoutSeconds: 60
      forwardedheaderstrustedips:
        - 10.0.0.0/8
        - 192.168.0.0/16
        - 203.0.113.0/24
        - 2001:db8::/32
      crowdsecAppsecEnabled: true
      crowdsecAppsecHost: crowdsec-appsec-service.crowdsec.svc.cluster.local:7422
      crowdsecAppsecFailureBlock: true
      crowdsecAppsecUnreachableBlock: true
```

note

If your IngressRoute lives in a different namespace than the Middleware (e.g. `default` vs `traefik`), Traefik's Kubernetes CRD provider blocks cross-namespace references by default. Either place the Middleware in the same namespace as the IngressRoute, or add the following to your Traefik Helm values:

traefik-values.yaml (addition)

YAMLtraefik-values.yaml (addition)COPY

```
providers:
  kubernetesCRD:
    allowCrossNamespace: true
```

You can still add some route configuration through [IngressRoute](https://doc.traefik.io/traefik/reference/routing-configuration/kubernetes/crd/http/ingressroute/) and attach the middleware to those routes.

For more comprehensive documentation on the Traefik Plugin configuration, please refer to the [official documentation](https://plugins.traefik.io/plugins/6335346ca4caa9ddeffda116/crowdsec-bouncer-traefik-plugin).

We can't cover all the possible configurations for Traefik in this guide, so please refer to the [official documentation](https://doc.traefik.io/traefik/) for more information.

### Directives[​](#directives "Direct link to Directives")

The following directives are available for the Traefik Plugin:

#### `crowdsecAppsecEnabled`[​](#crowdsecappsecenabled "Direct link to crowdsecappsecenabled")

> `bool`

Enable or disable the AppSec Component.

#### `crowdsecAppsecHost`[​](#crowdsecappsechost "Direct link to crowdsecappsechost")

> `string`

The host and port where the AppSec Component is running.

#### `crowdsecAppsecFailureBlock`[​](#crowdsecappsecfailureblock "Direct link to crowdsecappsecfailureblock")

> `bool`

If the AppSec Component returns `500` status code should the request be blocked.

#### `crowdsecAppsecUnreachableBlock`[​](#crowdsecappsecunreachableblock "Direct link to crowdsecappsecunreachableblock")

> `bool`

If the AppSec Component is unreachable should the request be blocked.

## Testing the AppSec Component + Remediation Component[​](#testing-the-appsec-component--remediation-component "Direct link to Testing the AppSec Component + Remediation Component")

note

For bare-metal/Docker, the web server is assumed to be listening on port 80 on the same host — adjust the URL if needed. For Kubernetes, send the test request through your Ingress (e.g. `http://<node-ip>/<path>`) rather than directly to the AppSec port.

If you try to access `http://localhost/.env` from a browser, your request will be blocked, resulting in the display of the following HTML page:

![appsec-denied](/assets/images/appsec_denied-6e67f77eebc0aafccfbb7304136ad33e.png)

We can also look at the metrics from `cscli metrics show appsec` — it will display:

* the number of requests processed by the AppSec Component
* Individual rule matches

The command to run depends on your environment:

Bare-metal / Docker

SHBare-metal / DockerCOPY

```
sudo cscli metrics show appsec
```

Kubernetes

SHKubernetesCOPY

```
kubectl exec -n crowdsec \
  $(kubectl get pod -n crowdsec -l type=appsec -o name | head -1) \
  -c crowdsec-appsec -- cscli metrics show appsec
```

Example Output

Bare-metal output (listen\_addr: 127.0.0.1:7422)

SHBare-metal output (listen\_addr: 127.0.0.1:7422)COPY

```
Appsec Metrics:
╭─────────────────┬───────────┬─────────╮
│  Appsec Engine  │ Processed │ Blocked │
├─────────────────┼───────────┼─────────┤
│ 127.0.0.1:7422/ │ 2         │ 1       │
╰─────────────────┴───────────┴─────────╯

Appsec '127.0.0.1:7422/' Rules Metrics:
╭─────────────────────────────────┬───────────╮
│             Rule ID             │ Triggered │
├─────────────────────────────────┼───────────┤
│ crowdsecurity/vpatch-env-access │ 1         │
╰─────────────────────────────────┴───────────╯
```

Kubernetes output (listen\_addr: 0.0.0.0:7422)

SHKubernetes output (listen\_addr: 0.0.0.0:7422)COPY

```
Appsec Metrics:
╭─────────────────┬───────────┬─────────╮
│  Appsec Engine  │ Processed │ Blocked │
├─────────────────┼───────────┼─────────┤
│ 0.0.0.0:7422/   │ 2         │ 1       │
╰─────────────────┴───────────┴─────────╯

Appsec '0.0.0.0:7422/' Rules Metrics:
╭─────────────────────────────────┬───────────╮
│             Rule ID             │ Triggered │
├─────────────────────────────────┼───────────┤
│ crowdsecurity/vpatch-env-access │ 1         │
╰─────────────────────────────────┴───────────╯
```

You can test and investigate further with [Stack Health-Check](https://docs.crowdsec.net/u/getting_started/health_check.md) and [Appsec Troubleshooting guide](https://docs.crowdsec.net/docs/next/appsec/troubleshooting.md)

### Explanation[​](#explanation "Direct link to Explanation")

What happened in the test that we just did is:

1. We did a request (`localhost/.env`) to our local webserver
2. Thanks to the Remediation Component configuration, forwarded the request to `http://127.0.0.1:7422`
3. Our AppSec Component, listening on `http://127.0.0.1:7422` analyzed the request
4. The request matches the [AppSec rule to detect .env access](https://app.crowdsec.net/hub/author/crowdsecurity/appsec-rules/vpatch-env-access)
5. The AppSec Component thus answered with [HTTP 403](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/403) to the Remediation Component, indicating that the request must be blocked
6. The web server then presented us with the default "request blocked" page.

## Integration with the Console[​](#integration-with-the-console "Direct link to Integration with the Console")

If you haven't yet, follow the guide about [how to enroll your Security Engine in the Console](https://docs.crowdsec.net/u/getting_started/post_installation/console.md).

Once done, all your alerts, including the ones generated by the AppSec Component, appear in the Console:

![appsec-console](/assets/images/appsec_console-59b5f39cf3f7fc002e61539c0e866f23.png)

## Next steps[​](#next-steps "Direct link to 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](https://app.crowdsec.net).
* Review the [AppSec troubleshooting guide](https://docs.crowdsec.net/docs/next/appsec/troubleshooting.md) if you need to investigate or refine the deployment.
* Explore [WAF deployment strategies](https://docs.crowdsec.net/docs/next/appsec/advanced_deployments.md), [rules syntax](https://docs.crowdsec.net/docs/next/appsec/rules_syntax.md), [rule creation](https://docs.crowdsec.net/docs/next/appsec/create_rules.md), and [benchmarks](https://docs.crowdsec.net/docs/next/appsec/benchmark.md) to go further.
