| Les deux révisions précédentesRévision précédente | |
| commun:architecture_reseau [2026/07/26 08:11] – francois | commun:architecture_reseau [2026/09/20 12:21] (Version actuelle) – francois |
|---|
| ====== Architecture du réseau — beafrancois.fr ====== | ====== Architecture du réseau — beafrancois.fr ====== |
| |
| Dernière mise à jour : juillet 2026 | Dernière mise à jour : septembre 2026 |
| |
| Vue d'ensemble de l'infrastructure, du général au particulier : segments réseau, machines, services, flux, puis détails de configuration et particularités connues. | Cette page décrit l'infrastructure du général au particulier : les segments réseau, les machines, les services et les flux, puis le détail du filtrage et les particularités connues. Le détail du reverse proxy se trouve sur la page « Migration proxy HTTP : Raspi → NUC ». |
| |
| ===== 1. Vue d'ensemble ===== | ===== 1. Vue d'ensemble ===== |
| |
| Le LAN1 filaire porte l'infrastructure, deux PC tour et un Raspberry Pi 5. Les portables, eux, sont exclusivement en WiFi sur un segment séparé. | Le LAN1 filaire porte l'infrastructure, deux PC tour et un Raspberry Pi 5. Les portables sont exclusivement en WiFi, sur un segment séparé. |
| |
| <code> | <code> |
| | TLS · reverse | | | TLS · reverse | |
| | proxy · filtre | [Docker] | | proxy · filtre | [Docker] |
| | | sortie cloisonnée| |
| +--------+---------+ | +--------+---------+ |
| | HTTP interne | | backends autorisés uniquement |
| v | v |
| +------------------------------------------+ | +------------------------------------------+ |
| | Debian 13 · RAID 2 To | | | Debian 13 · RAID 2 To | |
| | UFW : accès restreint à 192.168.1.0/26 | | | UFW : accès restreint à 192.168.1.0/26 | |
| | | (proxy .25 exclu par un deny) | |
| | | DOCKER-USER : ports des conteneurs | |
| | - Auth : LLDAP [Docker] | | | - Auth : LLDAP [Docker] | |
| | - Surveillance : Uptime-Kuma, Dozzle, | | | - Surveillance : Uptime-Kuma, Dozzle, | |
| | Homepage [Docker] | | | Homepage, Diun [Docker] | |
| | - Services : Nextcloud, Wiki, Blog, | | | - Services : Nextcloud, Wiki, Blog, | |
| | Ampache, Gitea, MariaDB, Cockpit | | | Ampache, Gitea, MariaDB, Cockpit | |
| | [bare-metal] | | | [bare-metal] | |
| | - Partages : NFS / Samba [bare-metal] | | | - Partages : NFS 4.2 [bare-metal] | |
| +------------------------------------------+ | +------------------------------------------+ |
| ^ | ^ |
| | WiFi : sssd (LDAPS 6360) · web via proxy | | WiFi : sssd (LDAPS 6360) · web via proxy |
| | VPN : partages NFS / Samba | | VPN : partages NFS |
| +------------------------------------------+ | +------------------------------------------+ |
| | PORTABLES (Debian GNOME, CachyOS…) | | | PORTABLES (Debian GNOME, CachyOS…) | |
| | VPN WireGuard | 10.1.100.0/24 | Accès distant (partages NFS, services) | | | VPN WireGuard | 10.1.100.0/24 | Accès distant (partages NFS, services) | |
| |
| ⚠ **Ne pas confondre :** le LAN est bien un ''/24''. Le ''192.168.1.0/26'' que l'on retrouve dans plusieurs règles n'est **pas un masque réseau** — c'est une **plage d'autorisation UFW** sur le serveur, qui limite l'accès aux adresses ''.1'' à ''.62''. | Le LAN est bien un ''/24''. Le ''192.168.1.0/26'' que l'on retrouve dans plusieurs règles n'est pas un masque réseau : c'est une plage d'autorisation UFW sur le serveur, qui limite l'accès aux adresses ''.1'' à ''.62''. |
| |
| Le segment WiFi est déclaré comme zone ''invite'' côté OpenWrt : les règles de redirection et les autorisations UFW couvrent cette zone, d'où l'accès des portables au web et à LDAPS. | Le segment WiFi est déclaré comme zone ''invite'' côté OpenWrt. Les règles de redirection et les autorisations du serveur couvrent cette zone, ce qui donne aux portables l'accès au web et à LDAPS. |
| |
| ^ Hôte ^ IP ^ Nom court ^ Fonction ^ | ^ Hôte ^ IP ^ Nom court ^ Fonction ^ |
| [ Caddy .25 ] terminaison TLS (Let's Encrypt ou tls internal) | [ Caddy .25 ] terminaison TLS (Let's Encrypt ou tls internal) |
| filtre @denied par remote_ip | filtre @denied par remote_ip |
| | sortie limitée aux backends (DOCKER-USER) |
| v | v |
| [ Serveur .9 ] port applicatif interne (ex. 3001, 3002, 8888, 17170) | [ Serveur .9 ] port applicatif interne (ex. 3001, 3002, 8888, 17170) |
| | accepté depuis .25 uniquement (DOCKER-USER) |
| </code> | </code> |
| |
| <code> | <code> |
| Portable en WiFi --X-- pas d'accès direct aux partages | Portable en WiFi --X-- pas d'accès direct aux partages |
| Portable (WiFi ou 5G) --> tunnel WireGuard --> NFS / Samba --> /mnt/stockage (.9) | Portable (WiFi ou 5G) --> tunnel WireGuard --> NFS 4.2 --> /mnt/stockage (.9) |
| | PC tour (LAN1 /26) --> NFS 4.2 --> /mnt/stockage (.9) |
| </code> | </code> |
| |
| Les partages ne sont **jamais montés directement** depuis le segment WiFi : le tunnel WireGuard est le seul chemin, à la maison comme à l'extérieur. | Les partages ne sont jamais montés directement depuis le segment WiFi. Pour un portable, le tunnel WireGuard est le seul chemin, à la maison comme à l'extérieur. |
| |
| ===== 4. Les machines ===== | ===== 4. Les machines ===== |
| ==== 4.1 Routeur — OpenWrt (192.168.1.90) ==== | ==== 4.1 Routeur — OpenWrt (192.168.1.90) ==== |
| |
| Point d'entrée unique. Assure NAT, pare-feu de zones, DNS local et terminaison VPN. | Le routeur est le point d'entrée unique. Il assure le NAT, le pare-feu de zones, le DNS local et la terminaison du VPN. |
| |
| * **Admin web :** %%https://aiguilleur.beafrancois.fr:8443%% ou %%https://192.168.1.90:8443%% (uhttpd déplacé de 443 vers 8443) | L'interface d'administration LuCI répond sur %%https://aiguilleur.beafrancois.fr:8443%% ou %%https://192.168.1.90:8443%%, car uhttpd a été déplacé du 443 vers le 8443. Le port est obligatoire : ''aiguilleur.beafrancois.fr'' résout directement vers le routeur (.90) et non vers le proxy. Sans le '':8443'', la requête arrive sur le 443 du routeur, qui est justement redirigé vers Caddy (.25), lequel n'a pas de bloc pour ce nom. |
| * ⚠ **Le port est obligatoire.** ''aiguilleur.beafrancois.fr'' résout **directement vers le routeur** (.90), et non vers le proxy. Sans le '':8443'', la requête part sur le 443 du routeur, qui est justement redirigé vers Caddy (.25) — lequel n'a pas de bloc pour ce nom. | |
| * **Nom :** ''aiguilleur'' (anciennement ''router'', qui ne résout plus) | Le routeur s'appelle ''aiguilleur''. Son ancien nom, ''router'', ne résout plus. Le DNS est assuré par dnsmasq, avec une entrée par sous-domaine et sans wildcard. Le serveur WireGuard utilise l'interface ''wg1'' (et ''wg0''). |
| * **DNS :** dnsmasq, entrées ciblées par sous-domaine (pas de wildcard) | |
| * **VPN :** serveur WireGuard, interface ''wg1'' (et ''wg0'') | |
| |
| === Redirections de port (DNAT) === | === Redirections de port (DNAT) === |
| </code> | </code> |
| |
| dnsmasq **écoute sur l'interface du tunnel** (''wg1''), ce qui permet la résolution depuis le VPN. | dnsmasq écoute aussi sur l'interface du tunnel (''wg1''), ce qui permet la résolution des noms internes depuis le VPN. |
| |
| ==== 4.2 Proxy HTTP — Caddy (192.168.1.25) ==== | ==== 4.2 Proxy HTTP — Caddy (192.168.1.25) ==== |
| |
| **Machine physique dédiée** (NUC), hostname ''proxyhttp'' — distincte du serveur principal. Seule machine exposée au trafic entrant 443. | Le proxy est une machine physique dédiée, un NUC de hostname ''proxyhttp'', distincte du serveur principal. C'est la seule machine exposée au trafic entrant sur le 443. Caddy y tourne en conteneur, et le projet se trouve dans ''/opt/proxy''. |
| |
| * Terminaison TLS : Let's Encrypt pour les noms publics, ''tls internal'' (auto-signé) pour les noms internes | Caddy termine le TLS avec des certificats Let's Encrypt pour les noms publics, et avec ''tls internal'' pour les noms internes. Ces derniers n'ont donc pas de certificat public et n'apparaissent pas dans les journaux de transparence des certificats. Il relaie ensuite les requêtes vers les ports applicatifs du serveur. |
| * Reverse proxy vers les ports applicatifs du serveur | |
| * Restriction d'accès par IP source sur les services d'administration | Les vhosts d'administration sont restreints par IP source. Le filtre utilise ''remote_ip'', c'est-à-dire l'adresse réelle de la connexion TCP et non un en-tête : il n'est pas falsifiable depuis l'extérieur. Aucun de ces noms n'a d'enregistrement AAAA, Caddy n'est donc joignable de l'extérieur qu'en IPv4. |
| * Projet dans ''/opt/proxy'', Caddy lui-même en conteneur | |
| |
| <code> | <code> |
| </code> | </code> |
| |
| Rechargement : ''docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile'' | Le proxy est traité comme une machine non fiable, parce qu'il est le premier exposé en cas de faille dans Caddy. Trois mesures en découlent : |
| | |
| | * Le conteneur Caddy est cloisonné en sortie par des règles ''DOCKER-USER'' (bloc ''CADDY EGRESS'' de ''/etc/ufw/after.rules''). Sur les réseaux privés, il ne peut joindre que ''cahute'' sur ses six ports de backend, ''homeassistant'' sur le 8123 et le DNS du routeur. |
| | * Le sous-réseau Docker du projet est figé à ''172.18.0.0/16'' dans le compose, parce que ces règles s'appuient dessus. |
| | * SSH n'est ouvert sur le proxy que depuis LAN1, le WiFi et le VPN. |
| | |
| | Le rechargement de la configuration se fait par ''docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile''. |
| |
| ==== 4.3 Serveur principal — cahute (192.168.1.9) ==== | ==== 4.3 Serveur principal — cahute (192.168.1.9) ==== |
| * **OS :** Debian Trixie (13), kernel 6.x | * **OS :** Debian Trixie (13), kernel 6.x |
| * **Matériel :** AMD x86, 8 Go RAM, 2 To en soft-RAID mdadm | * **Matériel :** AMD x86, 8 Go RAM, 2 To en soft-RAID mdadm |
| | * **Interface :** ''enp3s0'', sans adresse IPv6 globale |
| * **Conteneurisation :** Docker CE (dépôt officiel), Compose v2 | * **Conteneurisation :** Docker CE (dépôt officiel), Compose v2 |
| * **Stockage :** ''/mnt/stockage'' (multimedia, famille, partage, homes, system, docker) | * **Stockage :** ''/mnt/stockage'' (multimedia, famille, partage, homes, system, docker) |
| * **Jamais exposé directement** à Internet | |
| |
| === État de la migration vers Docker === | === État de la migration vers Docker === |
| |
| La conteneurisation est **partielle** sur le serveur. Situation actuelle : | La conteneurisation est partielle. LLDAP et la stack de surveillance (Dozzle, Uptime-Kuma, Homepage, Diun) tournent sous Docker, ainsi qu'''imaginary'', le service d'aperçus de Nextcloud, qui n'écoute que sur ''127.0.0.1:9000''. Apache (vhosts web sur le 443), Nextcloud, DokuWiki, le blog, Ampache, Gitea, MariaDB, Cockpit et NFS sont encore en bare-metal. |
| |
| * **Sous Docker :** LLDAP, stack surveillance (Dozzle, Uptime-Kuma, Homepage) | Caddy ne tourne pas sur ce serveur : il est en conteneur sur la machine proxy dédiée (.25), voir §4.2. |
| * **Encore en bare-metal :** Apache (vhosts web, :443), Nextcloud, DokuWiki, Blog, Ampache, Gitea, MariaDB, Cockpit, NFS | |
| | |
| Caddy, lui, ne tourne **pas** sur ce serveur : il est en conteneur sur la machine proxy dédiée (.25), voir §4.2. | |
| |
| Les dossiers ''compose/'' et ''data/'' des services restants sont déjà créés, mais les stacks ne sont pas déployées. | Les dossiers ''compose/'' et ''data/'' des services restants sont déjà créés, mais les stacks ne sont pas déployées. |
| === Portables === | === Portables === |
| |
| * Portables Linux (Debian/GNOME, CachyOS/Arch) | Les portables sont des Linux (Debian/GNOME, CachyOS/Arch) connectés exclusivement en WiFi (192.168.115.0/24). |
| * **Connexion en WiFi exclusivement** (192.168.115.0/24) | |
| * Authentification système via **sssd** sur LLDAP, ''cache_credentials = True'', cache offline **sans expiration** (''offline_credentials_expiration = 0'') | L'authentification système passe par sssd sur LLDAP, avec ''cache_credentials = True'' et un cache offline sans expiration (''offline_credentials_expiration = 0''). Le home est local sur chaque poste, sans NFS : c'est la condition du fonctionnement hors ligne. |
| * **Home local** sur chaque poste (pas de NFS) — condition du fonctionnement hors ligne | |
| * **WireGuard obligatoire pour les partages** : les montages NFS/Samba ne passent que par le tunnel (10.1.100.0/24), y compris depuis le WiFi de la maison. Le client pousse ''DNS = 192.168.1.90'' | WireGuard est obligatoire pour les partages. Les montages NFS ne passent que par le tunnel (10.1.100.0/24), y compris depuis le WiFi de la maison, parce que les exports n'autorisent pas le segment WiFi. Le client WireGuard pousse ''DNS = 192.168.1.90''. |
| |
| === PC tour (×2) === | === PC tour (×2) === |
| |
| * Postes fixes raccordés en **filaire sur le LAN1** | Les deux postes fixes sont raccordés en filaire sur le LAN1. Leurs adresses sont dans la plage ''.1–.62'', donc couvertes par les règles UFW du serveur : ils accèdent directement aux services et aux partages, sans VPN. Ils montent les partages en NFS 4.2 sur TCP, depuis un port source privilégié. |
| * Adresses dans la plage ''.1–.62'', donc couvertes par les règles UFW du serveur : accès direct aux services et aux partages, sans VPN | |
| |
| === Raspberry Pi 5 — ''homeassistant'' (192.168.1.67) === | === Raspberry Pi 5 — ''homeassistant'' (192.168.1.67) === |
| |
| * Domotique, raccordé en filaire sur le LAN1 | Le Raspberry Pi 5 porte la domotique et est raccordé en filaire sur le LAN1. |
| * ⚠ Son adresse (''.67'') est **hors de la plage ''.1–.62''** couverte par les règles UFW en ''/26''. Le serveur lui refuse donc SSH (22), HTTPS (443), NFS (111/2048/2049/4045), Gitea (3000) et Cockpit (9090). | |
| * Ce qui lui reste accessible : les services **via le proxy Caddy** (.25, machine distincte, dont le filtre autorise tout le ''192.168.1.0/24''), et **LDAPS 6360** (règle en ''/24''). | Son adresse (''.67'') est hors de la plage ''.1–.62'' couverte par les règles UFW en ''/26''. Le serveur lui refuse donc SSH (22), HTTPS (443), NFS (2049), Gitea (3000) et Cockpit (9090). Depuis cette machine, le serveur répond au ping mais refuse la connexion sur ces ports. |
| * Comportement observé depuis cette machine : le serveur répond au ping, mais refuse la connexion sur ces ports. | |
| | Il lui reste les services publiés par le proxy Caddy (.25), dont le filtre autorise tout le ''192.168.1.0/24'', et LDAPS sur le 6360, autorisé en ''/24''. |
| | |
| | Les règles de cloisonnement du proxy désignent ''homeassistant'' par son adresse IP : le relais de Caddy vers le 8123 dépend du maintien de l'adresse ''.67''. |
| |
| ===== 5. Services et points d'accès ===== | ===== 5. Services et points d'accès ===== |
| | NFS | — | filaire .1–.62 + VPN | bare-metal | | | NFS | — | filaire .1–.62 + VPN | bare-metal | |
| |
| Le port **3000 est occupé par Gitea**, d'où Homepage sur 3002. | Le port 3000 est occupé par Gitea, d'où Homepage sur le 3002. |
| Le port **3890** (LDAP en clair) n'est pas ouvert au pare-feu : usage interne au serveur. | |
| | Le port 3890 (LDAP en clair) est publié par le conteneur LLDAP mais fermé au réseau par ''DOCKER-USER''. Seuls les processus locaux de ''cahute'' peuvent l'atteindre. |
| | |
| | Dozzle exige une authentification (l'API répond 401 sans session). Homepage n'a pas d'authentification propre. |
| |
| ===== 6. Authentification et droits ===== | ===== 6. Authentification et droits ===== |
| |
| * **Annuaire :** LLDAP (conteneur Docker), remplace Kanidm (abandonné : offline cassé) | L'annuaire est LLDAP, en conteneur Docker. Il remplace Kanidm, abandonné parce que son mode offline ne fonctionnait pas. |
| * **Clients :** sssd (PAM + NSS) sur les portables **et** sur le serveur (mode NSS-only, pour que NFS applique les identités LLDAP) | |
| * **Compte bind** en lecture seule déclaré dans ''sssd.conf'' (permissions 600) | Les clients utilisent sssd (PAM et NSS) sur les portables et sur les PC tour. Le serveur l'utilise aussi, en mode NSS seul, pour que NFS applique les identités LLDAP. Un compte de bind en lecture seule est déclaré dans ''sssd.conf'' (permissions 600). |
| * **UID** forcés 1000–1003, **groupes** : ''partage'' (GID 301), ''famille'' (GID 302), ''multimedia'' | |
| * Pas de Kerberos : la cohérence des UID/GID suffit pour NFS | Les UID sont forcés de 1000 à 1003. Les groupes sont ''partage'' (GID 301), ''famille'' (GID 302) et ''multimedia''. Il n'y a pas de Kerberos : la cohérence des UID et des GID entre les machines suffit à NFS. |
| |
| ===== 7. Sécurité ===== | ===== 7. Sécurité ===== |
| |
| * Le serveur n'est joignable depuis Internet que par l'intermédiaire du proxy — **sauf le port 8329** (Gitea SSH), ouvert à tous | Le filtrage de ''cahute'' repose sur deux mécanismes distincts. UFW filtre les services en bare-metal. La chaîne ''DOCKER-USER'' filtre les ports publiés par les conteneurs, qu'UFW ne voit pas. |
| * MariaDB (3306) et le LDAP en clair (3890) ne sont pas ouverts | |
| * Le WiFi est autorisé sur LDAPS (6360) et Cockpit (9090), **pas sur NFS** : les partages ne sont joignables que depuis le filaire ''.1–.62'' ou par le VPN | MariaDB (3306) et le LDAP en clair (3890) ne sont pas ouverts au réseau. Le WiFi est autorisé sur LDAPS (6360) et sur Cockpit (9090), mais pas sur NFS : les partages ne sont joignables que depuis le filaire ''.1–.62'' ou par le VPN. |
| |
| ==== 7.1 Règles UFW du serveur (état réel) ==== | ==== 7.1 Règles UFW du serveur (état réel) ==== |
| |
| ^ Port(s) ^ Service ^ Sources autorisées ^ | ^ Port(s) ^ Service ^ Action ^ Sources ^ |
| | 22/tcp | SSH | 192.168.1.0/26 | | | 22, 111, 2049, 9090/tcp | SSH, rpcbind, NFS, Cockpit | **DENY**, en tête | 192.168.1.25 (proxy) | |
| | 443/tcp | Apache | 192.168.1.0/26 (dont le proxy .25) | | | 22/tcp | SSH | ALLOW | 192.168.1.0/26 | |
| | 111, 2048, 2049, 4045 | NFS (tcp + udp) | 192.168.1.0/26 | | | 443/tcp | Apache | ALLOW | 192.168.1.0/26 (dont le proxy .25) | |
| | 2049/tcp | NFSv4 | 10.1.100.0/24 (VPN) | | | 2049/tcp | NFSv4 | ALLOW | 192.168.1.0/26 + 10.1.100.0/24 (VPN) | |
| | 3000/tcp | Gitea (web) | 192.168.1.0/26 | | | 2048, 4045 (tcp + udp) | reliquat NFSv3 | ALLOW | 192.168.1.0/26 | |
| | 8329/tcp (v4 + v6) | Gitea (SSH) | **Anywhere** | | | 3000/tcp | Gitea (web) | ALLOW | 192.168.1.0/26 | |
| | 9090/tcp | Cockpit | 192.168.1.0/26 + 192.168.115.0/24 | | | 8329/tcp (v4 + v6) | Gitea (SSH) | ALLOW | **Anywhere** | |
| | 6360/tcp | LLDAP (LDAPS) | 192.168.1.0/**24** + WiFi + VPN | | | 9090/tcp | Cockpit | ALLOW | 192.168.1.0/26 + 192.168.115.0/24 | |
| | 17170/tcp | LLDAP (UI) | 192.168.1.25 (proxy) | | |
| | 3001, 3002, 8888 | Surveillance | 192.168.1.25 (proxy) | | |
| |
| Lecture de ces règles : | Lecture de ces règles : |
| |
| * **NFS n'est pas ouvert au WiFi** — ce qui confirme que les portables ne peuvent monter les partages que via WireGuard. Côté VPN, seul ''2049/tcp'' est autorisé : cela suffit en **NFSv4** (pas de portmapper), mais interdit NFSv3. | * **Le proxy est exclu de la zone de confiance.** Son adresse (.25) est dans la plage ''/26'', ce qui lui ouvrirait NFS, SSH et Cockpit. La règle ''deny'' placée en première position les lui ferme. UFW applique la première règle qui correspond, donc ce ''deny'' l'emporte sur les ''allow'' en ''/26''. Le filtrage est fait sur ''cahute'' et non sur le proxy, parce que le pare-feu d'un proxy compromis ne protège plus rien. Le proxy garde l'accès au 443 et au 3000, dont Caddy a besoin. |
| * **Samba n'est pas ouvert** (ni 445, ni 139). Les partages se font donc en NFS uniquement, malgré la présence du service. | * **NFS n'est ouvert qu'en 2049/tcp.** Les clients montent en NFS 4.2, qui n'utilise ni rpcbind (111) ni l'UDP. Ces ouvertures ont été supprimées. Les règles sur le 2048 et le 4045 datent de NFSv3 et ne correspondent à aucun flux des clients actuels. |
| * **6360 est en ''/24''**, contrairement aux autres règles en ''/26'' : l'authentification reste donc possible depuis toute machine du LAN filaire, y compris au-delà de ''.62''. | * **NFS n'est pas ouvert au WiFi**, ce qui confirme que les portables ne montent les partages que par WireGuard. |
| * **8329 est ouvert à Anywhere, IPv6 compris** : c'est le SSH de Gitea, seule exposition directe du serveur à Internet (accès Git distant). | * **Samba n'est pas ouvert** (ni 445, ni 139). Les partages se font en NFS uniquement, malgré la présence du service. |
| * Le **443** correspond à **Apache**, en bare-metal sur le serveur ; sa racine affiche encore la page par défaut. Le proxy (.25) étant dans la plage ''/26'', il peut le joindre — c'est par là que passent les services web non conteneurisés. | * **Le 8329 accepte toute source**, en IPv4 et en IPv6. C'est le SSH intégré de Gitea : il n'accepte que l'authentification par clé publique, pour des clés enregistrées dans un compte Gitea, et ne donne pas de shell. L'inscription est fermée (''DISABLE_REGISTRATION = true'') et la consultation exige une connexion (''REQUIRE_SIGNIN_VIEW = true''). ''cahute'' n'a pas d'adresse IPv6 globale, la règle « v6 » n'expose donc rien. |
| * Les règles portant sur **17170, 6360, 3001, 3002 et 8888** visent des conteneurs Docker : elles sont **inopérantes** en l'état (voir ci-dessous). | * **Le 443 correspond à Apache**, en bare-metal sur le serveur ; sa racine affiche encore la page par défaut. C'est par lui que passent les services web non conteneurisés. |
| | * **Aucune règle UFW ne porte sur les ports des conteneurs** (17170, 6360, 3890, 3001, 3002, 8888). Elles ont été retirées parce qu'elles ne filtraient rien : voir §7.2. |
| | |
| | ==== 7.2 Filtrage des ports Docker (DOCKER-USER) ==== |
| | |
| | Docker publie ses ports par un DNAT, et le trafic correspondant traverse la chaîne FORWARD, en amont des chaînes d'UFW. Les règles ''ufw allow from 192.168.1.25 to any port …'' ne s'appliquaient donc pas aux conteneurs : avant la mise en place de ce filtrage, Dozzle (:8888) était joignable depuis le VPN, alors que Cockpit (:9090, bare-metal) était bien filtré. Le binding sur ''192.168.1.9'' plutôt que sur ''0.0.0.0'' évite l'exposition en IPv6, mais ne change rien à ce contournement. |
| | |
| | Le filtrage se fait dans la chaîne ''DOCKER-USER'', que Docker évalue avant ses propres règles. Le bloc est placé en fin de ''/etc/ufw/after.rules'' (sauvegarde de l'original : ''after.rules.bak''), ce qui le rend persistant et rechargeable par ''ufw reload'' : |
| | |
| | <code> |
| | # BEGIN DOCKER INGRESS |
| | *filter |
| | :DOCKER-USER - [0:0] |
| | -A DOCKER-USER -m conntrack --ctstate ESTABLISHED,RELATED -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 192.168.1.25 -p tcp -m conntrack --ctorigdstport 3001:3002 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 192.168.1.25 -p tcp -m conntrack --ctorigdstport 8888 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 192.168.1.25 -p tcp -m conntrack --ctorigdstport 17170 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 192.168.1.0/24 -p tcp -m conntrack --ctorigdstport 6360 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 192.168.115.0/24 -p tcp -m conntrack --ctorigdstport 6360 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -s 10.1.100.0/24 -p tcp -m conntrack --ctorigdstport 6360 -j RETURN |
| | -A DOCKER-USER -i enp3s0 -m conntrack --ctstate DNAT -j DROP |
| | COMMIT |
| | # END DOCKER INGRESS |
| | </code> |
| | |
| | ^ Port publié ^ Conteneur ^ Sources autorisées ^ |
| | | 3001 | Uptime-Kuma | 192.168.1.25 (proxy) | |
| | | 3002 | Homepage | 192.168.1.25 (proxy) | |
| | | 8888 | Dozzle | 192.168.1.25 (proxy) | |
| | | 17170 | LLDAP (UI) | 192.168.1.25 (proxy) | |
| | | 6360 | LLDAP (LDAPS) | 192.168.1.0/24 + WiFi + VPN | |
| | | 3890 | LLDAP (LDAP en clair) | aucune | |
| | |
| | Quatre points expliquent la construction de ce bloc : |
| | |
| | * Dans ''DOCKER-USER'', le paquet a déjà subi le DNAT : son port de destination est celui du conteneur (3000 pour Homepage, 8080 pour Dozzle). Le critère ''--ctorigdstport'' filtre sur le port publié d'origine. |
| | * Le ''DROP'' final ne vise que les connexions DNATées qui entrent par ''enp3s0'', c'est-à-dire les ports publiés. Le trafic sortant des conteneurs (sondes d'Uptime-Kuma, Diun) n'est pas touché, et ses réponses passent par la première règle. |
| | * Les processus locaux de ''cahute'' ne traversent pas la chaîne FORWARD. sssd et les applications locales atteignent donc LLDAP, y compris sur le 3890, sans règle particulière. |
| | * Un nouveau conteneur qui publie un port est bloqué par défaut tant qu'aucune règle n'est ajoutée à ce bloc. |
| | |
| | Le 6360 est autorisé en ''/24'' et non en ''/26'' : l'authentification reste possible depuis toute machine du LAN filaire, y compris au-delà de ''.62''. |
| | |
| | <code bash> |
| | # Recharger après une modification du bloc |
| | sudo ufw reload |
| | |
| | # Afficher la chaîne telle qu'elle est chargée |
| | sudo iptables -S DOCKER-USER |
| | |
| | # Test depuis un poste du LAN : attendu bloqué, bloqué, ouvert |
| | for p in 3002 3890 6360; do echo -n "$p: "; timeout 3 bash -c "</dev/tcp/192.168.1.9/$p" 2>/dev/null && echo ouvert || echo bloqué; done |
| | </code> |
| |
| ==== ⚠ Limitation connue : Docker contourne UFW ==== | ==== 7.3 Exports NFS ==== |
| |
| Docker insère ses règles iptables (chaînes FORWARD / DOCKER) **en amont d'UFW**. Les règles ''ufw allow from 192.168.1.25 to any port …'' ne s'appliquent donc **pas** aux ports publiés par des conteneurs (LLDAP, Dozzle, Uptime-Kuma, Homepage). Constat vérifié : Dozzle (:8888, conteneur) reste joignable depuis le VPN, alors que Cockpit (:9090, bare-metal) est bien filtré. | ''cahute'' exporte ''/export/famille'', ''/export/multimedia'', ''/export/partage'', ''/export/home/beatrice'' et ''/export/home/francois''. Chaque export est ouvert à ''192.168.1.0/26'' et à ''10.1.100.0/24'', avec les options ''rw,secure,root_squash,no_subtree_check'' (''root_squash'' et ''secure'' sont les valeurs par défaut, ''exportfs -v'' les affiche). |
| |
| Ce qui protège malgré tout : le binding sur ''192.168.1.9'' (et non 0.0.0.0), et le filtre par IP source de Caddy sur les sous-domaines. | L'authentification est en ''sec=sys'' : le serveur fait confiance à l'UID annoncé par le client. L'option ''secure'' impose un port source inférieur à 1024, ce qui réserve le dialogue NFS au root du client et empêche un processus utilisateur de forger un UID. L'option ''root_squash'' ramène le root distant à ''nobody''. Un root sur un poste de la plage ''/26'' ou du VPN peut en revanche se présenter sous n'importe quel UID ordinaire. Seul Kerberos fermerait cette possibilité, et il n'est pas déployé. |
| |
| ''ufw-docker'' (règles dans la chaîne ''DOCKER-USER'') n'est pas installé. | La version précédente du fichier, avec l'option ''insecure'', est conservée dans ''/etc/exports.bak''. |
| |
| ===== 8. Particularités et incidents connus ===== | ===== 8. Particularités et incidents connus ===== |
| |
| * **Le ''/26'' n'est pas un sous-réseau** — c'est une plage d'autorisation dans les règles UFW du serveur (''ufw allow from 192.168.1.0/26 …''), qui ne couvre que ''.1–.62''. Toute machine du LAN adressée au-delà de ''.62'' est silencieusement refusée par le serveur alors que le réseau, lui, fonctionne. **Cas avéré :** ''homeassistant'' en ''.67''. | * **Le ''/26'' n'est pas un sous-réseau.** C'est une plage d'autorisation dans les règles UFW du serveur (''ufw allow from 192.168.1.0/26 …''), qui ne couvre que ''.1–.62''. Toute machine du LAN adressée au-delà de ''.62'' est silencieusement refusée par le serveur alors que le réseau, lui, fonctionne. C'est le cas de ''homeassistant'' en ''.67''. |
| * **Redirection 443 par zone** — une règle limitée à ''src=wan'' casse l'accès web interne. Les zones ''lan'' et ''invite'' ont donc chacune leur règle. | * **Redirection 443 par zone.** Une règle limitée à ''src=wan'' casse l'accès web interne. Les zones ''lan'' et ''invite'' ont donc chacune leur règle. |
| * **Admin OpenWrt sur 443** — entrait en conflit avec la redirection ; déplacé sur **8443**. | * **Admin OpenWrt sur 443.** Elle entrait en conflit avec la redirection et a été déplacée sur le 8443. |
| * **Pas de wildcard DNS** ''/beafrancois.fr/…'' — un wildcard casserait Gitea SSH (un nom ne peut pas résoudre vers deux IP selon le protocole). Chaque service est donc déclaré individuellement. | * **Pas de wildcard DNS** ''/beafrancois.fr/…''. Un wildcard casserait le SSH de Gitea, car un nom ne peut pas résoudre vers deux IP selon le protocole. Chaque service est donc déclaré individuellement. |
| * **dnsmasq et le VPN** — sans écoute sur ''wg1'' (et ''localservice'' adapté), la résolution depuis le tunnel ne fonctionnait pas. | * **dnsmasq et le VPN.** Sans écoute sur ''wg1'' (et un ''localservice'' adapté), la résolution depuis le tunnel ne fonctionnait pas. |
| * **HOMEPAGE_ALLOWED_HOSTS** — Homepage rejette tout ''Host'' non listé (« Host validation failed »). Valeurs séparées par des virgules, sans espace. | * **HOMEPAGE_ALLOWED_HOSTS.** Homepage rejette tout ''Host'' non listé (« Host validation failed »). Les valeurs sont séparées par des virgules, sans espace. |
| * **Bits spéciaux et NFSv4** — setuid/setgid sur les dossiers de partage bloquaient l'accès malgré l'appartenance au bon groupe. Corrigé par ''chmod -R a-s,a-t partage''. | * **Bits spéciaux et NFSv4.** Les bits setuid et setgid sur les dossiers de partage bloquaient l'accès malgré l'appartenance au bon groupe. Le problème a été corrigé par ''chmod -R a-s,a-t partage''. |
| * **Renommage ''router'' → ''aiguilleur''** — l'ancien nom ne résout plus du tout. Tout ce qui le référence est cassé : la **sonde Ping « Routeur » d'Uptime-Kuma** (hôte ''router''), et l'éventuel bloc ''router.beafrancois.fr'' du Caddyfile, désormais orphelin. À l'époque, ce nom résolvait vers le proxy (.25) qui reverse-proxifiait vers le routeur (.90:8443) — sans boucle, ce sont deux machines distinctes. Le nouveau nom pointe **directement** vers .90, sans passer par Caddy. | * **Renommage de ''router'' en ''aiguilleur''.** L'ancien nom ne résout plus du tout. La sonde Ping « Routeur » d'Uptime-Kuma, configurée sur l'hôte ''router'', est cassée depuis. À l'époque, ce nom résolvait vers le proxy (.25), qui relayait vers le routeur (.90:8443). Le nouveau nom pointe directement vers .90, sans passer par Caddy, et le Caddyfile ne contient plus de bloc vers le routeur. |
| | * **Règles de cloisonnement et nouveaux services.** Un vhost ajouté dans Caddy vers une cible absente du bloc ''CADDY EGRESS'' du proxy, ou un conteneur ajouté sur ''cahute'' sans règle dans le bloc ''DOCKER INGRESS'', échoue par timeout sans message explicite. |