---
title: "Audit sécurité NUC : ModSecurity supprimé, 500 MB récupérés"
description: "Audit complet de la stack sécurité d'un homelab NUC i3 : double inspection WAF supprimée, ModSecurity remplacé par CrowdSec AppSec seul. Résultat : -500 MB PSS nginx, zéro perte de sécurité."
url: "https://www.arleo.eu/posts/audit-securite-modsecurity-crowdsec/"
language: "fr"
datePublished: "2026-05-14T05:31:23+02:00"
dateModified: "2026-05-15T17:58:37+02:00"
tags: ["crowdsec","nginx","security","homelab","modsecurity","waf","hardening"]
categories: ["infrastructure"]
contentSignal: "ai-train=no, search=yes, ai-input=yes"
---

# Audit sécurité NUC : ModSecurity supprimé, 500 MB récupérés
Audit complet de la stack sécurité d'un homelab NUC i3 : double inspection WAF supprimée, ModSecurity remplacé par CrowdSec AppSec seul. Résultat : -500 MB PSS nginx, zéro perte de sécurité.



## ⚡ En bref

Un audit de la stack sécurité du NUC révèle une **double inspection WAF redondante** : ModSecurity + OWASP CRS chargent 11 872 règles en mémoire malgré `SecRuleEngine Off`, en parallèle d'un CrowdSec AppSec déjà actif et couvrant le même périmètre. Après suppression du module nginx ModSecurity et 5 autres correctifs ciblés, nginx passe de **~520 MB à ~27 MB PSS**. Sécurité identique, empreinte divisée par 20.

---

## 🏗️ Architecture avant audit

La stack sécurité reposait sur six couches empilées :

```
Internet
    ↓
Cloudflare WAF  (edge WAF + DDoS + CrowdSec auto-ban)
    ↓
nginx  (Lua bouncer CrowdSec)
    ↓
ModSecurity + OWASP CRS   ←── double inspection
CrowdSec AppSec            ←── double inspection
    ↓
CrowdSec agent v1.7.8  (scénarios : http-cve, nginx, linux, sshd)
    ↓
Bouncers : firewall-bouncer (nftables) + nginx-bouncer (Lua) + cf-sync
    ↓
Backend (Hugo VM 192.168.122.69 / PHP-FPM)
```

![Architecture sécurité NUC — audit mai 2026](/images/audit_schema.png)

Le problème identifié : **ModSecurity et CrowdSec AppSec inspectent chaque requête de façon indépendante**. Le CRS charge ses regex en mémoire même quand `SecRuleEngine Off` est positionné — le module est chargé, les règles compilées, rien n'est inspecté mais la RAM est consommée.

### Empreinte mémoire initiale (smem PSS)

| Composant | PSS |
|---|---|
| nginx (master + 4 workers) | ~520 MB |
| CrowdSec agent | ~170 MB |
| firewall-bouncer | ~30 MB |
| **Total stack sécurité** | **~720 MB** |

---

## 🔍 6 problèmes identifiés

### 1. Double inspection ModSecurity / AppSec

ModSecurity était configuré avec `SecRuleEngine Off` — inactif fonctionnellement — mais le module nginx chargeait quand même **11 872 règles OWASP CRS** à chaque démarrage. CrowdSec AppSec (`appsec-generic-rules` + `appsec-virtual-patching`) couvre le même périmètre avec des règles maintenues communautairement.

**Verdict : ModSecurity supprimé, AppSec conservé.**

### 2. APPSEC_FAILURE_ACTION=allow

La config du bouncer nginx avait `APPSEC_FAILURE_ACTION=allow` — si AppSec échoue à répondre dans le délai imparti (timeout, requête trop volumineuse), la requête passe. Configuré en `passthrough` : en cas d'échec AppSec, la requête est transmise au backend sans blocage mais sans inspection. C'est ce bug qui causait les 502 sur Hugo : AppSec renvoyait `request body exceeded limit` et la logique `allow` créait un état incohérent.

### 3. mcp-hugo.access.log parsé avec le mauvais parser

Le log JSON du MCP Hugo était lu par le parser nginx standard (format combined). 126 entrées sur 132 ressortaient "unparsed" dans les métriques CrowdSec. Fix : acquisition dédiée avec le parser `mcp-oauth`.

### 4. Doublon http-sensitive-files — fausse alerte

Le warning `multiple scenarios named crowdsecurity/http-sensitive-files` était trompeur : un override local (capacity 2, leakspeed 60s) remplace intentionnellement le scénario hub (capacity 4, leakspeed 5s) pour réduire les faux positifs. Un seul scénario actif, rien à corriger.

### 5. proxy_buffers nginx surdimensionnés

Trois vhosts (radarr, sonarr, plex) utilisaient `proxy_buffers 32 512k` ou `16 256k` — jusqu'à **16 MB de buffer par connexion en pic**. Pour un trafic homelab quasi nul, c'est un gaspillage direct de PSS nginx.

### 6. nginx-bouncer : 0 décisions non-vides

Les métriques montraient `crowdsec-nginx-bouncer : Empty answers = 11 / Non-empty = 0`. Comportement normal : les IPs bannies sont bloquées par Cloudflare avant d'atteindre nginx. Le bouncer interroge le LAPI pour chaque visiteur légitime → réponse vide normale. Aucun dysfonctionnement.

---

## 🔧 Fixes appliqués

### Fix 1 — Suppression ModSecurity

```bash
# Commenter le chargement du module dans nginx.conf
#load_module /etc/nginx/modules-enabled/ngx_http_modsecurity_module.so;

# Désactiver dans tous les vhosts (sed sur sites-available/)
#modsecurity on;        # DISABLED — using CrowdSec AppSec only
#modsecurity_rules_file ...;
```

Appliqué sur 10 vhosts actifs via `sed` en une passe. `nginx -t` + `systemctl reload nginx`.

### Fix 2 — APPSEC_FAILURE_ACTION

```ini
# /etc/crowdsec/bouncers/crowdsec-nginx-bouncer.conf
APPSEC_FAILURE_ACTION=passthrough   # was: allow
```

### Fix 3 — Acquisition dédiée mcp-hugo

```yaml
# /etc/crowdsec/acquis.d/mcp-hugo.yaml
filenames:
  - /var/log/nginx/mcp-hugo.access.log
labels:
  type: mcp-oauth
```

### Fix 5 — Réduction proxy_buffers

```nginx
# Avant (radarr, sonarr)
proxy_buffers 32 512k;
proxy_buffer_size 512k;

# Après
proxy_buffers 8 64k;
proxy_buffer_size 16k;
proxy_busy_buffers_size 128k;
```

---

## 📊 Résultats

### Mémoire nginx après restart complet

```
nginx: master    →  PSS  2.3 MB
nginx: worker ×3 →  PSS  6.4 MB chacun
nginx: worker ×1 →  PSS  6.9 MB
Total            →  PSS ~27 MB
```

### Comparatif avant / après

| Composant | Avant | Après |
|---|---|---|
| nginx (total PSS) | ~520 MB | **~27 MB** |
| CrowdSec agent | ~170 MB | ~170 MB |
| firewall-bouncer | ~30 MB | ~30 MB |
| **Stack sécurité totale** | **~720 MB** | **~227 MB** |

**-493 MB libérés.** Le gain vient intégralement du déchargement du module ModSecurity et de la réduction des proxy_buffers.

### Sécurité : aucune régression

| Couche | Avant | Après |
|---|---|---|
| Edge WAF | Cloudflare ✔ | Cloudflare ✔ |
| Inspection applicative | ModSec (off) + AppSec | AppSec seul ✔ |
| Détection comportementale | CrowdSec ✔ | CrowdSec ✔ |
| Ban réseau | firewall-bouncer ✔ | firewall-bouncer ✔ |
| Ban Cloudflare auto | cf-sync ✔ | cf-sync ✔ |

---

## 🧠 Leçon

La stack était qualifiée d'"over-secured" — plusieurs couches de WAF empilées, chacune ajoutée à un moment différent pour une raison valide. Le problème : **une couche désactivée fonctionnellement reste coûteuse en ressources si le module est chargé**. ModSecurity avec `SecRuleEngine Off` consommait autant de RAM qu'en mode actif — 11 872 règles regex compilées au démarrage nginx, que les requêtes soient inspectées ou non.

La règle à retenir : **une couche de sécurité inactive n'est pas gratuite si le module est en mémoire**.

---

## 🏁 Stack finale

```
Internet
    ↓
Cloudflare WAF + DDoS
    ↓
nginx (Lua bouncer — stream mode)
    ↓
CrowdSec AppSec  (appsec-generic-rules + appsec-virtual-patching)
    ↓
CrowdSec agent v1.7.8
    ↓
firewall-bouncer (nftables) / nginx-bouncer / cf-sync
    ↓
Backend
```

Quatre couches actives, zéro redondance, ~500 MB de RAM récupérés.


## Tags

- crowdsec
- nginx
- security
- homelab
- modsecurity
- waf
- hardening

## Categories

- infrastructure
