Pare-feu & Réseau – Rapport d’intervention
Client : Technolibre
Système : technolibre01
(Linux, Docker)
Date : 10 septembre 2025
1) Résumé exécutif
-
Problème initial : appels XML-RPC entre services sur la même machine impossibles, alors que les appels depuis un poste externe fonctionnaient.
-
Cause racine : pare-feu nftables avec
FORWARD
en DROP et absence de règle permettant le trafic inter-conteneurs (réseaudocker0
). Par ailleurs, Docker injectait automatiquement ses propres règles de NAT/filtrage, rendant la politique de sécurité opaque et non persistée dans/etc/nftables.conf
. -
Solution mise en place :
-
Retrait de l’ingérence de Docker dans le pare-feu (
"iptables": false
). -
Déploiement d’un ruleset nftables maître, sécurisé et journalisé, avec :
-
INPUT
/FORWARD
en DROP par défaut. -
ouverture publique de SSH (22), HTTP/HTTPS (80/443) et Icinga2 API (5665) (conformément à la demande).
-
autorisation inter-conteneurs (bridge
docker0
) pour XML-RPC/Postgres. -
NAT de sortie (masquerade) pour permettre aux conteneurs d’accéder à Internet.
-
-
Mise en place d’une journalisation dédiée (rsyslog) et d’une rotation des logs (logrotate).
-
-
Résultat :
-
Les flux XML-RPC inter-conteneurs fonctionnent.
-
Les backends (ERPLibre, Postgres, ports internes) ne sont pas exposés directement à Internet.
-
Seuls 22/80/443/5665 sont publiquement accessibles, par choix explicite.
-
Le pare-feu est désormais lisible, persistant, auditables.
-
2) Contexte & constats (extraits pertinents)
-
Conteneurs en cours (extrait) : 3 stacks ERPLibre/ERPLibre + PostGIS, ports mappés côté hôte (
10000–12002
) pour tests. -
nftables (avant) :
FORWARD
en DROP, règles Docker injectées (chaînesDOCKER*
), DNAT spécifiques, absence de règle générale inter-conteneurs. -
UFW : inactif.
/etc/nftables.conf
quasi vide (non maître). -
Netstat (écoute) : 22, 80, 443, 5665, 10000–12002, 9010/9012, etc.
-
Nginx : reverse-proxy frontal unique (80/443) vers backends locaux 127.0.0.1 (ex. 9010/9012/12000/12002).
→ Les ports de backend ne doivent pas être exposés publiquement : seule la couche Nginx l’est.
3) Configuration déployée
3.1 Retirer l’ingérence Docker (obligatoire)
/etc/docker/daemon.json
{
"iptables": false
}
Appliquer :
systemctl restart docker
3.2 Règles nftables finales (sécurisées + logs + NAT)
/etc/nftables.conf
flush ruleset
# Interfaces / réseaux (adapter si nécessaire)
define DOCKER0 = "docker0"
define WAN_IF = "eth0" # interface de sortie Internet (vers le WAN)
define DOCKER_NET = 172.17.0.0/16 # réseau docker par défaut (IPv4)
# ========== NAT (sortie Internet des conteneurs) ==========
table ip nat {
chain postrouting {
type nat hook postrouting priority srcnat;
oifname $WAN_IF ip saddr $DOCKER_NET masquerade
}
}
# ========== FILTRAGE ==========
table inet filter {
# --- Journaux dédiés (anti-flood) ---
chain LOG_INPUT_DROP {
limit rate 10/second burst 20 packets
log prefix "NFT-IN-DROP: " flags all level info
counter drop
}
chain LOG_FWD_DROP {
limit rate 10/second burst 20 packets
log prefix "NFT-FWD-DROP: " flags all level info
counter drop
}
# --- INPUT: trafic Ă destination de l'hĂ´te ---
chain input {
type filter hook input priority 0;
policy drop;
# loopback
iif lo accept
# connexions établies/associées
ct state established,related accept
# Services publics (ouverts Ă tous, IPv4/IPv6)
tcp dport 22 accept # SSH (ouvert partout, conforme Ă la demande)
tcp dport { 80, 443 } accept # Nginx web (public)
tcp dport 5665 accept # Icinga2 API (ouvert partout, conforme Ă la demande)
# tout le reste : log + drop
jump LOG_INPUT_DROP
}
# --- FORWARD: trafic routé ---
chain forward {
type filter hook forward priority 0;
policy drop;
# connexions établies/associées
ct state established,related accept
# inter-conteneurs sur le mĂŞme bridge docker
iifname $DOCKER0 oifname $DOCKER0 accept
# conteneurs -> Internet (docker0 -> WAN) autorisé
iifname $DOCKER0 oifname $WAN_IF accept
# le reste : log + drop
jump LOG_FWD_DROP
}
# --- OUTPUT: autorisé ---
chain output {
type filter hook output priority 0;
policy accept;
}
}
Activer & vérifier :
systemctl enable nftables
systemctl restart nftables
nft list ruleset
Note IPv6 : le NAT ci-dessus ne concerne que l’IPv4 (
table ip nat
).
Si Docker IPv6 est activé et doit sortir vers Internet, prévoir une tableip6 nat
appropriée.
4) Journalisation & rotation
4.1 rsyslog – fichier dédié
/etc/rsyslog.d/30-nftables.conf
# Logs nftables dédiés (préfixes définis dans nft)
:msg, contains, "NFT-IN-DROP: " /var/log/nftables.log
:msg, contains, "NFT-FWD-DROP: " /var/log/nftables.log
& stop
Appliquer :
systemctl restart rsyslog
4.2 logrotate – rotation hebdomadaire
/etc/logrotate.d/nftables
/var/log/nftables.log {
weekly
rotate 12
compress
delaycompress
missingok
notifempty
create 0640 syslog adm
sharedscripts
postrotate
/bin/systemctl reload rsyslog >/dev/null 2>&1 || true
endscript
}
5) Tests de recette (post-déploiement)
5.1 Exposition des ports
Depuis l’extérieur (Internet) :
nmap -Pn -p 22,80,443,5665,9000-9002,9010,9012,10000-12002,5432 VOTRE_IP_PUBLIQUE
Attendu ouverts : 22, 80, 443, 5665.
Attendu fermés : 9000–9002, 9010/9012, 10000–12002 (backends), 5432 (Postgres), etc.
5.2 Inter-conteneurs (XML-RPC, DB)
docker exec -it <cont-A> bash -lc 'curl -sS http://<IP_cont-B>:8069 | head'
# ou test XML-RPC/DB selon votre stack
5.3 Sortie Internet des conteneurs (NAT)
docker exec -it <cont> bash -lc 'curl -I https://example.com'
5.4 Journalisation
tail -f /var/log/nftables.log
journalctl -k -f | grep -E 'NFT-(IN|FWD)-DROP'
6) Exploitation – mode d’emploi
6.1 Ouvrir un nouveau service public (ex. TCP 8443)
Ajouter avant jump LOG_INPUT_DROP
dans chain input
:
tcp dport 8443 accept
Puis :
systemctl restart nftables
6.2 Restreindre ultérieurement SSH/Icinga (optionnel)
Remplacer :
tcp dport 22 accept
tcp dport 5665 accept
par (exemples) :
ip saddr 192.168.10.0/24 tcp dport 22 accept
ip saddr 192.168.10.25 tcp dport 5665 accept
6.3 Sauvegarde/rollback
Sauvegarder l’état courant :
nft list ruleset > /root/nft-$(date +%F-%H%M%S).nft
Restaurer :
nft -f /root/nft-YYYY-MM-DD-HHMMSS.nft
7) Matrice d’exposition (état final)
Service | Port(s) | Exposé Internet | Commentaire |
---|---|---|---|
SSH | 22 | Oui | Ouvert Ă tous (demande client) |
HTTP/HTTPS | 80 / 443 | Oui | Nginx reverse-proxy |
Icinga2 API | 5665 | Oui | Ouvert Ă tous (demande client) |
ERPLibre backends | 10000–12002 | Non | Accès via Nginx uniquement |
Services internes | 9000/9002/9010/9012 | Non | Locaux / via reverse-proxy |
Postgres | 5432 | Non | Conteneurs uniquement |
Conteneurs → WAN | — | Sortie autorisée | NAT (masquerade) via eth0 |
8) Recommandations (sécurité)
(Non bloquant, Ă planifier selon politique interne)
-
SSH ouvert à tous : imposer clé publique, désactiver mot de passe, considérer Fail2ban.
-
Icinga2 API ouverte : restreindre à l’IP 192.168.10.25 (ou sous-réseau) si possible.
-
IPv6 : si conteneurs IPv6 + sortie requise, ajouter
table ip6 nat
équivalente. -
Surveillance : superviser
/var/log/nftables.log
(alertes si pics de DROP).
9) Conclusion
Le pare-feu est désormais maîtrisé par la configuration système et non par Docker, avec une politique par défaut restrictive, des ouvertures explicites conformes à la demande, une journalisation dédiée et une sortie Internet contrôlée pour les conteneurs.
La cause du dysfonctionnement XML-RPC (blocage inter-conteneurs) est résolue de manière pérenne.