Ce qui tourne chez moi
J’héberge chez moi l’essentiel de ce que j’utilise au quotidien. Ce n’est pas une vitrine : c’est ce qui tourne, avec les compromis que ça suppose. Cette page dit quoi, sur quoi, comment j’en garde la porte, et ce que les pannes m’ont appris.
La forme générale
La porte d’entrée
Tout ce qui vient d’Internet passe par une seule machine, frontend, et par un seul processus : Caddy. Il termine le TLS, renouvelle les certificats et relaie vers les services, qui n’ont aucune interface publique.
Devant lui, CrowdSec, en trois morceaux qui font trois choses distinctes :
| Composant | Ce qu’il fait |
|---|---|
| Moteur de détection | lit les journaux de Caddy et qualifie les comportements |
| Bouncer pare-feu | applique les décisions au niveau réseau, avant Caddy |
| AppSec / WAF | inspecte le contenu des requêtes, pas seulement l’adresse d’origine |
La distinction entre les deux derniers est celle qui m’a décidé. Un bouncer d’adresses bloque un attaquant connu ; il ne voit rien d’une requête d’apparence banale envoyée par une adresse propre. L’AppSec regarde ce que la requête demande. Les deux se complètent, aucun ne remplace l’autre.
Une seule instance de détection tourne, sur frontend. L’interface web hébergée sur docker consomme cette instance par une identité machine dédiée, plutôt que d’en démarrer une seconde : deux moteurs, ce serait deux jeux de règles à tenir à jour et deux occasions de croire qu’on est protégé par celui qui ne tourne plus.
Trois provenances, deux politiques
Le même Caddy sert trois publics, et c’est l’adresse d’origine de la requête qui décide de ce qu’elle a le droit de voir.
| Provenance | Ce qui est joignable |
|---|---|
| Internet | les seuls services publiés volontairement : ce site, les webhooks, les notifications derrière authentification |
| Réseau local | tout, y compris les interfaces d’administration |
| Maillage Headscale | même chose que le réseau local, depuis n’importe où |
Trois provenances, mais deux politiques seulement : le maillage n’est pas un troisième niveau, c’est le réseau local étendu à mes appareils où qu’ils soient. C’est tout l’intérêt d’un VPN maillé — il déplace la frontière au lieu d’ouvrir une porte de plus. Il n’y a donc aucun service exposé « juste pour y accéder de l’extérieur ».
Le filtre tient en un fragment de configuration, importé par chaque service interne :
(internal_only) {
@public not remote_ip private_ranges 100.64.0.0/10
respond @public 404
}Le code retourné est un 404, pas un 403. Un 403 confirmerait que la ressource existe et qu’elle est protégée ; un 404 dit qu’il n’y a rien à cette adresse. La différence ne change rien pour moi et retire à un visiteur non invité la seule information qu’il aurait obtenue gratuitement : la liste de ce qui mérite d’être protégé.
Un certificat qui ne dit pas ce qu’il couvre
Répondre 404 depuis Internet ne sert à rien si la liste des services fuit ailleurs — et elle fuit par un canal auquel on ne pense pas : les journaux deCertificate Transparency. Chaque certificat TLS émis par une autorité publique y est inscrit, avec les noms qu’il couvre, et ces journaux sont consultables par n’importe qui. Demander un certificat pourun-service.exemple.net revient donc à publier l’existence deun-service, quand bien même il répondrait 404 à tout le monde.
D’où deux certificats en joker, un par domaine, plutôt qu’un par service. Ce qui paraît dans les journaux publics est *.exemple.net : le nombre de sous-domaines n’y figure pas, ni leurs noms.
Un joker ne s’obtient pas de la même manière qu’un certificat ordinaire. La vérification par HTTP suppose que l’autorité joigne le nom demandé — ce qui est impossible pour un joker, et ce qui exigerait d’exposer chaque service le temps du renouvellement. Il faut donc prouver qu’on contrôle le domaine par le DNS : l’autorité demande de publier un enregistrement précis dans la zone, et Caddy le crée par l’API du registrar, puis le retire.
C’est le seul point où l’infrastructure dépend d’un service extérieur, et il demande une clé d’API qui peut écrire dans la zone DNS — donc une clé qui vaut le domaine entier si elle fuit. Le compromis est assumé : sans elle, chaque nom de service serait publié à chaque renouvellement, tous les deux mois.
Ce que le Caddyfile ne répète pas
La configuration est écrite en fragments réutilisables plutôt qu’en blocs recopiés. Un service interne se déclare en trois lignes qui disent son intention :
import internal_only
import public_security_headers
reverse_proxy …L’intérêt n’est pas d’écrire moins. Il est qu’une correction se fasse à un seul endroit. Quand j’ai durci les en-têtes de sécurité, tous les services publiés en ont bénéficié le même jour, sans que j’aie à me souvenir de la liste — et c’est précisément la liste qu’on oublie.
public_security_headers fait deux choses de nature différente. Il ajoute ce qu’un navigateur doit savoir : HSTS,nosniff, politique de référent, cadrage, restrictions de permissions. Et il retire Server, Via etX-Powered-By — trois en-têtes qui annoncent le logiciel et sa version à qui les demande. Ne rien dire sur ce qu’on fait tourner ne protège de rien à soi seul ; ça évite simplement de publier de quoi cibler.
La politique de sécurité du contenu, elle, reste par site : ce qu’une page a le droit de charger dépend de la page. Ce site-ci a la sienne, plus stricte, dont je raconte plus bas ce qu’elle m’a coûté.
Ce qui tourne
Sur la VM docker :
| Service | Rôle |
|---|---|
| Homepage | tableau de bord d’accueil |
| Paperless-ngx | gestion documentaire |
| Homebox | inventaire personnel |
| FreshRSS | lecteur de flux |
| Kavita | bibliothèque numérique |
| Actual Budget | budget personnel |
| Dockhand | gestion des piles Docker |
| Uptime Kuma | supervision et alertes |
| Prometheus + Grafana | métriques et tableaux de bord |
| CrowdSec (interface web) | consulte le LAPI hébergé sur frontend |
| n8n | automatisation de traitements personnels |
| ntfy | notifications push auto-hébergées |
Ce qui m’a mordu
Ce que ça m’a appris
La partie la plus utile de cette infrastructure n’est pas ce qui tourne, mais ce qui signale quand ça s’arrête : une sonde qui détecte sans alerter, ou une alerte qui part sans jamais arriver, produit exactement le même silence qu’une panne inexistante.