Table des matières
Architecture du réseau — beafrancois.fr
Dernière mise à jour : septembre 2026
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
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é.
Internet
|
v
+----------------------------------------------+
| ROUTEUR OpenWrt 192.168.1.90 |
| NAT · pare-feu · DNS local (dnsmasq) |
| serveur WireGuard · admin LuCI :8443 |
+----------------------------------------------+
| | |
LAN1 filaire WiFi VPN WireGuard
192.168.1.0/24 192.168.115.0/24 10.1.100.0/24
(infra + fixes) (portables) (accès distant)
| | |
| +----------+----------+
| |
+-------+--------+-----------+ |
| | | | |
| [2 PC tour] [RPi5 « homeassistant »]
| |
v |
+------------------+ |
| PROXY Caddy .25 | <--------------+ (web des 3 segments)
| TLS · reverse |
| proxy · filtre | [Docker]
| sortie cloisonnée|
+--------+---------+
| backends autorisés uniquement
v
+------------------------------------------+
| SERVEUR « cahute » 192.168.1.9 |
| Debian 13 · RAID 2 To |
| UFW : accès restreint à 192.168.1.0/26 |
| (proxy .25 exclu par un deny) |
| DOCKER-USER : ports des conteneurs |
| - Auth : LLDAP [Docker] |
| - Surveillance : Uptime-Kuma, Dozzle, |
| Homepage, Diun [Docker] |
| - Services : Nextcloud, Wiki, Blog, |
| Ampache, Gitea, MariaDB, Cockpit |
| [bare-metal] |
| - Partages : NFS 4.2 [bare-metal] |
+------------------------------------------+
^
| WiFi : sssd (LDAPS 6360) · web via proxy
| VPN : partages NFS
+------------------------------------------+
| PORTABLES (Debian GNOME, CachyOS…) |
| WiFi uniquement · home local |
| cache offline sssd · WireGuard pour les |
| partages |
+------------------------------------------+
2. Plan d'adressage
| Segment | Plage | Rôle |
|---|---|---|
| LAN1 filaire | 192.168.1.0/24 | Infrastructure + postes fixes + domotique |
| WiFi | 192.168.115.0/24 | Tous les portables |
| VPN WireGuard | 10.1.100.0/24 | Accès distant (partages NFS, services) |
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 du serveur couvrent cette zone, ce qui donne aux portables l'accès au web et à LDAPS.
| Hôte | IP | Nom court | Fonction |
|---|---|---|---|
| Routeur | 192.168.1.90 | aiguilleur | OpenWrt : NAT, DNS, pare-feu, WG |
| Proxy | 192.168.1.25 | proxyhttp | Caddy : TLS + reverse proxy |
| Serveur | 192.168.1.9 | cahute | Docker, données, partages |
| PC tour ×2 | 192.168.1.x | — | Postes fixes filaires |
| Raspberry Pi 5 | 192.168.1.67 | homeassistant | Domotique — hors plage UFW (voir §8) |
3. Les flux principaux
3.1 Flux web (HTTPS)
Client (LAN / wifi invité / VPN / Internet)
| résolution DNS : sous-domaine -> 192.168.1.25
v
[ Routeur .90 ] DNAT 443 -> .25 (règle par zone : wan, lan, invite)
v
[ Caddy .25 ] terminaison TLS (Let's Encrypt ou tls internal)
filtre @denied par remote_ip
sortie limitée aux backends (DOCKER-USER)
v
[ Serveur .9 ] port applicatif interne (ex. 3001, 3002, 8888, 17170)
accepté depuis .25 uniquement (DOCKER-USER)
3.2 Flux d'authentification
Portable (online) --> sssd --> ldaps://auth.beafrancois.fr:6360 --> LLDAP (.9) Portable (offline) --> sssd --> cache local chiffré (sans expiration) Navigateur --> ldap.beafrancois.fr / auth.beafrancois.fr --> UI LLDAP Serveur (NSS-only) --> sssd --> LLDAP (résolution UID/GID pour NFS)
3.3 Flux fichiers
Portable en WiFi --X-- pas d'accès direct aux partages Portable (WiFi ou 5G) --> tunnel WireGuard --> NFS 4.2 --> /mnt/stockage (.9) PC tour (LAN1 /26) --> NFS 4.2 --> /mnt/stockage (.9)
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.1 Routeur — OpenWrt (192.168.1.90)
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.
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 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).
Redirections de port (DNAT)
| Port | Destination | Zones sources | Usage |
|---|---|---|---|
| 443 | 192.168.1.25:443 | wan + lan + invite | Web via Caddy |
| 6360 | 192.168.1.9:6360 | lan + invite | LDAPS (sssd) |
| 9090 | 192.168.1.9:9090 | lan | Cockpit |
| 8329 | 192.168.1.9 | lan | Gitea SSH |
DNS local (dnsmasq)
uci add_list dhcp.@dnsmasq[0].address='/nuage.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/wiki.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/blog.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/musique.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/surveillance.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/status.beafrancois.fr/192.168.1.25' uci add_list dhcp.@dnsmasq[0].address='/logs.beafrancois.fr/192.168.1.25' uci commit dhcp /etc/init.d/dnsmasq restart
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)
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.
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.
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.
surveillance.beafrancois.fr {
@denied not remote_ip 192.168.1.0/24 192.168.115.0/24 10.1.100.0/24
respond @denied 403
tls internal
reverse_proxy 192.168.1.9:3002
}
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(blocCADDY EGRESSde/etc/ufw/after.rules). Sur les réseaux privés, il ne peut joindre quecahutesur ses six ports de backend,homeassistantsur le 8123 et le DNS du routeur. - Le sous-réseau Docker du projet est figé à
172.18.0.0/16dans 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)
- OS : Debian Trixie (13), kernel 6.x
- 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
- Stockage :
/mnt/stockage(multimedia, famille, partage, homes, system, docker)
État de la migration vers Docker
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.
Caddy 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.
Arborescence Docker
/mnt/stockage/docker/ ├── compose/ ← un dossier par stack (lldap, surveillance, + à venir) └── data/ ← données persistantes (bind mounts vers le RAID)
4.4 Postes clients
Portables
Les portables sont des Linux (Debian/GNOME, CachyOS/Arch) connectés exclusivement en WiFi (192.168.115.0/24).
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.
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)
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é.
Raspberry Pi 5 — ''homeassistant'' (192.168.1.67)
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 (2049), Gitea (3000) et Cockpit (9090). 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
| Service | Sous-domaine | Cible | Exécution |
|---|---|---|---|
| Nextcloud | nuage.beafrancois.fr | .9:443 (Apache) | bare-metal |
| DokuWiki | wiki.beafrancois.fr | .9:443 (Apache) | bare-metal |
| Blog | blog.beafrancois.fr/blog | .9:443 (Apache) | bare-metal |
| Ampache | musique.beafrancois.fr/musique/public | .9:443 (Apache) | bare-metal |
| Gitea | gitea.beafrancois.fr (+ SSH 8329) | .9:3000 | bare-metal |
| MariaDB | — | .9:3306 | bare-metal |
| LLDAP (UI) | auth… / ldap.beafrancois.fr | .9:17170 | Docker |
| LLDAP (LDAPS) | auth.beafrancois.fr:6360 | .9:6360 | Docker |
| Homepage | surveillance.beafrancois.fr | .9:3002 | Docker |
| Uptime-Kuma | status.beafrancois.fr | .9:3001 | Docker |
| Dozzle | logs.beafrancois.fr | .9:8888 | Docker |
| Cockpit | — | .9:9090 | bare-metal |
| NFS | — | filaire .1–.62 + VPN | bare-metal |
Le port 3000 est occupé par Gitea, d'où Homepage sur le 3002.
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
L'annuaire est LLDAP, en conteneur Docker. Il remplace Kanidm, abandonné parce que son mode offline ne fonctionnait pas.
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).
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é
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 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)
| Port(s) | Service | Action | Sources |
|---|---|---|---|
| 22, 111, 2049, 9090/tcp | SSH, rpcbind, NFS, Cockpit | DENY, en tête | 192.168.1.25 (proxy) |
| 22/tcp | SSH | ALLOW | 192.168.1.0/26 |
| 443/tcp | Apache | ALLOW | 192.168.1.0/26 (dont le proxy .25) |
| 2049/tcp | NFSv4 | ALLOW | 192.168.1.0/26 + 10.1.100.0/24 (VPN) |
| 2048, 4045 (tcp + udp) | reliquat NFSv3 | ALLOW | 192.168.1.0/26 |
| 3000/tcp | Gitea (web) | ALLOW | 192.168.1.0/26 |
| 8329/tcp (v4 + v6) | Gitea (SSH) | ALLOW | Anywhere |
| 9090/tcp | Cockpit | ALLOW | 192.168.1.0/26 + 192.168.115.0/24 |
Lecture de ces règles :
- 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ègledenyplacée en première position les lui ferme. UFW applique la première règle qui correspond, donc cedenyl'emporte sur lesallowen/26. Le filtrage est fait surcahuteet 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. - 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.
- NFS n'est pas ouvert au WiFi, ce qui confirme que les portables ne montent les partages que par WireGuard.
- Samba n'est pas ouvert (ni 445, ni 139). Les partages se font en NFS uniquement, malgré la présence du service.
- 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).cahuten'a pas d'adresse IPv6 globale, la règle « v6 » n'expose donc rien. - 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 :
# 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
| 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–ctorigdstportfiltre sur le port publié d'origine. - Le
DROPfinal ne vise que les connexions DNATées qui entrent parenp3s0, 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
cahutene 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.
# 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
7.3 Exports NFS
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).
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é.
La version précédente du fichier, avec l'option insecure, est conservée dans /etc/exports.bak.
8. Particularités et incidents connus
- Le
/26n'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.62est silencieusement refusée par le serveur alors que le réseau, lui, fonctionne. C'est le cas dehomeassistanten.67. - Redirection 443 par zone. Une règle limitée à
src=wancasse l'accès web interne. Les zoneslanetinviteont donc chacune leur règle. - 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 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 unlocalserviceadapté), la résolution depuis le tunnel ne fonctionnait pas. - HOMEPAGE_ALLOWED_HOSTS. Homepage rejette tout
Hostnon listé (« Host validation failed »). Les valeurs sont séparées par des virgules, sans espace. - 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 de
routerenaiguilleur. L'ancien nom ne résout plus du tout. La sonde Ping « Routeur » d'Uptime-Kuma, configurée sur l'hôterouter, 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 EGRESSdu proxy, ou un conteneur ajouté surcahutesans règle dans le blocDOCKER INGRESS, échoue par timeout sans message explicite.
