Skip to content

Docker Network — Réseaux et Isolation

Configuration complète des réseaux Docker, équipements physiques (FortiGate, Cisco) et isolation VLAN CPE.

Réseaux Docker principaux

rgz_rgz-net (Réseau principal)

Type: Bridge network

CIDR: 172.23.0.0/16 (prefix rgz_ ajouté par Docker Compose)

⚠️ LL#72 Important: Le vrai nom du réseau est rgz_rgz-net, pas rgz-net

bash
# Vérifier le nom réel
docker network ls | grep rgz

# Inspecter le réseau
docker network inspect rgz_rgz-net

Services connectés (22 services):

Core (14):
  rgz-api:172.23.0.10
  rgz-db:172.23.0.11
  rgz-redis:172.23.0.12
  rgz-radius:172.23.0.13
  rgz-gateway:172.23.0.14 (+ host network)
  rgz-dns:172.23.0.15
  rgz-beat:172.23.0.16
  rgz-portal:172.23.0.17
  rgz-web:172.23.0.18
  rgz-kea:172.23.0.19
  rgz-ids:172.23.0.20
  rgz-nginx:172.23.0.21
  rgz-wireguard:172.23.0.22
  rgz-canary:172.23.0.23

Monitoring (8):
  rgz-prometheus:172.23.0.30
  rgz-grafana:172.23.0.31
  rgz-alertmanager:172.23.0.32
  rgz-elasticsearch:172.23.0.33
  rgz-kibana:172.23.0.34
  rgz-logstash:172.23.0.35
  rgz-netflow:172.23.0.36
  rgz-docs:172.23.0.37

Isolation: Services internes uniquement (pas d'accès direct depuis Internet)

DNS interne: Docker embedded DNS resolver (127.0.0.11:53)

bash
# Dans un container
nslookup rgz-api
# Résout: 172.23.0.10

# Ou via Docker DNS
ping rgz-postgres  # ✅ Works

traefik-public (Réseau externe)

Type: Bridge external (partagé entre docker-compose files)

CIDR: 10.0.9.0/24

Services connectés: Traefik + services exposés (API, web, portal, grafana, docs)

Role: Reverse proxy HTTPS vers Internet

bash
# Créer réseau (une fois)
docker network create traefik-public --driver bridge

# Services exposés
docker network inspect traefik-public

Docker Compose expose services:

yaml
networks:
  traefik-public:
    external: true

services:
  rgz-api:
    networks:
      - rgz-net          # réseau privé core
      - traefik-public   # réseau public Traefik
    ports:
      - "8000:8000"      # accessible localhost:8000 (dev)

Host network mode (rgz-gateway seulement)

Type: host

Raison: NET_ADMIN capability + nftables rules

yaml
# rgz-gateway doit être sur host network
networks:
  - host

# JAMAIS sur rgz-net (nftables ne fonctionne pas en réseau bridge)

Accès:

bash
# Service sur host network = directement accessible
nft list tables           # Depuis l'hôte
curl 127.0.0.1:8080      # Port sur hôte

Configuration IP fixes

Chaque service a une IP fixe statique pour fiabilité:

yaml
# docker-compose.core.yml
version: '3.8'

networks:
  rgz-net:
    driver: bridge
    ipam:
      config:
        - subnet: 172.23.0.0/16

services:
  rgz-api:
    networks:
      rgz-net:
        ipv4_address: 172.23.0.10

  rgz-db:
    networks:
      rgz-net:
        ipv4_address: 172.23.0.11

  # ... etc

Pourquoi IPs fixes?

✅ Healthchecks fiables (pas de DNS résolution)

✅ Configuration statique dans configs (Prometheus scrape)

✅ RADIUS NAS-Identifier validation

✅ Firewall rules nftables

Architecture réseau physique (FortiGate + Cisco)

Le réseau physique en amont de la stack Docker repose sur deux équipements :

┌──────────────────────────────────────────────────────────────────┐
│   INTERNET (ISP)                                                  │
└───────────┬──────────────────────────────────────────────────────┘

┌───────────▼──────────────────────────────────────────────────────┐
│   🛡️ FortiGate Firewall                                          │
│   ├─ VLAN Gateway (termine VLANs 100-499)                        │
│   ├─ NAT : 10.(100+n).0.0/24 → IP publique                      │
│   ├─ ACL : deny inter-VLAN, allow VLAN→Internet                  │
│   ├─ RADIUS NAS : Access-Request → rgz-radius (1812/udp)         │
│   ├─ DHCP relay → rgz-kea (67/udp)                               │
│   ├─ NetFlow v5 → rgz-netflow (2055/udp)                         │
│   ├─ Syslog → Logstash → Elasticsearch                           │
│   └─ SNMP v3 → Prometheus                                        │
└───────────┬──────────────────────────────────────────────────────┘
            │ Trunk 802.1Q (VLANs 100-499 tagués)

┌───────────▼──────────────────────────────────────────────────────┐
│   🔌 Cisco L2/L3 Switches                                        │
│   ├─ Ports trunk → FortiGate (tous VLANs)                        │
│   ├─ Ports access → CPE (1 VLAN par revendeur)                   │
│   ├─ PoE : alimentation CPE via câble Ethernet                   │
│   ├─ Spanning Tree RSTP                                           │
│   └─ SNMP v3 → Prometheus                                        │
└───────────┬──────────────────────────────────────────────────────┘
            │ Câbles Ethernet PoE

┌───────────▼──────────────────────────────────────────────────────┐
│   📡 CPE LiteBeam 5AC (160+ antennes terrain)                    │
│   ├─ SSID : "ACCESS [NomRevendeur]"                              │
│   ├─ VLAN : port access sur Cisco (ex: VLAN 142)                 │
│   └─ Abonnés WiFi → DHCP → portail captif → Internet            │
└──────────────────────────────────────────────────────────────────┘

Isolation VLAN CPE

Schéma complet (couche physique + Docker) :

┌────────────────────────────────────────┐
│   ABONNÉ WiFi (smartphone/laptop)      │
│   MAC aléatoire (iOS 14+/Android 10+)  │
└────────┬───────────────────────────────┘
         │ Connexion WiFi

    ┌────▼────────────────────────────────┐
    │  CPE LiteBeam 5AC                   │
    │  SSID: "ACCESS [Revendeur]"         │
    │  → Trafic abonné vers switch        │
    └────┬────────────────────────────────┘
         │ Ethernet PoE (untagged)

    ┌────▼────────────────────────────────┐
    │  Cisco Switch (port access)         │
    │  ↓ Tag VLAN (100+n) sur le port     │
    │  ↓ Forward via trunk 802.1Q         │
    └────┬────────────────────────────────┘
         │ Trunk 802.1Q (tagged VLAN n)

    ┌────▼────────────────────────────────┐
    │  FortiGate (VLAN Gateway)           │
    │  ↓ Termine VLAN n                   │
    │  ↓ ACL: deny inter-VLAN             │
    │  ↓ DHCP relay → rgz-kea            │
    │  ↓ RADIUS NAS → rgz-radius         │
    │  ↓ NAT → Internet                  │
    └────┬────────────────────────────────┘
         │ DHCP relay

    ┌────▼────────────────────────────────┐
    │  rgz-kea (DHCP Server)              │
    │  ↓ Identifie VLAN via Option 82     │
    │  ↓ Alloue IP par VLAN              │
    │  IP: 10.(100+n).0.x/24             │
    │  GW: 10.(100+n).0.1 (FortiGate)    │
    └────┬────────────────────────────────┘
         │ Trafic data

    ┌────▼────────────────────────────────┐
    │  FortiGate NAT                      │
    │  → rgz-gateway (QoS HTB/fq_codel)  │
    │  → rgz-ids (Suricata IDS/IPS)      │
    │  → Internet                         │
    └────┬────────────────────────────────┘
         │ Trafic isolé par VLAN
         │ Pas d'accès inter-revendeur

    Internet

Allocation VLAN

V1 RevendeurVLAN IDSubnetGatewayCPE IPs
Tech Connect10010.100.0.0/2410.100.0.110.100.0.2-254
WIFI Zone A10110.101.0.0/2410.101.0.110.101.0.2-254
WIFI Zone B10210.102.0.0/2410.102.0.110.102.0.2-254
...............
Dernier V149910.499.0.0/2410.499.0.110.499.0.2-254

Total: 400 VLANs (100-499) pour 400 V1 revendeurs

Kea DHCP configuration

dhcp
# /config/kea/kea-dhcp4.conf

subnet 10.100.0.0 netmask 255.255.255.0 {
  option routers 10.100.0.1;
  option domain-name-servers 172.23.0.15;  # rgz-dns
  pool {
    range 10.100.0.10 10.100.0.250;
    option routers 10.100.0.1;
  }

  # Identifier = NAS-ID via Option 82
  subnet-id 0;  # VLAN 100
}

subnet 10.101.0.0 netmask 255.255.255.0 {
  # ... même structure
  subnet-id 1;  # VLAN 101
}

# ... jusqu'à VLAN 499

Option 82 (Remote-ID) — Identifie le revendeur:

DHCP Request:
  Client MAC: AA:BB:CC:DD:EE:FF
  Option 82: Remote-ID = "access_tech_connect" (NAS-ID from API)

Kea mapping:
  NAS-ID "access_tech_connect" → VLAN 100 → subnet 10.100.0.0/24

Result:
  IP allocation: 10.100.0.x/24
  Gateway: 10.100.0.1 (rgz-gateway)

nftables firewall par VLAN

bash
# /scripts/gateway/rgz-main.nft

table inet rgz_filter {
  chain forward {
    type filter hook forward priority 0; policy drop;

    # Accepter VLAN internes (100-499)
    iifname "vlan100" oifname "vlan100" accept
    iifname "vlan100" oifname "eth0" accept
    iifname "eth0" oifname "vlan100" accept

    iifname "vlan101" oifname "vlan101" accept
    iifname "vlan101" oifname "eth0" accept
    iifname "eth0" oifname "vlan101" accept

    # ... répéter pour vlan100-499

    # Inter-VLAN: DROP (isolation)
    iifname "vlan*" oifname "vlan*" drop

    # Logging (optionnel)
    counter log prefix "rejected: "
  }

  chain nat {
    type nat hook postrouting priority 100;

    # SNAT: 10.x.0.0/24 → publique IP
    oifname "eth0" ip saddr 10.100.0.0/24 snat to <server_ip>
    oifname "eth0" ip saddr 10.101.0.0/24 snat to <server_ip>
    # ... etc
  }
}

VLAN tagging — Cisco + FortiGate (production)

En production, le VLAN tagging est géré par les équipements physiques :

Cisco Switch                     FortiGate
├─ Port 1 (access VLAN 100)  →  interface vlan100 (10.100.0.1/24)
├─ Port 2 (access VLAN 101)  →  interface vlan101 (10.101.0.1/24)
├─ Port 3 (access VLAN 102)  →  interface vlan102 (10.102.0.1/24)
├─ ...                        →  ...
└─ Trunk (all VLANs) ──────→  Trunk port (tagged 100-499)

Configuration Cisco (exemple) :

interface GigabitEthernet0/1
  switchport mode access
  switchport access vlan 100
  description "CPE Tech Connect"

interface GigabitEthernet0/24
  switchport mode trunk
  switchport trunk allowed vlan 100-499
  description "Trunk vers FortiGate"

Configuration FortiGate (exemple) :

config system interface
  edit "vlan100"
    set vdom "root"
    set ip 10.100.0.1 255.255.255.0
    set interface "port1"
    set vlanid 100
  next
end

VLAN tagging fallback (driver Linux vlan)

En dev/test sans FortiGate/Cisco, les VLANs peuvent être créés en logiciel :

bash
# Créer VLANs Linux (dev uniquement)
ip link add link eth0 name vlan100 type vlan id 100
ip addr add 10.100.0.1/24 dev vlan100
ip link set vlan100 up
yaml
# docker-compose.core.yml (dev)
services:
  rgz-gateway:
    cap_add:
      - NET_ADMIN
    networks:
      - host

Résolution DNS interne

Docker embedded resolver:

bash
# Depuis container rgz-api
cat /etc/resolv.conf
# nameserver 127.0.0.11:53

# Requête DNS
nslookup rgz-db
# Server: 127.0.0.11
# Address: 172.23.0.11

# Service discovery automatique
# rgz-db resolves to IP de rgz-db container

Healthcheck utilise DNS:

yaml
healthcheck:
  test: ["CMD", "curl", "-f", "http://rgz-api:8000/health"]
  # ✅ Résout rgz-api → 172.23.0.10

Isolation réseau (LL#40 warning)

⚠️ JAMAIS nft flush ruleset sur Docker:

bash
# ❌ DANGEREUX
nft flush ruleset
# Résultat: Docker chains disparaissent → perte connectivité réseau

# ✅ CORRECT: Supprimer sélectivement
nft delete table inet rgz_filter 2>/dev/null || true

Raison: Docker crée des tables nftables par défaut:

bash
# Tables Docker
nft list tables
# inet firewalld  (ou inet filter si iptables-nft)
# inet docker-ingress
# ... autres

# Flush supprime TOUTES les tables
# Y compris Docker infrastructure

Network policies

ServiceInboundOutboundNotes
rgz-api8000/tcp (rgz-net)rgz-db, rgz-redis, externalJSON responses
rgz-db5432/tcp (rgz-net)noneRead-only replication
rgz-redis6379/tcp (rgz-net)noneNo persistence
rgz-radius1812/udp, 1813/udp (ext)rgz-api, rgz-dbAuth + acct
rgz-gatewayall (host)all (host)Firewall
rgz-dns53/udp (ext+rgz-net)externalRecursive
rgz-portal80/tcp (rgz-net)rgz-apiProxy
rgz-web3000/tcp (rgz-net)rgz-apiAPI calls

Debugging réseau

bash
# Lister tous les réseaux
docker network ls

# Inspecter réseau
docker network inspect rgz_rgz-net

# Services sur réseau
docker network inspect rgz_rgz-net | grep -A 2 "\"Containers\""

# Tester connectivité container → container
docker exec rgz-api ping rgz-db
# PING rgz-db (172.23.0.11): 56 data bytes

# Vérifier DNS résolution
docker exec rgz-api nslookup rgz-redis
# Address: 172.23.0.12

# Vérifier nftables (sur host)
sudo nft list tables
sudo nft list table inet rgz_filter

# Vérifier VLAN tagging
ip link show
# vlan100@eth0: <...>
# vlan101@eth0: <...>

# Vérifier DHCP (sur rgz-gateway)
docker exec rgz-gateway cat /var/lib/dhcp/dhcpd.leases

Monitoring réseau

Prometheus metrics:

# Container networking
container_network_receive_bytes_total{container_id="...", interface="eth0"}
container_network_transmit_bytes_total

# Docker bridge
docker_network_up

Grafana dashboard:

  • Network I/O (bits/sec)
  • Dropped packets
  • Connection count (netstat)
  • VLAN isolation validation

Configuration yaml (référence)

yaml
# docker-compose.core.yml
version: '3.8'

networks:
  rgz-net:
    name: rgz_rgz-net
    driver: bridge
    driver_opts:
      com.docker.network.bridge.name: docker_rgz
      com.docker.driver.mtu: 1500
    ipam:
      driver: default
      config:
        - subnet: 172.23.0.0/16
          gateway: 172.23.0.1

  traefik-public:
    external: true
    name: traefik-public

services:
  rgz-api:
    networks:
      rgz-net:
        ipv4_address: 172.23.0.10
      traefik-public:
        ipv4_address: 10.0.9.10
    ports:
      - "127.0.0.1:8000:8000"  # localhost only
    labels:
      traefik.http.routers.api.rule: "Host(`api-rgz.duckdns.org`)"

  rgz-gateway:
    network_mode: host  # Special case
    cap_add:
      - NET_ADMIN

Étapes suivantes

  1. Ports Reference — Tous les ports
  2. Dataflow — Flux de données
  3. Dependencies — Graph 81 outils

PROJET MOSAÏQUE — 81 outils, 22 conteneurs, 500+ revendeurs WiFi Zone