Pare-feu et Fail2ban : verrouiller les accès réseau du NAS
- Publié le
- ·6 min de lecture
Pourquoi un pare-feu sur un NAS domestique ?
Un NAS, c'est un serveur qui tourne 24h/24. Y'a pas à avoir honte de vouloir le sécuriser. Il héberge des trucs sensibles : partages de fichiers, media server, peut-être de la domotique. Sans pare-feu, tous les ports sont ouverts par défaut et n'importe quel device du réseau peut y taper. Puis si vous exposez un port vers l'extérieur pour l'accès distant, l'surface d'attaque explose.
J'ai mis en place deux couches complémentaires sur mon TerraMaster F4-424 : UFW pour filtrer les ports, et Fail2ban pour détecter et bannir les tentatives d'intrusion.
UFW : un pare-feu lisible
UFW (Uncomplicated Firewall) c'est un frontend pour iptables/nftables. Avantage principal : les règles sont lisibles. Contrairement à la syntaxe cryptique d'iptables qui vous rend dingue après 5 minutes.
Politique par défaut
Première chose : verrouiller tout par défaut :
ufw default deny incoming
ufw default allow outgoing
Deny incoming = tout le trafic entrant est bloqué sauf ce qu'on autorise explicitement. Allow outgoing = le NAS peut communiquer avec l'extérieur (mises à jour, DNS, NTP...).
SSH : limiter les connexions
Plutôt qu'un simple allow, j'utilise limit qui restreint à 3 connexions par minute depuis la même IP :
ufw limit ssh
C'est une première défense contre le brute-force, avant même que Fail2ban arrive.
Services LAN uniquement
Les partages de fichiers n'ont pas besoin d'être accessibles depuis l'internet. Je les restreins au sous-réseau local :
# Samba (Windows/macOS)
ufw allow from 192.168.1.0/24 to any port 445
ufw allow from 192.168.1.0/24 to any port 139
# NFS (Linux)
ufw allow from 192.168.1.0/24 to any port 2049
Simple et efficace. Ça prend 30 secondes à configurer et ça sauve ta vie.
Services Docker ouverts
Certains conteneurs doivent rester accessibles, même de l'extérieur (VPN, accès distant) :
ufw allow 8096/tcp # Jellyfin
ufw allow 6881/tcp # qBittorrent
ufw allow 22000/tcp # Syncthing
ufw allow 8123/tcp # Home Assistant
Vérification
Un ufw status verbose et vous voyez tout :
Status: active
Logging: on (low)
Default: deny (incoming), allow (outgoing), disabled (routed)
To Action From
-- ------ ----
22/tcp LIMIT Anywhere
445 ALLOW 192.168.1.0/24
139 ALLOW 192.168.1.0/24
2049 ALLOW 192.168.1.0/24
8096/tcp ALLOW Anywhere
6881/tcp ALLOW Anywhere
22000/tcp ALLOW Anywhere
8123/tcp ALLOW Anywhere
Clair. Propre.
Le piège Docker + UFW
Classique. Tout le monde tombe dedans. Docker bypasse UFW. Par défaut, quand Docker publie un port (-p 8080:80), il injecte ses règles directement dans iptables avant UFW. Résultat ? Vos containers sont accessibles depuis partout même si UFW bloque le port.
Deux solutions :
Option 1 : Désactiver iptables dans Docker (dans /etc/docker/daemon.json) :
{
"iptables": false
}
Attention : Docker ne gère plus le NAT des containers. Faut configurer manuellement.
Option 2 (celle que je préfère) : Ajouter des règles dans DOCKER-USER, qui s'évalue avant les règles Docker :
# /etc/ufw/after.rules (à la fin du fichier)
*filter
:DOCKER-USER - [0:0]
-A DOCKER-USER -s 192.168.1.0/24 -j ACCEPT
-A DOCKER-USER -j DROP
COMMIT
Voilà. Docker reste fonctionnel, mais les containers sont limités au LAN. Après 2h à chercher pourquoi mon Jellyfin était accessible de partout, je peux vous le confirmer : c'est important.
Fail2ban : bannir les intrus
UFW filtre les ports. Mais ça détecte pas les tentatives de brute-force. C'est le job de Fail2ban : surveiller les logs et bannir temporairement les IP suspectes.
Configuration de la jail SSH
Le fichier principal : /etc/fail2ban/jail.local :
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
backend = systemd
# 3 tentatives max avant ban
maxretry = 3
# Fenêtre de détection : 10 minutes
findtime = 600
Bans progressifs
Plutôt qu'un ban fixe, j'utilise une stratégie de bans progressifs. Logique : quelqu'un qui se trompe de mot de passe une fois reste banni 10 minutes. Un attaquant qui insiste se retrouve banni de plus en plus longtemps :
# Ban progressif
bantime.increment = true
bantime.multipliers = 1 5 30 60 180 360 720
bantime = 600
Avec un bantime de base 600 secondes (10 min) et ces multiplicateurs :
| Récidive | Multiplicateur | Durée du ban |
|---|---|---|
| 1er ban | x1 | 10 minutes |
| 2e ban | x5 | 50 minutes |
| 3e ban | x30 | 5 heures |
| 4e ban | x60 | ~10 heures |
| 5e ban | x180 | ~30 heures |
| 6e ban | x360 | ~2.5 jours |
| 7e ban | x720 | ~5 semaines |
Un bot qui insiste se retrouve banni 5 semaines. En pratique, 99% abandonnent avant.
Action de bannissement
L'action par défaut utilise iptables pour bloquer l'IP. On peut aussi ajouter une notification email :
action = %(action_mwl)s
action_mwl = ban + email avec logs et whois de l'attaquant. Utile pour monitorer.
Vérification
Pour voir l'état de la jail SSH :
fail2ban-client status sshd
Status for the jail: sshd
|- Filter
| |- Currently failed: 0
| |- Total failed: 47
| `- File list: /var/log/auth.log
`- Actions
|- Currently banned: 2
|- Total banned: 12
`- Banned IP list: 203.0.113.42 198.51.100.7
47 tentatives échouées, 12 bans au total, 2 IP bannies maintenant.
Automatisation Ansible
Comme tout le reste, le pare-feu et Fail2ban sont déployés via Ansible. Le rôle est idempotent : relancer autant de fois qu'on veut, zéro effet de bord.
# roles/firewall/tasks/main.yml
- name: Install UFW and Fail2ban
ansible.builtin.apt:
name:
- ufw
- fail2ban
state: present
tags: [firewall]
- name: Set UFW default policies
community.general.ufw:
direction: '{{ item.direction }}'
policy: '{{ item.policy }}'
loop:
- { direction: incoming, policy: deny }
- { direction: outgoing, policy: allow }
tags: [firewall, ufw]
- name: Configure UFW rules
community.general.ufw:
rule: '{{ item.rule }}'
port: '{{ item.port }}'
proto: "{{ item.proto | default('tcp') }}"
from_ip: "{{ item.from_ip | default('any') }}"
loop: '{{ firewall_rules }}'
tags: [firewall, ufw]
- name: Deploy Fail2ban jail configuration
ansible.builtin.template:
src: jail.local.j2
dest: /etc/fail2ban/jail.local
mode: '0644'
notify: Restart fail2ban
tags: [firewall, fail2ban]
Les règles sont dans les variables du rôle, c'est déclaratif et versionnable dans Git.
Conclusion
Un pare-feu sur un NAS domestique, c'est pas de la paranoïa. C'est de l'hygiène basique. UFW rend le filtrage de ports accessible, Fail2ban ajoute une détection active, et Ansible garantit que ça survive aux réinstallations. Le piège Docker/UFW est le truc le plus critique : si vous utilisez Docker sans avoir adressé ça, vos containers sont probablement exposés sans que vous le sachiez. À peu près 100% des gens que j'ai vu configurer un NAS finissent par tomber dessus.
Article précédent
← LSP natif dans Neovim 0.11 : zéro plugin, zéro compromisArticle suivant
Complétion IA dans Neovim : Codeium, Gemini et nvim-cmp→