CRITICAL INFRA
Loading critical CVEs…
ALL EXPLOITED
Loading…

Case study: nginx reverse proxy + Suricata + CrowdSec

After cleaning a server compromised through a web shell, the next step was to make sure it wouldn't happen again. We put an nginx reverse proxy with TLS and hardening in front of the apps, a Suricata that sees all traffic, and a CrowdSec that reads the logs and auto-blocks hostile sources. Three layers that cover each other.

The requirement: defense in depth, not a single wall

A single defense layer fails at the first mistake. We wanted three: a proxy that hides and filters the app, an IDS that sees attacks at the network level, and an engine that learns from logs and blocks automatically. If one misses, the other catches.

In short: the same attacker is seen by the proxy, the IDS and the reputation engine; we block it once, automatically, before it does damage.

Architecture

The internet enters through nginx (reverse proxy + TLS), which forwards to the PHP apps behind. Suricata inspects all traffic (IDS), and CrowdSec reads the nginx logs, applies scenarios and puts bad IPs into a firewall bouncer.

Internettrafic + atacuringinxreverse proxy + TLSaplicatii PHPbackendSuricataIDS pe traficCrowdSec -> bouncer

nginx: reverse proxy, TLS and hardening

nginx hides the app, terminates TLS and applies the basic limits (rate-limit, security headers). The PHP app is no longer directly exposed.

# /etc/nginx/sites-available/app
limit_req_zone $binary_remote_addr zone=app:10m rate=10r/s;
server {
  listen 443 ssl http2;
  server_name app.example.com;
  ssl_certificate     /etc/letsencrypt/live/app.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
  add_header X-Content-Type-Options nosniff;
  add_header X-Frame-Options SAMEORIGIN;
  location / {
    limit_req zone=app burst=20 nodelay;
    proxy_pass http://127.0.0.1:8080;
    proxy_set_header X-Real-IP $remote_addr;
  }
}
nginx -t && systemctl reload nginx

Suricata: IDS on all traffic

Suricata sees the packets and raises alerts on signatures (scans, exploits, web shells). You put it on the interface and load the Emerging Threats rules.

apt -y install suricata
suricata-update            # download ET open rules
# set the listening interface (af-packet) in /etc/suricata/suricata.yaml
systemctl enable --now suricata
# watch the alerts:
tail -f /var/log/suricata/fast.log

In short: Suricata doesn't block on its own here (it's an IDS), but it gives eyes on the network: you see the scan or exploit as it happens.

CrowdSec: reads the logs and blocks

CrowdSec reads the nginx logs (and optionally Suricata's eve.json), recognizes attack patterns and puts the bad IP into a firewall bouncer. Plus community reputation.

curl -s https://install.crowdsec.net | sudo sh
apt -y install crowdsec
cscli collections install crowdsecurity/nginx crowdsecurity/base-http-scenarios
# firewall bouncer (nftables) -> drops the IPs
apt -y install crowdsec-firewall-bouncer-nftables
# what it collects + what it blocked
cscli metrics
cscli decisions list
cscli alerts list --since 24h
cscli console enroll <KEY>   # dashboard + CTI blocklists

Result

The PHP app is no longer directly exposed: nginx filters and limits, Suricata gives visibility on attacks, and CrowdSec auto-blocks hostile sources before they persist. A scanner looking for web shells is caught and blocked at the firewall in seconds, and community reputation stops many attacks before they start. Everything goes into 24/7 monitoring.

Let's discuss your project →