What is CrowdSec?#
If you enjoyed this article, you can support the author by becoming a sponsor on Boosty (link in the contacts section).
CrowdSec is a modern open-source security system built on principles similar to a “crowdsourced firewall.” It analyzes logs, detects suspicious activity (such as brute-force attempts, port scanning, DDoS), and automatically applies decisions to block or limit access for malicious IP addresses.
CrowdSec’s main feature is sharing data about malicious IP addresses among all users of the system, which builds a collective knowledge base formed from data contributed by all participants. So if one server detects an attack, other participants can block the attacker preemptively.
CrowdSec architecture#
CrowdSec is built on a modular architecture and is globally split into two layers:
- Agent (analysis engine) - analyzes log journals (Nginx, Traefik, SSH, Postfix, system logs, etc.).
- Bouncers (blocking modules) - apply blocking decisions (ban, captcha, throttle) to protect or allow access (for example, iptables, Nginx middleware, Traefik bouncer).
The Agent only handles detecting bad IPs, while blocking is carried out by the bouncers.
This means CrowdSec can be easily integrated into any infrastructure without directly touching network rules.
How does CrowdSec work?#
Let’s now look at how Crowdsec operates in more detail.
1. Log collection and analysis#
CrowdSec connects to the logs of various services (SSH, Nginx, Traefik, Postfix, system journals). Parsers are used for analysis, normalizing the logs into a common format.
2. Applying scenarios#
CrowdSec uses scenarios - sets of rules described in YAML.
For example, the ssh-bf scenario defines that 5 failed login attempts within 1 minute count as an attack.
3. Creating Decisions#
If a scenario triggers, the agent generates a decision, for example:
ban- block the IP.captcha- require verification.throttle- limit the request rate.
4. Passing the decision to a Bouncer#
Bouncers are separate components that carry out the blocking. Examples:
- an
iptablesbouncer adds the IP to the firewall. - a
traefik-bouncerconnects to Traefik as middleware. - an
nginx-bounceruses ACLs in Nginx.
5. Collective protection (Crowd Threat Intelligence)#
The agent can send anonymized data about attackers to a central CrowdSec server. In response, the server sends back a global list of IP addresses with a bad reputation, improving protection for all users.
Advantages of CrowdSec#
- Collective threat database - real-time protection from IP addresses known to the community.
- Flexibility - support for dozens of integrations (Docker, Kubernetes, Traefik, Nginx).
- Simplicity - ready-made scenarios for popular services.
- DevOps compatibility - integrates easily into CI/CD, works in containers.
Why CrowdSec is better than Fail2Ban#
1. Modern architecture#
- Fail2Ban - a classic solution written in Python, works at the log-analysis level and blocks IPs via the firewall.
- CrowdSec - a modern tool built on a microservice architecture and written in Go, which makes it more performant and scalable.
2. Collective protection (crowdsourcing)#
- Fail2Ban only blocks IPs attacking that specific server.
- CrowdSec shares data about malicious IPs with the global user network, forming a dynamic reputation database. Your server is protected not just from attacks that have already happened, but also from IPs observed attacking other servers.
3. Support for multiple data sources#
- Fail2Ban mainly analyzes system logs.
- CrowdSec supports:
- logs from various services (Nginx, SSH, Postfix, etc.);
- network flows (Netfilter, nftables);
- integrations with cloud services and APIs.
4. Flexible scenario system (Scenarios)#
- CrowdSec uses YAML scenarios that describe attack signatures (brute force, port scanning, etc.).
- New scenarios can be added in one click from the official hub, with no manual filter writing.
5. Performance#
- Fail2Ban scales poorly with a large volume of logs, especially on busy servers.
- CrowdSec is written in Go and processes logs asynchronously, making it significantly faster and less resource-intensive.
6. Modular structure#
- CrowdSec separates the analysis engine (agent) from the blocking mechanisms (bouncers).
- You can flexibly configure where blocks are sent: iptables, nginx, Cloudflare, HAProxy, etc.
7. Convenient visualization tools#
- CrowdSec provides a CLI with clear statistics, and it can integrate with Metabase for graphs and analytics. There’s also a clear graphical console for analyzing what’s happening on your server (registration on the developer’s website is required).
- Fail2Ban is limited to logs and basic commands.
8. Simplicity of management and updates#
- CrowdSec has a centralized scenario repository and automatic updates.
- With Fail2Ban you often need to update filters and regexes for new attacks yourself.
Conclusion:
CrowdSec isn’t just a modern equivalent of Fail2Ban, but a next-generation defense system that combines collective experience, high performance, and flexibility. Using CrowdSec - a more modern solution for server security that combines local log analysis with a collective knowledge base - alongside a reverse proxy (such as Traefik) lets you build dynamic, smart protection for any web application.
Attack detection capabilities#
Let’s talk in more detail about the principles behind attack detection. CrowdSec uses a three-tier detection approach to protect systems from known and new threats:
1. Local decision-making.#
- CrowdSec’s local decision-making capabilities let it detect attacks and respond to them in real time as they happen. When suspicious activity, such as password guessing or forced HTTP probing, matches one of CrowdSec’s predefined detection scenarios, the system triggers a local decision. This action - for example, blocking or restricting an IP address - directly addresses attacks detected in the system. Local decisions ensure any new or previously unknown attacker is blocked immediately before they can escalate their activity. This is usually done by analyzing log files. Finally, the decision’s metadata, such as the attacker’s IP address, is shared with the CrowdSec community through CrowdSec’s cyber threat intelligence (CTI) system.
2. Remote decision-making.#
- Remote decision-making is powered by the global CrowdSec community and CTI. When any CrowdSec instance anywhere in the world detects an attacker, that information is sent to CrowdSec’s Cyber Threat Intelligence (CTI) database. Such known malicious IP addresses are then flagged for preventive blocking across all other CrowdSec-protected systems via remote decisions. This proactive layer lets CrowdSec block incoming threats based on collective knowledge, stopping known attackers before they even reach your services. This is implemented via the CrowdSec bouncer (for example, for the Traefik reverse proxy).
3. AppSec (WAF).#
- CrowdSec can be set up as middleware acting as a WAF. In this case, every HTTP request is relayed to the CrowdSec AppSec endpoint over TCP/7422, which analyzes the request for malicious patterns. These patterns could be a payload for SQL injection, XSS exploitation, or command injection. If desired, you can use the well-known OWASP core rule set (CRS), best known from the open-source ModSecurity WAF and used by many other WAF vendors. Setting up the WAF takes time and often requires custom tuning for each proxied application.
As noted above, for local decision-making CrowdSec supports various Scenarios. These typically ship in so-called Collections.
By following this guide (apologies for the lofty tone), you’ll be able to implement more than 100 local-decision scenarios. Your HTTP services running behind Traefik will be protected against a substantial number of threats.
In this guide we’ll use the following collections:
crowdsecurity/traefik traefik support: parser and generic http scenarios crowdsecurity/http-cve Detect CVE exploitation in http logs crowdsecurity/base-http-scenarios http common : scanners detection crowdsecurity/sshd support : parser and brute-force detection crowdsecurity/linux core linux support : syslog+geoip+ssh crowdsecurity/appsec-generic-rules A collection of generic attack vectors for additional protection crowdsecurity/appsec-virtual-patching a generic virtual patching collection, suitable for most web servers. crowdsecurity/appsec-crs Appsec: Modsecurity core rule set rules
Running Crowdsec#
Crowdsec can be installed and run either natively on various OSes, or as a container in Docker. In this article we’ll cover running it as a Docker container, but in the future we’ll also look at running Crowdsec natively alongside a native Traefik install.
Docker Compose file#
First, let’s put together a docker-compose.yml file describing the main Crowdsec parameters.
services:
crowdsec: # Service name
image: crowdsecurity/crowdsec:v1.7 # pin to the current stable branch (1.8 is still in release-candidate status as of this update). Don't use latest, for the same reasons as with Traefik - an unannounced image update could bring a breaking change
container_name: crowdsec # container name
environment: # environment variables
GID: "${GID-1000}" # user:group permissions
COLLECTIONS: "crowdsecurity/traefik crowdsecurity/http-cve crowdsecurity/base-http-scenarios crowdsecurity/sshd crowdsecurity/linux crowdsecurity/appsec-generic-rules crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-crs" # names of the collections that will be loaded
TZ: Europe/Moscow # timezone. A very important setting, so that event times display correctly
volumes: # volumes required for Crowdsec to work correctly
- /home/path/to/crowdsec/db:/var/lib/crowdsec/data/ # path to the crowdsec database. Starting with version 1.7, this is no longer a recommendation but a mandatory requirement - without this volume, the Crowdsec container simply won't start
- /home/path/to/crowdsec/config:/etc/crowdsec/ # path to the crowdsec config. This same volume also mounts the acquis.d/ folder - a separate volume for it isn't needed
- /home/path/to/traefik/logs:/var/log/traefik/:ro # specify the paths to the logs crowdsec needs to parse. Note that these are read-only
- /var/log/auth.log:/var/log/auth.log:ro #
- /var/log/syslog:/var/log/syslog:ro #
ports:
- 127.0.0.1:9876:8080 # map the port for local firewall bouncers #
expose: # expose ports for the corresponding tasks
- 8080 # http api for bouncers
- 6060 # metrics endpoint for prometheus
- 7422 # appsec waf endpoint
networks: # specify the network in which both the Traefik reverse proxy and crowdsec run
- proxy #
security_opt: # set permission restrictions
- no-new-privileges:true #
restart: unless-stopped # ensure that if the container crashes, it will always try to restart
networks: # specify that the corresponding network is external and doesn't need to be created
proxy: #
external: true #Setting up the log-parsing configuration#
Now we need to tell Crowdsec how and where to parse the local logs of our Linux machine and the logs of the Traefik reverse proxy. We’ll also specify the port the WAF will run on.
cscli setup detect, which can scan the system on its own and find installed services (nginx, ssh, various logs, etc.), after which a single command, cscli setup install-acquisition, can generate the acquisition files for them. This mainly works for native (deb/rpm) installations and doesn’t always correctly see services tucked away at non-standard paths inside containers, so in this guide I still show the manual approach - it’s more predictable and easier to follow.Previously all log sources had to be stacked into a single acquis.yaml file using YAML documents separated by ---. This method is still supported, but starting with version 1.5 the acquis.d/ directory appeared (and has since been the recommended approach for any non-trivial setup) - you can lay out sources into separate files, one per source. This is more convenient: no need to scroll through one giant file looking for a particular source, and adding/removing a source doesn’t affect the rest.
There’s no need to create a separate volume for acquis.d/ - it’s already available inside the container, since we mounted the entire config folder (/home/path/to/crowdsec/config:/etc/crowdsec/). Just create an acquis.d directory inside it and lay out the following files:
acquis.d/traefik.yaml:
filenames:
- /var/log/traefik/*.log
labels:
type: traefikacquis.d/syslog.yaml:
filenames:
- /var/log/syslog
- /var/log/auth.log
labels:
type: syslogacquis.d/appsec.yaml:
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/appsec-default
name: myAppSecComponent
source: appsec
labels:
type: appsecIf you later want to add parsing for the logs of some other service - for example, Authentik or Nextcloud - just add a separate file, acquis.d/authentik.yaml or acquis.d/nextcloud.yaml, following the same principle, without touching the rest:
filenames:
- /var/log/authentik.log
labels:
type: authentikCreating an API token for our Bouncer#
Since we’ll need to link up our reverse proxy, Traefik, later on, we need to get the corresponding token that lets Traefik access the Bouncer.
Create the token with a simple command in the command line
docker exec crowdsec cscli bouncers add traefik-bouncerBe sure to copy the resulting token somewhere safe, because it’s displayed only once, and if you lose it you’ll have to start over from scratch. We’ll specify this token later in the corresponding middlewares section of Traefik’s dynamic configuration.
Setting up notifications#
In this article I won’t cover how to set up your own web panel on the developer’s website. The documentation is simple and intuitive. Register on the site and follow the instructions for your operating system. It’s practically impossible to get it wrong, unless you’re deliberately trying to.
But I will show you how to get instant notifications about events in Crowdsec, which in my view is much more important than having a visual overview of statistics.
I’ll show you how to set up notifications in Telegram, but you can use any notification service: e-mail, Gotify, Telegram, and so on.
You can see the full list here.
Essentially, you just need to configure two files:
First, you need to uncomment the two http_default lines in the profiles.yaml file, located at /path/to/crowdsec/config/profiles.yaml.
It looks roughly like this:
name: default_ip_remediation
#debug: true
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
- type: ban
duration: 4h
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
notifications:
# - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
# - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
- http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
# - email_default # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: break
---
name: default_range_remediation
#debug: true
filters:
- Alert.Remediation == true && Alert.GetScope() == "Range"
decisions:
- type: ban
duration: 4h
#duration_expr: Sprintf('%dh', (GetDecisionsCount(Alert.GetValue()) + 1) * 4)
notifications:
# - slack_default # Set the webhook in /etc/crowdsec/notifications/slack.yaml before enabling this.
# - splunk_default # Set the splunk url and token in /etc/crowdsec/notifications/splunk.yaml before enabling this.
- http_default # Set the required http parameters in /etc/crowdsec/notifications/http.yaml before enabling this.
# - email_default # Set the required email parameters in /etc/crowdsec/notifications/email.yaml before enabling this.
on_success: breakIf you want to receive notifications by email or via one of the messengers listed, uncomment the corresponding lines.
After that, you need to edit the notifications/http.yaml file, located at /path/to/crowdsec/config/notifications.
In the case of Telegram, the file looks like this:
type: http # Don't change
name: http_default # Must match the registered plugin in the profile
# One of "trace", "debug", "info", "warn", "error", "off"
log_level: info
# group_wait: # Time to wait collecting alerts before relaying a message to this plugin, eg "30s"
# group_threshold: # Amount of alerts that triggers a message before <group_wait> has expired, eg "10"
# max_retry: # Number of attempts to relay messages to plugins in case of error
# timeout: # Time to wait for response from the plugin before considering the attempt a failure, eg "10s"
#-------------------------
# plugin-specific options
# The following template receives a list of models.Alert objects
# The output goes in the http request body
# Replace XXXXXXXXX with your Telegram chat ID
format: |
{
"chat_id": "-XXXXXXXXX",
"text": "
{{range . -}}
{{$alert := . -}}
{{range .Decisions -}}
{{.Value}} will get {{.Type}} for next {{.Duration}} for triggering {{.Scenario}}.
{{end -}}
{{end -}}
",
"reply_markup": {
"inline_keyboard": [
{{ $arrLength := len . -}}
{{ range $i, $value := . -}}
{{ $V := $value.Source.Value -}}
[
{
"text": "See {{ $V }} on shodan.io",
"url": "https://www.shodan.io/host/{{ $V -}}"
},
{
"text": "See {{ $V }} on crowdsec.net",
"url": "https://app.crowdsec.net/cti/{{ $V -}}"
}
]{{if lt $i ( sub $arrLength 1) }},{{end }}
{{end -}}
]
}
url: https://api.telegram.org/botXXX:YYY/sendMessage # Replace XXX:YYY with your API key
method: POST
headers:
Content-Type: "application/json"All you need to do is replace the xxxxxx placeholders with your own values.
For the settings to take effect, crowdsec needs to be restarted with the command docker compose up -d --force-recreate.
You can test that notifications work.
# list notifications
docker exec crowdsec cscli notifications list
# test a notification channel
docker exec crowdsec cscli notifications test http_defaultLet’s finish up the initial Crowdsec setup and move on to configuring Traefik.
Configuring the Traefik reverse proxy#
You can read about the basic principles of configuring the Traefik reverse proxy in the article on the site. There’s also a link to the video there for, let’s say, a visual walkthrough.
So we’ll assume you know what the Traefik reverse proxy is and understand how it works.
After setting up the Crowdsec Docker container, enabling log parsers, and getting an API token for our bouncer, we need to connect our bouncer to our reverse proxy and configure the corresponding middlewares in Traefik.
There are several ways to add the Crowdsec bouncer to Traefik. It used to be done using a forward-auth middleware and a separate bouncer container. You’ll come across this “old,” or rather outdated, method around the internet. Nowadays everything can be done via the corresponding plugin for Traefik.
First, check whether logging is configured in your Traefik reverse proxy.
Configuring logs in the Traefik reverse proxy#
We’re especially interested in access.log, because that’s exactly what Crowdsec needs.
Here’s an excerpt from the article about Traefik, where I described the static configuration parameters for the reverse proxy in the traefik.yaml file.
log: # define the logging level and path for logs
level: "INFO"
filePath: "/var/log/traefik/traefik.log"
accessLog:
filePath: "/var/log/traefik/access.log"but now let’s set broader parameters for logging and how Traefik handles them.
Now our logging section will look roughly like this:
log:
level: "INFO"
filePath: "/var/log/traefik/traefik.log"
noColor: false # Recommended to be true when using common. When using the 'common' format, disables the colorized output
maxSize: 100 # In megabytes. Maximum size in megabytes of the log file before it gets rotated.
compress: true # gzip compression when rotating. Determines if the rotated log files should be compressed using gzip.
#
accessLog:
addInternals: true #Enables access logs for internal resources (e.g.: ping@internal)
filePath: "/var/log/traefik/access.log"
bufferingSize: 100 # number of log lines Traefik will keep in memory before writing them to the selected output
fields:
names:
StartUTC: drop # Write logs in Container Local Time instead of UTC. The time at which request processing started.
filters:
statusCodes:
- "204-299"
- "400-599"I want access.log to filter responses by their status codes. This will significantly speed things up. You’re of course free to set whatever suits you.
By the way, you can have Traefik write logs in JSON format - that will further speed up how quickly Crowdsec can read the logs.
Configuration notes when using Cloudflare#
If your Traefik reverse proxy sits behind the CloudFlare CDN, you need to configure Cloudflare’s IP addresses in Traefik as trusted IPs. This is necessary because requests to the HTTP service effectively pass through two reverse proxies - CloudFlare and our Traefik. This means the visitor’s real IP address is hidden from us, and Traefik only sees requests coming from CloudFlare’s servers.
As a result, Traefik’s logs will only contain CloudFlare IP addresses, which won’t help CrowdSec analyze and block the actual threat actor sitting behind CloudFlare.
To fix this and see the real user IP addresses, we define all the IPv4 and IPv6 addresses of the CloudFlare CDN network as trusted. This way, Traefik will trust those IP addresses and parse CloudFlare’s special request headers (X-Forwarded-For) that carry the user’s real IP address.
There are several methods for accomplishing this. I personally use a special plugin for Traefik called cloudflarewarp. There are other similar plugins too, but to avoid overcomplicating an already fairly dense article, I’ll show a more straightforward method.
We’ll manually define CloudFlare’s IP addresses as trusted in Traefik’s entry point definitions.
Open Traefik’s static configuration file, traefik.yaml, and define all of CloudFlare’s IPv4 and IPv6 addresses as trusted IPs for your entry points. I’ll only include the part of the traefik.yaml file that’s directly relevant to the topic of the article.
It should look roughly like this:
entryPoints:
http:
address: :80
forwardedHeaders:
trustedIPs: &trustedIps
# Start of Cloudlare's public IP list
- 103.21.244.0/22
- 103.22.200.0/22
- 103.31.4.0/22
- 104.16.0.0/13
- 104.24.0.0/14
- 108.162.192.0/18
- 131.0.72.0/22
- 141.101.64.0/18
- 162.158.0.0/15
- 172.64.0.0/13
- 173.245.48.0/20
- 188.114.96.0/20
- 190.93.240.0/20
- 197.234.240.0/22
- 198.41.128.0/17
- 2400:cb00::/32
- 2606:4700::/32
- 2803:f800::/32
- 2405:b500::/32
- 2405:8100::/32
- 2a06:98c0::/29
- 2c0f:f248::/32
# End of Cloudlare's public IP list
http:
redirections:
entryPoint:
to: https
scheme: https
# HTTPS endpoint, with domain wildcard
https:
address: :443
forwardedHeaders:
# Reuse the list of Cloudflare's public IPs from above
trustedIPs: *trustedIps
http:
tls:
.....
.....After this, restart Traefik and check Traefik’s access.log. In the journal you shouldn’t now see anonymized CloudFlare IP addresses.
The CrowdSec Bouncer plugin#
First, we need to install the corresponding plugin, whose configuration you can view here.
This plugin has extensive configuration options, but I’ll only cover one of its features - blocking “bad” IPs.
Open Traefik’s static configuration file, traefik.yaml, and add the plugin’s definition to it:
# crowdsec bouncer
experimental:
plugins:
bouncer: # this is the name we've given the plugin, to link this definition with the values in Traefik's dynamic configuration later
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
version: v1.7.1 # the current plugin version as of this update, check it against the releases on GitHub before installingYou can restart Traefik and check traefik.log to see whether the plugin loaded.
The CrowdSec Bouncer middleware in Traefik#
After downloading and enabling the CrowdSec bouncer plugin in Traefik’s static configuration file, we need to link it to a specific middleware for everything to work. Because right now, even though the plugin is downloaded and enabled, it’s not doing anything. Think of it like a car idling. The engine’s running, sure, but we’re not going anywhere - just burning fuel, or electricity, if you’ve got an “electric one.”
The definition for the corresponding middleware will be set in the config.yaml dynamic configuration file (yours might be named differently). Here we’ll specify all the necessary settings for our CrowdSec implementation. To do this, let’s edit Traefik’s dynamic configuration file and define the following middleware:
http:
middlewares:
crowdsec:
plugin:
bouncer:
enabled: true
logLevel: INFO
updateIntervalSeconds: 15
updateMaxFailure: 0
defaultDecisionSeconds: 15
httpTimeoutSeconds: 10
crowdsecMode: stream
crowdsecAppsecEnabled: true
crowdsecAppsecHost: crowdsec:7422
crowdsecAppsecFailureBlock: true
crowdsecAppsecUnreachableBlock: true
crowdsecLapiKey: yourlapikey # Replace CrowdSec API key (docker exec crowdsec cscli bouncers add crowdsecBouncer)
crowdsecLapiHost: crowdsec:8080
crowdsecLapiScheme: http
forwardedHeadersTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
clientTrustedIPs:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16As you can see, we’ve set certain parameters in the middleware. There are, as I mentioned above, many, many more configuration options available. You can read more about it in the official documentation.
Make sure to fill in the corresponding field with the value of your API key that we created earlier.
Also note that we set values for forwardedHeadersTrustedIPs and clientTrustedIPs. This defines the private IPv4 subnets as trusted IP addresses.
This is necessary because we want to trust the HTTP headers of our Traefik reverse proxy, such as X-Forwarded-For and X-Real-IP. These headers typically carry the real IP addresses of visitors to our site - and, of course, attackers too - which CrowdSec uses to make its blocking decisions.
You can fine-tune this further and add Traefik’s exact IP address in a /32 range. However, whitelisting all the private-class ranges is the more convenient approach. This especially applies to the dynamic IP addresses of Docker subnets, which can change on container restart.
With the clientTrustedIPs variable, we specify the trusted IP addresses or ranges we consider trustworthy. These IP addresses or network ranges won’t be blocked or denied by CrowdSec. In effect, this is a whitelist-based option.
In my case I trust the local LAN, which is reflected by adding all the private-class ranges to the whitelist. You can restrict them to your current LAN subnet range (CIDR) if needed.
Protecting Traefik’s web services#
Even though we’ve downloaded the plugin and linked it to the corresponding middleware with the necessary settings, our services still aren’t protected. That is, our reverse proxy knows there’s a plugin, and it can even protect services, but Ttraefik doesn’t yet know which services need protecting. Let’s take care of that now.
Protecting individual services with labels#
As a general rule, you can activate the recently added CrowdSec middleware individually for each container via Traefik labels. I’ll show this using a test container that the Traefik developers created specifically for testing purposes.
whoami:
image: traefik/whoami
container_name: whoami-crowdsec-test
command:
- --name=whoami
networks:
- proxy
labels:
- traefik.enable=true
- traefik.docker.network=proxy
- traefik.http.routers.whoami.rule=Host(`whoami.mydomain.ru`)
- traefik.http.routers.whoami.service=whoami
- traefik.http.services.whoami.loadbalancer.server.port=80
** - traefik.http.routers.whoami.middlewares=crowdsec@file**
networks:
proxy:
external: trueAs you can see, in the last label we define exactly the middleware we need, telling Traefik it should apply the corresponding middleware and pointing it to the dynamic configuration file for that middleware’s details.
Protection at the entrypoints level#
But it seems more correct to me to protect all the services running behind our Traefik reverse proxy.
The advantage of this approach is that we protect everything globally and can sleep soundly, as if we’d paid our taxes. No headache over which service is protected and which isn’t. They’re all protected. Referring, of course, to services hidden behind Traefik.
So let’s edit Traefik’s static configuration file, traefik.yaml, which will now look like this.
# Traefik entrypoints (network ports) configuration
entryPoints:
http:
address: :80
forwardedHeaders:
trustedIPs: &trustedIps
# Start of Cloudlare's public IP list
- 103.21.244.0/22
- 103.22.200.0/22
- 103.31.4.0/22
- 104.16.0.0/13
- 104.24.0.0/14
- 108.162.192.0/18
- 131.0.72.0/22
- 141.101.64.0/18
- 162.158.0.0/15
- 172.64.0.0/13
- 173.245.48.0/20
- 188.114.96.0/20
- 190.93.240.0/20
- 197.234.240.0/22
- 198.41.128.0/17
- 2400:cb00::/32
- 2606:4700::/32
- 2803:f800::/32
- 2405:b500::/32
- 2405:8100::/32
- 2a06:98c0::/29
- 2c0f:f248::/32
# End of Cloudlare public IP list
http:
redirections:
entryPoint:
to: https
scheme: https
# HTTPS endpoint, with domain wildcard
https:
address: :443
forwardedHeaders:
# Reuse the list of Cloudflare's public IPs from above
trustedIPs: *trustedIps
http:
middlewares:
- crowdsec@file # reference to a dynamic middleware for enabling crowdsec bouncerAs you can see, I applied the corresponding middleware on port 443, but you could just as well apply it on port 80.
After this, restart the Traefik container.
Verifying Crowdsec is working.#
We’ve spent a lot of time setting everything up, so let’s now check that the whole construction actually works. Just in case it was all “for nothing.”
Below is a list of commands we’ll definitely need.
First let’s check whether our Crowdsec can see our Traefik.
This command shows CrowdSec’s metrics, and also tells us whether Crowdsec sees Traefik’s logs and is parsing them.
docker exec crowdsec cscli metrics # For Metrics and log read checkingAfter entering the command above, you’ll see, or should see, a large number of tables showing which collections were loaded, but the topmost table will show us whether our Crowdsec can see the reverse proxy’s access.log. If it doesn’t, restart Crowdsec first, then Traefik.
Starting with version 1.7 there’s also more detailed parsing stats - how many lines were read and parsed per each log source separately (parsed, unparsed, whitelisted). You can view this with the command:
docker exec crowdsec cscli machines inspect <machine name> # detailed parsing stats per datasource, helps catch a misconfigured acquis.yamlYou can find your machine’s name with the command docker exec crowdsec cscli machines list.
This command updates all our collection lists.
docker exec crowdsec cscli hub update && docker exec crowdsec cscli hub upgrade # Security List update commandInstead of manually updating the lists every time, it makes sense to set up a cronjob with the command
crontab -e if you’re using a Debian/Ubuntu-based distro.
* * * * * docker exec crowdsec cscli hub update && docker exec crowdsec cscli hub upgradeSet whatever update interval you feel is appropriate. You can check with any cronjob calculator.
And now the most important thing. We need to check whether our Crowdsec can actually ban bad IP addresses.
For people working in IT security, simulating an attack is trivial, but we’re simple, down-to-earth folks, so let’s do the following.
First, we need to comment out the trusted private subnets in the middleware, because we’re working from the local network and for testing purposes we want Corwdsec to check all IP addresses by default, with no exceptions.
Then we’ll manually ban our work computer’s IP with the following command.
docker exec crowdsec cscli decisions add --ip 192.168.x.x # add ip to crowdsec decisions block listNow we can view the list of banned addresses with the command
docker exec crowdsec cscli decisions list # List crowdsec ip decisionsYou should see your IP address in this list. Now try to open your Traefik reverse proxy’s dashboard site, but only in your browser’s incognito mode.
If everything’s fine, you’ll be denied access. By default the ban lasts 4 hours. But of course you don’t need to wait those 4 hours - let’s unban ourselves manually.
docker exec crowdsec cscli decisions delete --ip 192.168.x.x # delete ip from crowdsec decisions block listSince everything’s working, and I hope it is, you can now uncomment our private subnets in the middlewares.
If you just want to see the full list of warnings your Crowdsec produces, you can do so with the command
docker exec crowdsec cscli alerts list Well, that’s all, dear friends.
If you enjoyed this article, you can support me on boosty.





