In this guide, we will set up CrowdSec with Nginx Proxy Manager Plus (NPMPlus) using Docker-Compose. CrowdSec is a powerful, open-source intrusion prevention system that leverages collaborative security to detect and mitigate threats. NPMPlus is an enhanced version of Nginx Proxy Manager, a user-friendly reverse proxy tool for managing web services. By integrating CrowdSec with NPMPlus, you can enhance the security of your network by automatically blocking malicious traffic while maintaining seamless management of your web applications.
Why Combine CrowdSec and NPMPlus?
- Enhanced Security: CrowdSec protects your services by analyzing traffic patterns and blocking suspicious activity based on its extensive community-driven database.
- Ease of Use: NPMPlus simplifies the management of reverse proxy configurations, SSL certificates, and domains.
- Proactive Defense: The integration enables real-time protection against bots, brute force attacks, and other threats, ensuring your network remains secure and resilient.
Note: We will be using a fork of Nginx Proxy Manager, which called NPMplus. Thanks to its developer ZoeyVid
Prerequisites
- A server with Docker and Docker-Compose installed.
- Basic knowledge of Docker and YAML configuration.
- Access to the NPMPlus Docker image (requires a valid subscription).
Step 1: Prepare Your Environment
Create a Project Directory
mkdir crowdsec-npmplus
cd crowdsec-npmplus
Create a docker-compose.yml
File
Use the following configuration to define the services for CrowdSec and NPMPlus:
Note : Here I am usingmariadb
as database, you can always remove the same if you want to usesqllite3
. Andgeoipupdate
is an optional, but recommended
services:
npmplus:
container_name: npmplus
image: zoeyvid/npmplus
restart: always
ports:
- "81:81"
- "80:80"
- "443:443"
volumes:
- "./npm:/data"
- "./www:/var/www"
environment:
- "TZ=Asia/Kolkata" #! Change Me
- "[email protected]" #! Change Me
- "ACME_SERVER=https://api.buypass.com/acme/directory"
- "ACME_MUST_STAPLE=false"
- "ACME_OCSP_STAPLING=false"
- "ACME_KEY_TYPE=rsa"
- "NPM_PORT=81"
- "GOA_PORT=91"
- "GOAIWSP=48692"
- "DB_MYSQL_HOST=mariadb"
- "DB_MYSQL_PORT=3306"
- "DB_MYSQL_USER=nginxadmin"
- "DB_MYSQL_PASSWORD=strong password" #! Change Me
- "DB_MYSQL_NAME=nginxdb"
- "LOGROTATE=true"
- "LOGROTATIONS=7"
- "X_FRAME_OPTIONS=sameorigin"
- "GOA=true"
- "GOACLA=--agent-list --real-os --double-decode --anonymize-ip --anonymize-level=2 --keep-last=7 --with-output-resolver --no-query-string"
depends_on:
- mariadb
networks:
server-farm:
ipv4_address: 172.50.50.12
mariadb:
image: 'mariadb:10.11.5'
restart: unless-stopped
environment:
- "MYSQL_ROOT_PASSWORD=strong password" #! Change Me
- "MYSQL_DATABASE=nginxdb"
- "MYSQL_USER=nginxadmin"
- "MYSQL_PASSWORD=strong password" #! Change Me
volumes:
- ./mysql:/var/lib/mysql
networks:
server-farm:
ipv4_address: 172.50.50.10
phpmyadmin:
image: lscr.io/linuxserver/phpmyadmin
container_name: phpmyadmin
ports:
- 8445:80
environment:
- PMA_ARBITRARY=1
- PMA_HOST=mariadb
- PUID=1001
- PGID=1001
- TZ=Asia/Kolkata
volumes:
- ./phpadmin/config:/config
depends_on:
- mariadb
networks:
server-farm:
ipv4_address: 172.50.50.11
crowdsec:
container_name: crowdsec
image: crowdsecurity/crowdsec
restart: always
ports:
- "7422:7422"
- "8080:8080"
environment:
- "TZ=Asia/Kolkata"
- "COLLECTIONS=ZoeyVid/npmplus"
volumes:
- "./npm/crowdsec/conf:/etc/crowdsec"
- "./npm/crowdsec/data:/var/lib/crowdsec/data"
- "./npm/nginx:/opt/npm/nginx:ro"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
network_mode: host #! Make sure this is set to "host" in order to work
geoipupdate:
container_name: npmplus-geoipupdate
image: maxmindinc/geoipupdate
restart: always
environment:
- "TZ=Asia/Kolkata"
- "GEOIPUPDATE_EDITION_IDS=GeoLite2-Country GeoLite2-City GeoLite2-ASN"
- "GEOIPUPDATE_ACCOUNT_ID=Your_account_Key" #! Change Me
- "GEOIPUPDATE_LICENSE_KEY=your_license_key" #! Change Me
- "GEOIPUPDATE_FREQUENCY=24"
volumes:
- "./npm/goaccess/geoip:/usr/share/GeoIP"
networks:
server-farm:
ipv4_address: 172.50.50.14
networks:
server-farm:
driver: bridge
ipam:
config:
- subnet: 172.50.50.0/24
bring the stack by executing docker compose up
command.
Log in to the Admin UI When your docker container is running, connect to it on port 81
for the admin interface. Sometimes this can take a little bit because of the entropy of keys. You may need to open port 81 in your firewall. You may need to use another IP-Address. https://127.0.0.1:81 Default Admin User
Email: [email protected]
Password: iArhP1j7p1P6TA92FA2FMbbUGYqwcYzxC4AVEe12Wbi94FY9gNN62aKyF1shrvG4NycjjX9KfmDQiwkLZH1ZDR9xMjiG2QmoHXi
Step 2: CrowdSec configuration
- Open and modify
npm/crowdsec/conf/acquis.d/npmplus.yaml
(location may change based on your installation)
filenames:
- /opt/npm/nginx/access.log
labels:
type: npmplus
---
source: docker
container_name:
- npmplus
labels:
type: npmplus
---
source: docker
container_name:
- npmplus
labels:
type: modsecurity
---
listen_addr: 0.0.0.0:7422
appsec_config: crowdsecurity/appsec-default
name: appsec
source: appsec
labels:
type: appsec
Step 3: Generate an API Key for the Bouncer
Once the CrowdSec container is running, generate an API key for the Nginx bouncer:
docker exec -it crowdsec cscli bouncers add nginx-bouncer
Save the generated api_key
Open and Modify npm/crowdsec/crowdsec.conf
file
- set
ENABLED
totrue
- set
API_KEY
to the generated value.
the file will be look like this
ENABLED=true #!Set to True
API_URL=http://localhost:8080
API_KEY=Your_API_Key #!Change to your API Key
CACHE_EXPIRATION=1
# bounce for all type of remediation that the bouncer can receive from the local API
BOUNCING_ON_TYPE=all
FALLBACK_REMEDIATION=ban
REQUEST_TIMEOUT=2500
UPDATE_FREQUENCY=10
# By default internal requests are ignored, such as any path affected by rewrite rule.
# set ENABLE_INTERNAL=true to allow checking on these internal requests.
ENABLE_INTERNAL=false
# live or stream
MODE=live
# exclude the bouncing on those location
EXCLUDE_LOCATION=
#those apply for "ban" action
# /!\ REDIRECT_LOCATION and RET_CODE can't be used together. REDIRECT_LOCATION take priority over RET_CODE
BAN_TEMPLATE_PATH=/data/crowdsec/ban.html
REDIRECT_LOCATION=
RET_CODE=
#those apply for "captcha" action
#valid providers are recaptcha, hcaptcha, turnstile
CAPTCHA_PROVIDER=
# Captcha Secret Key
SECRET_KEY=
# Captcha Site key
SITE_KEY=
CAPTCHA_TEMPLATE_PATH=/data/crowdsec/captcha.html
CAPTCHA_EXPIRATION=3600
APPSEC_URL=http://10.30.30.204:7422
APPSEC_FAILURE_ACTION=passthrough
APPSEC_CONNECT_TIMEOUT=1000
APPSEC_SEND_TIMEOUT=30000
APPSEC_PROCESS_TIMEOUT=10000
ALWAYS_SEND_TO_APPSEC=false
SSL_VERIFY=true
Once all done, restart the docker stack
by executing docker compose restart
Step 4: Verify Integration
To confirm CrowdSec is protecting your services:
- Add your external IP address (connect to any free vpn service) to
crowdsec
block list
docker exec -it crowdsec cscli decisions add -i 123.456.789.123
- If everything works, you will see
crowdsec
block page.
Conclusion
By combining the security features of CrowdSec with the user-friendly interface of NPMPlus, you can create a robust and secure reverse proxy setup. This integration provides proactive defense against threats while simplifying service management, making it an essential addition to any modern network environment.
Side Note : Those who using Immich
photo backup solution, Please go through this link and make necessary changes #1241
let me know if you have any questions.