En bref
| Prérequis | SWAG installé (épisode précédent), CrowdSec configuré, AdGuard Home comme DNS/DHCP |
| Résultat | SWAG qui se recharge automatiquement, captcha CrowdSec fonctionnel, rÚgles réseau internes/externes, géoblocage actif |
| Ressources | Securing SWAG â virtualize.link |
Cet épisode est le neuviÚme de la série sur mon reverse proxy SWAG. Quatre sujets abordés, par ordre de complexité croissante.
Rappel rapide : Ă quoi sert un reverse proxy
Pour les gens qui ne connaissent pas encore : un reverse proxy, câest un guichet dâentrĂ©e unique pour tous vos services auto-hĂ©bergĂ©s. PlutĂŽt que dâexposer chaque service individuellement sur internet â avec tout ce que ça implique en termes de sĂ©curisation individuelle â on centralise tout derriĂšre SWAG, qui gĂšre pour vous la sĂ©curisation des accĂšs.
SWAG sâoccupe de :
- La gestion des certificats HTTPS
- Lâauthentification des utilisateurs (avec Authelia, SSO, multifacteur)
- Lâanalyse comportementale avec CrowdSec pour dĂ©tecter et bannir les IP suspectes
- La centralisation des logs
Chez moi, Homepage sert de page dâaccueil qui liste tous mes services : Nextcloud pour les fichiers et photos, Jellyfin pour les vidĂ©os et musique, Duplicati pour les sauvegardes, LibreSpeed pour les tests de dĂ©bit, File Browser pour manipuler des fichiers, Vaultwarden pour les mots de passe. Plus la couche domotique avec Jeedom, Home Assistant, et AdGuard Home pour le DNS/DHCP.
Tout ça tourne sur une machine qui fait tourner Open MediaVault avec Docker par-dessus.
Point 1 â Auto-reload natif dans SWAG
Câest la nouveautĂ© la plus directement utile de cet Ă©pisode.
Depuis SWAG v3.1.0-ls356 (publiĂ©e mi-janvier 2025), lâauto-reload est intĂ©grĂ© directement dans lâimage de base. Ce nâĂ©tait pas le cas avant â il fallait utiliser un Docker mod sĂ©parĂ©, que jâavais dâailleurs mentionnĂ© dans une vidĂ©o prĂ©cĂ©dente. Ce mod est maintenant dĂ©prĂ©ciĂ© (deprecation notice dans le README de lâimage), remplacĂ© par la fonctionnalitĂ© native.
ConcrĂštement, lâauto-reload surveille tous les fichiers sous config/nginx/ (probablement via inotify en interne) et dĂ©clenche automatiquement un rechargement de la configuration nginx Ă chaque modification. Plus besoin de faire docker compose restart swag aprĂšs avoir modifiĂ© un fichier .conf.
Pour lâactiver, deux variables dâenvironnement Ă ajouter dans le docker-compose de SWAG :
environment:
- SWAG_AUTORELOAD=true
- SWAG_WATCH_LIST= # optionnel, personnalisable
Par défaut SWAG_AUTORELOAD=false, donc si vous ne le mettez pas, rien ne change.
DĂ©monstration en direct : jâai renommĂ© le fichier librespeed.subdomain.conf en .backup â SWAG a immĂ©diatement dĂ©tectĂ© le dĂ©placement et rechargĂ© sa configuration. En rafraĂźchissant la page LibreSpeed, le service Ă©tait dĂ©jĂ inaccessible, remplacĂ© par la page 404 par dĂ©faut de mon reverse proxy. Pareil dans lâautre sens : modifier le fichier dĂ©clenche un rechargement visible dans les logs avec un modify suivi dâun rechargement.
Mise en garde importante : je conseille de dĂ©sactiver lâauto-reload en production une fois les services stables. En phase de mise en place ou dâexpĂ©rimentation, câest super pratique. Mais si on modifie un fichier par inadvertance et que ça dĂ©clenche un rechargement avec une configuration incorrecte, ça peut rendre des services momentanĂ©ment inaccessibles. Pire : si on dĂ©commente des lignes de protection Authelia par erreur, on peut se retrouver Ă exposer des services non sĂ©curisĂ©s sur internet sans sâen rendre compte immĂ©diatement.
La bonne pratique en production : faire toutes ses modifications, les passer en revue avec VS Code pour vĂ©rifier lâensemble des changements, puis faire un seul rechargement intentionnel.
Point 2 â reCAPTCHA CrowdSec enfin fonctionnel
La remediation par captcha Ă©tait une fonctionnalitĂ© que jâavais essayĂ© de configurer il y a longtemps, mais elle ne fonctionnait pas correctement Ă cause de bugs de compatibilitĂ© entre les dĂ©pendances Lua de nginx (utilisĂ©es par SWAG) et le module CrowdSec. Deux bugs en particulier me posaient problĂšme :
- Une erreur critique au dĂ©marrage de SWAG, ouverte par Giovanni Papini â corrigĂ©e il y a peu
- Des warnings de dĂ©prĂ©ciation autour de la cryptographie dans les images nginx â moins critique mais embĂȘtant
Ces incompatibilités entre les dépendances Lua de SWAG et CrowdSec faisaient que le captcha était activable sur le papier mais inopérant en pratique. Les deux problÚmes sont maintenant résolus.
Comment ça fonctionne : CrowdSec peut prendre deux types de dĂ©cisions face Ă une IP suspecte â le ban direct, ou le captcha. Le captcha prĂ©sente un challenge Google reCAPTCHA Ă lâIP concernĂ©e : si câest un humain, il rĂ©sout le challenge et passe ; si câest un bot, il ne peut pas.
La configuration du captcha remediation se fait dans le fichier de profil CrowdSec. On ajoute une entrĂ©e captcha remediation avant lâentrĂ©e de ban standard, avec des conditions pour dĂ©cider quand lâappliquer. LâidĂ©e : si une IP a dĂ©clenchĂ© un scĂ©nario HTTP et que câest sa premiĂšre alerte, on lui prĂ©sente dâabord un captcha pendant 4 heures. Si elle le rĂ©sout, elle est libĂ©rĂ©e. Sinon, elle reste bloquĂ©e, et les alertes suivantes peuvent dĂ©clencher un ban direct.
DĂ©monstration : jâai ajoutĂ© manuellement une dĂ©cision captcha sur mon adresse IP locale via cscli decisions add --ip X.X.X.X --type captcha --duration 1h. En retournant sur ma page Homepage, tous les services sont passĂ©s au rouge, et jâavais bien un Google reCAPTCHA qui sâaffichait. AprĂšs avoir cliquĂ© (Google a dĂ©tectĂ© une activitĂ© humaine, pas de challenge dâimages Ă rĂ©soudre), jâĂ©tais dĂ©banni. Jâai aussi reçu une notification par mail, puisque jâai configurĂ© les notifications CrowdSec par email â utile pour ĂȘtre informĂ© des bannissements sans avoir Ă surveiller activement.
Point 3 â DiffĂ©renciation accĂšs interne / externe
Cette configuration nâest pas dans les templates par dĂ©faut de SWAG. Je lâai mise en place sur ma machine Ă ma sauce, en mâinspirant dâun blog que je nâai malheureusement pas retrouvĂ© pour le citer correctement.
Le principe : chaque fichier de configuration de service dans nginx/proxy-confs/ peut avoir deux blocs de rĂšgles dâaccĂšs â un pour le rĂ©seau interne, un pour lâaccĂšs externe depuis internet. Ăa permet dâappliquer des rĂšgles de sĂ©curitĂ© diffĂ©rentes selon lâorigine.
Le contexte rĂ©seau chez moi : ma Livebox est le routeur, mais AdGuard Home est le serveur DNS/DHCP. Dans AdGuard Home, jâai des réécritures DNS : mes noms de domaine publics (monservice.mondomaine.fr) redirigent vers lâIP locale de mon serveur reverse proxy quand on est sur mon rĂ©seau local. RĂ©sultat : que je sois chez moi ou dehors, jâutilise les mĂȘmes URLs. Mais quand je suis chez moi, le trafic passe en local directement â pas besoin de sortir sur internet pour atteindre mes services.
Deux fichiers de configuration à créer dans le dossier nginx/ :
internal.conf â valide uniquement si la requĂȘte vient dâune IP du rĂ©seau local : il faut y dĂ©clarer la plage dâadresses autorisĂ©es (ex. 192.168.1.0/24) avec un allow, suivi dâun deny all pour tout le reste.
external.conf â valide pour les accĂšs depuis internet, avec gestion du gĂ©oblocage : il faut y tester la variable $geo_blacklist et retourner une 404 si elle vaut yes.
Dans chaque fichier .conf de service, jâajoute ensuite un bloc selon que le service est exposĂ© en interne uniquement, en externe uniquement, ou dans les deux cas. Pour LibreSpeed par exemple, ça nâa aucun sens de lâexposer sur internet â je fais mes tests de dĂ©bit en interne. Je le configure donc en internal uniquement.
Lâavantage de cette approche : tout est centralisĂ© dans le fichier de config du service, visible dâun seul coup dâĆil. Et si un jour je veux exposer un service qui nâĂ©tait quâinterne, je change juste le bloc dans le fichier .conf correspondant â avec lâauto-reload, câest actif immĂ©diatement.
Point 4 (bonus) â GĂ©oblocage avec DB-IP
Le docker mod DB-IP enrichit SWAG avec une base de donnĂ©es de gĂ©olocalisation des adresses IP. On peut ensuite configurer nginx pour rejeter automatiquement les requĂȘtes provenant de certains pays.
Activation dans le docker-compose : dans la variable DOCKER_MODS, on ajoute le module linuxserver/mods:swag-dbip à la suite des autres mods déjà présents (swag-dashboard, swag-crowdsec).
Configuration en deux étapes :
1. Dans nginx/nginx.conf, dans le bloc HTTP : on ajoute un include vers le fichier dbip.conf.
2. Dans dbip.conf (fourni par le docker mod), on liste les pays Ă bloquer : le bloc geo associe la variable $geo_blacklist Ă une valeur yes pour chaque code ISO 3166-1 des pays Ă bloquer, et Ă no pour les plages dâIP locales (qui ne sont jamais gĂ©olocalisables).
Pour les adresses IP locales (192.168.x.x), il nây a pas de gĂ©olocalisation possible â elles sont toujours en no donc toujours autorisĂ©es. Câest la raison pour laquelle cette configuration sâarticule avec le point 3 : en interne, on vĂ©rifie que câest bien une IP locale ; en externe, on vĂ©rifie que le pays nâest pas bloquĂ©.
Dans mon cas, jâai dĂ©jĂ une liste de pays bloquĂ©s qui me paraissent ne pas avoir de raison lĂ©gitime dâaccĂ©der Ă mes services personnels. Je pense mĂȘme Ă rajouter les Ătats-Unis â je suis rĂ©guliĂšrement scannĂ© depuis des IPs amĂ©ricaines, et il nây a aucune raison quâun amĂ©ricain inconnu accĂšde Ă mes donnĂ©es personnelles auto-hĂ©bergĂ©es.
En pratique, quel niveau activer ?
Pour synthĂ©tiser ce que jâai en production :
- Auto-reload : activĂ© uniquement quand jâajoute ou modifie des services, dĂ©sactivĂ© le reste du temps
- reCAPTCHA CrowdSec : activĂ© en permanence â câest maintenant stable, autant lâavoir
- Internal/External : activé en permanence, sur tous les services
- DB-IP : activé en permanence avec une liste de pays bloqués
Ce sont des couches complĂ©mentaires. Chacune apporte quelque chose, et lâensemble forme une protection assez solide pour un serveur personnel auto-hĂ©bergĂ©.
La sĂ©rie sur SWAG continuera â il y a encore des sujets Ă couvrir pour complĂ©ter lâinstallation.