Deployment guide

Traefik with CrowdSec

Overview

Traefik with CrowdSec Security Engine is an edge appliance. It terminates inbound HTTP and HTTPS traffic, routes it to your backend services, and defends it at the same time. Traefik handles routing, load balancing and automatic Let's Encrypt certificates.

The CrowdSec Security Engine reads Traefik's own access log. Its behavioural scenarios recognise probing, crawling, brute force and known CVE exploitation patterns, and the engine records a ban decision. A bouncer middleware inside Traefik enforces those decisions on the very next request, so an attacking source is refused at the edge and never reaches your application.

The detection and enforcement halves are already wired together. Both run on the instance against local decisions, with no external service and no account to create.

What you get:

  • Traefik 3.7.10 (a single static Go binary) at /usr/local/bin/traefik
  • CrowdSec Security Engine 1.7.x with its Local API on 127.0.0.1:8080, never exposed publicly
  • The CrowdSec bouncer as a Traefik middleware, vendored on disk so it loads with no internet access
  • The crowdsecurity/traefik collection, which parses Traefik's access log, plus the linux and sshd collections
  • web (:80), websecure (:443) and a loopback ping (127.0.0.1:8082) entrypoint
  • The Traefik dashboard at /dashboard/ behind the bouncer and HTTP basic auth (api.insecure is false)
  • A branded block page shown to refused visitors, at /etc/traefik/ban.html
  • A pre-configured Let's Encrypt certificate resolver (HTTP challenge)
  • A dedicated Azure data disk at /var/lib/traefik for ACME storage and logs, re-provisioned with every VM
  • File-provider dynamic configuration in /etc/traefik/dynamic/ for your own routers and services
  • Unique per-VM credentials generated on first boot, and a start-up guard that refuses to run on a stand-in credential
  • 24/7 SecureAnyCloud support

How the protection works

Requests flow through a closed loop:

visitor --> Traefik (:80/:443) --> [crowdsec bouncer] --> your backend | ^ | writes JSON access log | checks local decisions v | /var/lib/traefik/access.log | | | +--> CrowdSec parses it -+ scenarios fire, a ban decision is stored locally

CrowdSec reads Traefik's own access log, so anything you publish through this proxy is protected automatically. You do not need to instrument your application.

Prerequisites

  • An active Azure subscription, an SSH key pair, and a VNet and subnet in the target region
  • Standard_B2ms (2 vCPU / 8 GiB RAM) as a starting size. Scale up for high request volumes
  • NSG inbound rules that allow 22/tcp from your management network, and 80/tcp and 443/tcp for proxied traffic and the dashboard
  • A DNS name that points at the VM's public IP, if you want real Let's Encrypt certificates

The CrowdSec Local API on 8080 is bound to loopback. Never open it.

Step 1 - Deploy from the Azure Marketplace

In the Azure portal, choose Create a resource. Search for Traefik with CrowdSec Security Engine on Ubuntu 24.04 LTS by SecureAnyCloud, and select Create.

Pick your subscription, resource group and region. Choose the Standard_B2ms size and select SSH public key authentication with the username azureuser. Allow inbound 22, 80 and 443. Then choose Review + create.

Step 2 - Deploy from the Azure CLI

az vm image terms accept --urn sac1647283583153:traefik-crowdsec:default:latest
az vm create \
  --resource-group my-resource-group \
  --name traefik-crowdsec-01 \
  --image sac1647283583153:traefik-crowdsec:default:latest \
  --size Standard_B2ms \
  --admin-username azureuser \
  --generate-ssh-keys \
  --public-ip-sku Standard

Open the ports the proxy needs:

az vm open-port --resource-group my-resource-group --name traefik-crowdsec-01 --port 80 --priority 1010
az vm open-port --resource-group my-resource-group --name traefik-crowdsec-01 --port 443 --priority 1020

Step 3 - Connect to your VM

ssh azureuser@YOUR_VM_PUBLIC_IP

Step 4 - Confirm the services are running

The appliance runs three units: Traefik, the CrowdSec engine, and a small loopback backend that serves the default landing page. Check that all three are running:

sudo systemctl is-active traefik.service crowdsec.service sac-landing.service

The output looks like this:

active active active

Check the installed versions:

/usr/local/bin/traefik version | head -3 dpkg-query -W -f='crowdsec ${Version}\n' crowdsec

Step 5 - Retrieve your per-VM credentials

Every VM generates its own credentials on first boot. Nothing is shared between deployments.

sudo cat /root/traefik-crowdsec-credentials.txt

This file contains your dashboard URL, the admin username, and the password unique to this VM. Store the password in your password manager. Treat the file as sensitive. It has mode 0600 root:root.

Step 6 - Check the health endpoints

Traefik exposes a loopback ping endpoint, and CrowdSec exposes its Local API health endpoint. Both are bound to 127.0.0.1, so they are not reachable from outside the VM. Query them:

curl -s -o /dev/null -w 'traefik ping: %{http_code}\n' http://127.0.0.1:8082/ping
curl -s http://127.0.0.1:8080/health; echo

The output looks like this:

traefik ping: 200 {"status":"up"}

Step 7 - Open the dashboard

Browse to http://YOUR_VM_PUBLIC_IP/. You see the default landing page, served through the CrowdSec bouncer.

Browse to http://YOUR_VM_PUBLIC_IP/dashboard/ and sign in as admin with the password from Step 5. The dashboard shows your entrypoints, routers, services and middlewares.

The dashboard is never exposed without authentication. Confirm that from the command line:

PW=$(sudo grep '^TRAEFIK_ADMIN_PASSWORD=' /root/traefik-crowdsec-credentials.txt | cut -d= -f2-)
curl -s -o /dev/null -w 'no credentials: %{http_code}\n' http://127.0.0.1/dashboard/
curl -s -o /dev/null -w 'with credentials: %{http_code}\n' -u "admin:$PW" http://127.0.0.1/dashboard/

The output looks like this:

no credentials: 401 with credentials: 200

Step 8 - Confirm the bouncer middleware is active

Open HTTP Middlewares in the dashboard. You see crowdsec@file with a green status. This is the component that enforces bans.

Run the same check from the command line:

PW=$(sudo grep '^TRAEFIK_ADMIN_PASSWORD=' /root/traefik-crowdsec-credentials.txt | cut -d= -f2-)
curl -s -u "admin:$PW" http://127.0.0.1/api/http/middlewares \
  | jq -r '.[] | "\(.name) type=\(.type) status=\(.status)"'

The output includes:

crowdsec@file type=crowdsec status=enabled

If crowdsec@file is missing, the bouncer is not protecting your routers. Check the Traefik log with sudo journalctl -u traefik -n 50.

Step 9 - Confirm CrowdSec is reading Traefik's access log

This step checks the detection half of the loop. CrowdSec must be attached to /var/lib/traefik/access.log. List its data sources:

sudo cscli metrics show acquisition

You see a row for file:/var/lib/traefik/access.log with a non-zero Lines parsed count once traffic has flowed. A row that reads lines but parses none means the parser is not matching.

sudo cscli collections list | grep -i traefik

Step 10 - Watch the protection work end to end

You can test the whole loop in under a minute. Add a ban for a test address, then make a request that appears to come from it. 203.0.113.42 is a documentation-only address from RFC 5737, so this is safe to run.

sudo cscli decisions add --ip 203.0.113.42 --duration 2m --reason "guide demonstration"
sudo cscli decisions list

Request the site as that address. The appliance trusts X-Forwarded-For only from the machine itself, which is why this works locally.

for i in $(seq 1 15); do
CODE=$(curl -s -o /dev/null -w '%{http_code}' -H 'X-Forwarded-For: 203.0.113.42' http://127.0.0.1/) [ "$CODE" = "403" ] && break sleep 2 done echo "banned address: HTTP $CODE" curl -s -o /dev/null -w 'unrelated address: HTTP %{http_code}\n' -H 'X-Forwarded-For: 203.0.113.99' http://127.0.0.1/

The output looks like this:

banned address: HTTP 403 unrelated address: HTTP 200

The block applies only to the offending address. Everyone else is served normally. A blocked visitor sees this page:

Lift the ban:

sudo cscli decisions delete --ip 203.0.113.42

In normal operation you do not add decisions by hand. CrowdSec creates them when a source trips one of its scenarios, for example by scanning for .env files or probing known CVE paths.

Step 11 - Publish your own service behind the proxy

Add a YAML file to /etc/traefik/dynamic/. Traefik watches the directory and reloads automatically, so you do not need to restart it. Attach the crowdsec middleware, and the same loop protects your service.

# /etc/traefik/dynamic/my-app.yml http: routers: my-app: rule: "Host(`app.example.com`)" entryPoints: - web middlewares: - crowdsec service: my-app services: my-app: loadBalancer: servers: - url: "http://127.0.0.1:3000"

The built-in landing router has priority 1, the lowest, so any router you add takes precedence over the default page.

Step 12 - Review what the engine is doing

sudo cscli metrics
sudo cscli alerts list
sudo cscli decisions list
sudo cscli scenarios list | head -15

To exempt your own office or management network from bans, edit the whitelist and reload. Loopback and the RFC 1918 private ranges are already exempt.

# /etc/crowdsec/parsers/s02-enrich/sac-admin-whitelist.yaml whitelist: reason: "sac trusted management networks" ip: - "127.0.0.1" cidr: - "10.0.0.0/8" - "203.0.113.0/24"
# <- add your own office CIDR here

Apply the change:

sudo systemctl reload crowdsec

Getting the real client IP behind a load balancer

If you put Azure Load Balancer, Application Gateway or Front Door in front of this VM, Traefik sees the proxy's address instead of the visitor's, and CrowdSec would ban the proxy. Add your front-end ranges to the forwardedHeaders.trustedIPs list for the web and websecure entrypoints in /etc/traefik/traefik.yml. Then restart Traefik:

entryPoints: web: address: ":80" forwardedHeaders: trustedIPs: - "127.0.0.1/32" - "10.0.0.0/8"
# <- your load balancer subnet

Enabling HTTPS

A Let's Encrypt resolver named letsencrypt is pre-configured. It stores certificates on the data disk at /var/lib/traefik/acme.json. Set your contact address in /etc/traefik/traefik.yml and point a DNS name at the VM. Then attach the resolver to a router that listens on websecure:

http: routers: my-app-secure: rule: "Host(`app.example.com`)" entryPoints: - websecure middlewares: - crowdsec service: my-app tls: certResolver: letsencrypt

Optional - community blocklists and the CrowdSec console

The appliance ships with no CrowdSec Central API enrollment, so nothing is shared and no credential is stored in the image. Everything above works entirely offline. To use community blocklists and the hosted console, opt in:

sudo cscli capi register
sudo systemctl restart crowdsec
sudo cscli console enroll YOUR_ENROLLMENT_KEY

Maintenance

unattended-upgrades applies Ubuntu security updates automatically. CrowdSec is installed from its official repository and upgrades with the OS. Keep the hub content current with:

sudo cscli hub update

Traefik is a single binary at /usr/local/bin/traefik. To move to a newer release, replace the binary and restart traefik.service. Access and engine logs live on the data disk at /var/lib/traefik and rotate daily for 14 days.

Support

Every SecureAnyCloud image is backed by 24/7 support. Contact us at sales@secureanycloud.com.

Deploy on Azure

Launch Traefik with CrowdSec Security Engine on Ubuntu 24.04 LTS on Azure with 24/7 support from SecureAnyCloud.

View this image

Need Help?

Our support team is available 24/7.

sales@secureanycloud.com

Deploy on Azure