Today, we’ve taken a significant step forward in securing our web infrastructure by implementing CrowdSec, an open-source, collaborative Intrusion Prevention System (IPS). We integrated it tightly with our existing Docker stack to actively monitor and block malicious traffic before it ever reaches our applications.
Here’s an overview of our updated architecture and how the pieces fit together.
Our Website Architecture
Our website is built using Hugo, a blazing-fast static site generator. To optimize performance and security, we’ve structured our stack using docker-compose into several distinct layers:
- The Content Builder: A Hugo container (
hugomods/hugo) that watches our source directory for changes. Whenever a post (like this one!) is written, it automatically builds the static HTML files to a./publicdirectory. - The Web Server: A lightweight Nginx Alpine container that solely serves those statically built files. Fast, simple, and isolated.
- The Gatekeeper: We use Nginx Proxy Manager (specifically the
zoeyvid/npmplusimage) to handle SSL termination, manage domains, and act as our reverse proxy routing external traffic to our internal Nginx web server.
While Nginx Proxy Manager does a great job at routing traffic, we wanted to ensure we were protected against brute-force attacks, port scanners, and malicious bots. Enter CrowdSec.
Implementing CrowdSec
CrowdSec acts as our active defense layer. Instead of relying on static IP blocklists, CrowdSec analyzes our logs in real-time and shares threat intelligence with a massive global network. If an IP attacks another CrowdSec user anywhere in the world, our server knows to block it preemptively.
The Integration
Integrating CrowdSec into our Docker compose file was remarkably clean. Here is a breakdown of the implementation:
- Log Parsing: Nginx Proxy Manager handles all inbound requests and writes access logs. We mounted these logs (
./npm-data/logs) into the CrowdSec container as read-only. We set theLOGROTATE=trueenvironment variable in NPM to ensure log files remain manageable. - The NPM Collection: We configured CrowdSec to use the
ZoeyVid/npmpluscollection. A collection in CrowdSec is a bundle of parsers and scenarios tailored for a specific service. This allows CrowdSec to instantly understand the Nginx Proxy Manager log format and identify common attack signatures. - The Bouncer: The beauty of using the
zoeyvid/npmplusimage is that it has a CrowdSec bouncer built right in. When the CrowdSec container detects malicious activity (via the logs), it flags the IP. The NPMplus bouncer queries CrowdSec and immediately drops traffic from that IP at the proxy level.
The Compose Configuration
Our CrowdSec service definition in docker-compose.yaml looks like this:
crowdsec:
image: crowdsecurity/crowdsec:latest
container_name: crowdsec
restart: unless-stopped
environment:
- TZ=America/Chicago
- COLLECTIONS=ZoeyVid/npmplus
volumes:
- ./crowdsec/data:/var/lib/crowdsec/data
- ./crowdsec/config:/etc/crowdsec
- ./npm-data/logs:/var/log/npm:ro
Looking Forward
By decoupling our site generation (Hugo) from static serving (Nginx), and layering Nginx Proxy Manager with CrowdSec on top, we’ve created an incredibly resilient, performant, and secure stack. We get the peace of mind knowing that malicious bots are being dropped at the edge, allowing our server resources to be dedicated entirely to serving legitimate users.
We’re excited to see how this performs and contribute back to the CrowdSec network!