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Ă©seau docker0). 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 :

    1. Retrait de l’ingérence de Docker dans le pare-feu ("iptables": false).

    2. 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.

    3. 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Ă®nes DOCKER*), 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 table ip6 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.