Fransys

Blog technique — Architecture, Cloud & DevOps

BlogServicesContactÀ propos

Suivez-moi

githubGitHublinkedinLinkedinmailMail

© 2026 Fransys • Fransys

Fransys

Catégories

  • Tous les articles
  • Tags
  • productivite10
  • nas10
  • ia8
  • securite7
  • linux6
  • claude-code6
  • auto-hebergement6
  • neovim5
  • docker5
  • editeur4
  • mcp3
  • vpn3
  • reseau3
  • lua2
  • terminal2
naslinuxsecuritereseau

Pare-feu et Fail2ban : verrouiller les accès réseau du NAS

Publié le
20 janvier 2026·6 min de lecture
Avatar François GUERLEZFrançois GUERLEZ

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écidiveMultiplicateurDurée du ban
1er banx110 minutes
2e banx550 minutes
3e banx305 heures
4e banx60~10 heures
5e banx180~30 heures
6e banx360~2.5 jours
7e banx720~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 compromis

Article suivant

Complétion IA dans Neovim : Codeium, Gemini et nvim-cmp→
← Retour au blog

Sommaire

  • Pourquoi un pare-feu sur un NAS domestique ?
  • UFW : un pare-feu lisible
  • Politique par défaut
  • SSH : limiter les connexions
  • Services LAN uniquement
  • Services Docker ouverts
  • Vérification
  • Le piège Docker + UFW
  • Fail2ban : bannir les intrus
  • Configuration de la jail SSH
  • Bans progressifs
  • Action de bannissement
  • Vérification
  • Automatisation Ansible
  • Conclusion