Ce que vous gagnez réellement à masquer l'origine
Trois choses, et cela vaut la peine d'être précis. D'abord, le déluge cesse d'arriver sur la machine incapable de l'absorber : un VPS unique a une liaison montante finie, et quand l'adresse qui encaisse les paquets est un edge conçu pour ça plutôt que la machine qui héberge votre base de données, une attaque volumétrique devient le problème d'ingénierie de quelqu'un d'autre. Ensuite, l'application cesse d'être joignable en contournant ses propres défenses — limites de débit, règles anti-bots, WAF et geo-blocking se contournent tous trivialement dès que quelqu'un peut se connecter directement à l'origine, et la plupart de ceux qui les déploient ne vérifient jamais si c'est encore possible. Enfin, l'adresse qui sert votre contenu cesse d'être l'adresse qui détient vos données, ce qui est précisément la séparation qui fait atterrir une plainte pour abus, un scan ou une sonde ciblée quelque part de bon marché et remplaçable.
Et voici maintenant la partie honnête. Masquer une origine n'est pas de l'anonymat — cela masque une adresse, pas une personne, et la trace de paiement, l'enregistrement du domaine et le compte qui se trouvent derrière sont un problème distinct qui a son propre guide. Cela ne corrige pas votre application : une origine que personne ne trouve reste exploitable dès l'instant où quelqu'un la trouve, et les origines finissent toujours par être trouvées. Cela ne cache rien à votre hébergeur, qui par définition sait quelle machine répond sur quelle adresse. Traitez-la comme une couche qui augmente le coût d'une attaque, posée par-dessus une machine correctement durcie au préalable — pas comme un substitut à l'une ou l'autre.
SP·02Toutes les façons dont une IP d'origine fuit
Si tant d'origines soi-disant cachées ne le sont pas vraiment, c'est parce qu'on ferme un canal et qu'on suppose que les autres ont suivi. Ce n'est pas le cas. Voici la liste que nous parcourons, à peu près dans l'ordre de fréquence à laquelle c'est elle qui a vraiment fait tomber quelqu'un :
- DNS historique. Les collecteurs de DNS passif enregistrent votre enregistrement A depuis bien avant que vous ne soyez passé derrière un proxy. L'adresse que vous utilisiez l'an dernier est une recherche permanente, consultable et gratuite.
- Transparence des certificats. Chaque certificat publiquement approuvé est publié dans des journaux publics append-only, avec chaque nom d'hôte qu'il couvre. Un certificat émis pour
origin.example.comannonce ce nom au monde entier, et son enregistrement A fait le reste. - Les sous-domaines qui n'ont jamais bougé.
mail,ftp,webmail,cpanel,dev,staging,vpn,monitor— le domaine racine est passé derrière le CDN, et ceux-là sont restés pointés sur la machine. - Le mail. Un enregistrement MX sur l'origine révèle l'adresse directement ; un en-tête
Received:dans un mail envoyé par votre application aussi, et n'importe qui peut le déclencher avec un formulaire de réinitialisation de mot de passe. - Les requêtes sortantes. Webhooks, récupération d'avatars, flux RSS, aperçus de liens, vérifications de mise à jour, callbacks OAuth. Chacune révèle l'adresse de l'origine à qui opère l'autre bout — et une fonction d'aperçu d'URL laisse un attaquant choisir cet autre bout.
- Répondre sur l'adresse nue. Si l'origine sert encore votre site à une requête sans en-tête
Hostcorrespondant, les scanners qui parcourent tout Internet l'ont déjà indexée : le hash du favicon, le titre de la page, l'empreinte du certificat et l'ordre des en-têtes HTTP sont tous consultables. - L'enregistrement IPv6 que vous avez oublié. L'enregistrement A est passé sur le proxy ; l'enregistrement AAAA, lui, pointe encore vers la maison.
- L'application qui parle d'elle-même. URLs absolues dans la config d'un CMS, redirections vers un nom d'hôte interne, stack traces, bannières
Server, source maps, un endpoint de statut non authentifié.
Remarquez ce que la plupart de ces canaux ont en commun : ils sont permanents. Les journaux de certificats sont append-only et le DNS passif est une archive. Vous ne pouvez pas retirer une adresse une fois qu'elle est publiée — vous pouvez seulement cesser de l'utiliser, ce qui est exactement pourquoi l'ordre des opérations ci-dessous compte.
SP·03Deux formes d'edge : un CDN, ou votre propre machine
Un CDN commercial vous offre une capacité anycast qu'aucun serveur unique ne peut égaler, dans des dizaines de villes, souvent sur un palier gratuit. La contrepartie, c'est que le TLS se termine sur une infrastructure que vous ne contrôlez pas : l'opérateur voit votre trafic en clair, sait à quel compte il appartient, et peut être contraint d'agir sur la base de cette connaissance — ou simplement décider un beau matin que votre contenu n'est plus le bienvenu. Il existe aussi un problème plus subtil, propre aux façades partagées — si le pare-feu de votre origine autorise les plages d'adresses publiées du CDN, alors n'importe qui d'autre ayant un compte sur ce même CDN se trouve dans votre allowlist et peut pointer son propre nom d'hôte vers votre origine. C'est un contournement réel, pas théorique, et c'est exactement pourquoi l'origin pull authentifié existe.
Un edge que vous gérez vous-même, c'est la contrepartie inverse. Personne d'autre que vous ne détient la clé privée, la machine se trouve dans une juridiction que vous avez choisie délibérément, et cela coûte $8.00/mois pour le plus petit plan — littéralement une erreur d'arrondi au regard de ce que cela protège. Ce que vous n'obtenez pas, c'est l'anycast : un déluge de 200 Gbps saturera la liaison montante de l'edge, aussi soignée que soit votre configuration nginx, donc l'absorption au niveau réseau doit venir d'ailleurs. Dans notre cas, ce sont les 1.5 Tbps de mitigation en amont placés devant chaque machine de la flotte, ce qui est justement ce qui rend un edge autogéré viable plutôt qu'un point de rupture unique. Les deux formes se combinent d'ailleurs très bien : un CDN devant pour la portée et le volume, votre propre nœud derrière pour la partie que vous refusez de céder. Choisissez selon l'échec que vous préféreriez avoir à expliquer.
SP·04Le tunnel est la partie que la plupart des gens ratent
Le montage habituel consiste en une origine qui écoute sur 0.0.0.0:443, avec un pare-feu qui place les adresses du proxy dans une allowlist. Ça fonctionne, et c'est le maillon le plus faible du montage. Les allowlists dérivent — les plages publiées changent et la mise à jour n'est jamais appliquée ; elles sont partagées, donc sur un CDN public elles admettent tous les autres clients ; et surtout, quand elles échouent, elles échouent grand ouvertes, parce que l'origine reste tout ce temps un écouteur public actif, à la merci d'une simple erreur de configuration ou d'un ufw disable lâché en pleine session de débogage.
La version qui tient fait l'inverse : l'origine n'a aucun écouteur public, point final. Un tunnel WireGuard est établi entre l'edge et l'origine, le serveur web ne se lie qu'à l'adresse du tunnel, et l'interface publique applique une politique default-deny sur les deux familles d'IP, sans exception pour les ports 80 ou 443. La joignabilité n'est alors plus une règle que quelqu'un peut oublier de renouveler — c'est l'absence de route. WireGuard est le bon outil ici parce que c'est un module noyau à la surface d'attaque minuscule, qu'il reste silencieux face aux scanners non authentifiés (un paquet non authentifié ne reçoit aucune réponse, si bien que le port UDP ne semble même pas exister), et qu'il ne coûte que des microsecondes à un chiffre par paquet. Si vous n'en avez encore jamais configuré un, le guide WireGuard couvre les fondamentaux ; ici, nous n'avons besoin que d'un lien point à point entre deux pairs.
Une règle avant de commencer, et c'est celle qui vous épargnera votre après-midi : gardez une seconde session SSH ouverte en permanence. Enfermer SSH derrière un tunnel que vous êtes encore en train de reconfigurer, c'est exactement comme ça qu'on perd une machine, et chez un hébergeur qui ne conserve aucune identité, il n'y a aucun support vers lequel se tourner et personne ne peut vous accorder de session de console — le chemin de retour, c'est un redéploiement suivi d'une restauration, rapide si vos sauvegardes sont à jour, et définitif si elles ne le sont pas.
SP·05Les certificats, et le journal qui publie vos noms d'hôte
La transparence des certificats est une chose réellement bénéfique, qui se fera un plaisir de ruiner votre semaine. Chaque certificat émis par une AC publique est soumis à des journaux append-only que n'importe qui peut consulter, et l'entrée contient chaque nom présent dans le certificat. Émettez-en un pour origin.example.com ou direct.example.com, et vous avez publié, de façon permanente et dans un format structuré, exactement le nom d'hôte que vous essayiez de ne pas annoncer. Pire, l'habitude de mettre les noms d'hôte de staging et d'administration dans la même liste SAN transforme un renouvellement fait à la légère en plan détaillé de votre infrastructure.
La discipline à adopter est simple. Les certificats publics ne vivent que sur l'edge, et ne couvrent que les noms réellement utilisés par le public. L'origine reçoit un certificat autosigné, ou un certificat émis par une petite AC privée, épinglé dans la configuration du proxy via proxy_ssl_trusted_certificate — rien sur un lien que seul votre propre proxy parle n'a besoin d'être publiquement approuvé, et émettre un certificat public pour ce lien ne vous rapporte rien d'autre qu'une entrée dans un journal. Si vous avez besoin de nombreux sous-domaines publics, un wildcard publie un seul nom au lieu de trente. Et faites basculer ACME vers le challenge DNS-01 : HTTP-01 exige que quelque chose réponde sur le port 80 pour le nom d'hôte en cours de validation, ce qui est précisément l'écouteur public que vous venez de supprimer. Enfin, acceptez l'asymétrie — les journaux sont append-only, donc un nom d'hôte déjà publié ne peut pas être retiré. S'il pointait vers l'origine, l'origine a besoin d'une nouvelle adresse.
Le mail, et les autres services qui répondent sur la mauvaise adresse
Le mail est le contournement classique, parce qu'il doit par définition rester joignable. Si l'enregistrement MX de votre domaine pointe vers l'origine, l'exercice est terminé avant même d'avoir commencé — l'enregistrement est public, et un simple dig met fin à la chasse. Même avec un MX ailleurs, une application qui envoie du mail directement depuis l'origine imprime l'adresse de la machine émettrice dans la chaîne Received: de chaque message, et n'importe quel formulaire qui mail un utilisateur à la demande transforme cela en recherche en libre-service. Le correctif consiste à faire de l'origine un client, jamais un serveur : relayez le mail sortant via un service de soumission ou une machine séparée, gardez le MX sur une machine qui a le droit d'être trouvée, et lisez les en-têtes complets d'un message de test avant de considérer que c'est fait. Faire tourner sa propre messagerie à grande échelle est un projet en soi, qui n'a rien à faire sur la machine que vous cachez.
Passez ensuite au peigne fin tout ce qui écoute discrètement par ailleurs. Agents de supervision, tableaux de bord de conteneurs, ports de base de données ouverts « temporairement », un endpoint de métriques sur le port 9100, un panneau de contrôle sur un port élevé, un démon SSH sur l'interface publique. Chacun d'eux est un service qui répond sur l'adresse que vous essayez de garder privée, et les scanners trouvent les ports élevés aussi facilement que les ports bas. L'audit tient en une seule commande — ss -tulpn — et la sortie correcte est une liste où rien n'est lié à une adresse publique. L'étape trois ci-dessous est ce qui rend cela vrai, et le maintient vrai.
Egress : les connexions que votre origine initie
Une origine qui n'accepte rien peut quand même se trahir, parce qu'elle ne se contente pas de recevoir des connexions — elle en ouvre aussi. Miroirs de paquets, NTP, un webhook vers un prestataire de paiement, une API de bot, une image récupérée pour un aperçu de lien, une vérification de licence, une session SMTP sortante, un remote Git, un outil de reporting d'erreurs. Pour chacun de ces interlocuteurs, l'adresse publique de l'origine n'est autre que l'adresse source de la connexion. La plupart du temps, c'est inoffensif, parce que c'est vous qui avez choisi l'autre bout et que vous lui faites confiance. Le problème, ce sont les quelques endpoints qu'un attaquant choisit lui-même : collez un lien dans n'importe quoi qui affiche un aperçu, enregistrez un webhook, ou trouvez une server-side request forgery dans un importeur d'images, et l'origine résout et se connecte vers un hôte que l'attaquant surveille. C'est une désanonymisation en deux minutes, sans le moindre exploit.
Il existe deux réponses défendables. La stricte fait passer tout l'egress par le tunnel et laisse l'edge faire du NAT dessus, de sorte que l'adresse source sortante de l'origine devient celle de l'edge — un seul réglage dans la config du pair, plus le forwarding et une règle de masquerade à l'autre bout. wg-quick gère la boucle de routage à votre place : avec une route 0.0.0.0/0, il installe une règle fwmark pour que les paquets du tunnel lui-même continuent d'atteindre l'endpoint directement — c'est justement la partie que les gens cassent quand ils écrivent les routes à la main. La réponse pragmatique conserve l'egress direct pour le trafic que vous contrôlez, et place un proxy devant tout ce qui va chercher une URL fournie par l'utilisateur. Ce qui n'est pas défendable, c'est de ne pas savoir laquelle des deux vous avez. Décidez-le délibérément, puis vérifiez-le avec une requête vers un hôte que vous possédez et un coup d'œil à l'adresse source dans ses logs.
Ce que ça coûte, et comment prouver que ça fonctionne
La ligne budgétaire, c'est un VPS supplémentaire. Le plus petit plan à $8.00/mois termine le TLS et fait proxy pour un petit site sans même sentir la charge — un reverse proxy, c'est essentiellement une copie de socket, et 2 vCPU avec 4 Go de RAM sont largement suffisants, bien au-delà du point où l'origine derrière lui devient le goulot d'étranglement. Placez-le dans une région différente de celle de l'origine, pour qu'un seul instrument juridique ou un seul incident dans une installation ne puisse pas atteindre les deux à la fois, et n'oubliez pas la latence : un saut supplémentaire ajoute de vraies millisecondes, donc un edge à Amsterdam devant une origine à Kuala Lumpur est une décision de conception, pas un accident. Des paires situées sur le même continent coûtent typiquement quelques millisecondes à un chiffre, et la réutilisation de session TLS que vous gagnez au niveau de l'edge compense souvent ce coût dès le premier vrai chargement de page.
Prouver que ça fonctionne, c'est ce qui sépare une configuration d'un contrôle, et c'est un travail récurrent, pas ponctuel — chaque nouveau sous-domaine, chaque nouveau certificat, chaque nouvelle intégration est une nouvelle occasion de republier l'adresse. La batterie de tests de l'étape sept prend environ dix minutes : essayez de joindre votre site directement sur l'adresse de l'origine, listez chaque nom d'hôte que vous avez jamais certifié, parcourez les noms de sous-domaines évidents, vérifiez l'enregistrement AAAA que vous avez oublié, et envoyez-vous un mail. Relancez-la après chaque changement d'infrastructure. Et gardez la conclusion en tête pendant que vous le faites : si l'origine répond, le bon correctif n'est pas une règle de pare-feu de plus — c'est une nouvelle adresse, parce que l'ancienne se trouve déjà dans les archives de quelqu'un. Dans la pile, cette couche se situe après le durcissement et à côté des sauvegardes hors site : le durcissement décide à quel point la machine est difficile à casser, les sauvegardes décident à quelle vitesse vous vous en remettez, et ceci décide à quel point la machine est difficile à trouver en premier lieu.
SP·09Étape par étape
-
01
Déployez l'edge et ne lui donnez qu'un seul rôle
Déployez un second VPS dans une région qui n'est pas celle où vit l'origine, et traitez-le comme un appareil à vocation unique : terminaison TLS, reverse proxy, et rien d'autre. Pas de base de données, pas de code applicatif, pas de script shell dont l'absence se remarquerait. Appliquez-lui la checklist de la première heure — SSH par clé uniquement, pare-feu default-deny sur les deux familles d'IP, mises à jour de sécurité automatiques — puis ouvrez exactement trois ports.
ssh root@198.51.100.20 apt update && apt full-upgrade -y hostnamectl set-hostname edge-01 apt install -y nginx wireguard-tools ufw unattended-upgrades ufw default deny incoming && ufw default allow outgoing ufw limit 22/tcp ufw allow 80,443/tcp ufw allow 51820/udp ufw --force enable
-
02
Établissez le tunnel avant de toucher au DNS
Deux pairs, un seul lien. Générez une paire de clés sur chaque machine et donnez au tunnel son propre petit sous-réseau — l'origine finira par n'être joignable qu'à
10.66.0.2, et nulle part ailleurs. C'est l'origine qui compose vers l'edge (c'est le côté qui n'aura aucun port ouvert), donc c'est elle qui porte l'Endpointet un keepalive ; l'edge, lui, se contente d'écouter.# on both machines umask 077; wg genkey | tee privkey | wg pubkey > pubkey # edge-01 — /etc/wireguard/wg0.conf [Interface] Address = 10.66.0.1/24 ListenPort = 51820 PrivateKey = <edge-privkey> [Peer] PublicKey = <origin-pubkey> AllowedIPs = 10.66.0.2/32 # origin-01 — /etc/wireguard/wg0.conf [Interface] Address = 10.66.0.2/24 PrivateKey = <origin-privkey> [Peer] PublicKey = <edge-pubkey> Endpoint = 198.51.100.20:51820 AllowedIPs = 10.66.0.1/32 PersistentKeepalive = 25
Activez-le aux deux extrémités, et confirmez le handshake avant d'aller plus loin — un tunnel qui ne fonctionne que jusqu'au prochain redémarrage est pire que pas de tunnel du tout.
systemctl enable --now wg-quick@wg0 wg show # expect a recent handshake and non-zero transfer ping -c3 10.66.0.1 # from the origin
-
03
Rendez l'origine injoignable depuis l'Internet public
C'est l'étape qui fait le vrai travail, et celle où les gens se retrouvent enfermés dehors. Ouvrez une seconde session SSH et laissez-la connectée avant d'exécuter quoi que ce soit ci-dessous — il n'existe ici aucune console de support sur laquelle se rabattre. Liez ensuite le serveur web à l'adresse du tunnel, coupez tout sur l'interface publique, et n'autorisez que le tunnel plus l'endpoint WireGuard lui-même.
# /etc/nginx/sites-available/app — listen on the tunnel only listen 10.66.0.2:8080; ufw --force reset ufw default deny incoming ufw default allow outgoing ufw allow in on wg0 to any port 8080 proto tcp ufw allow in on wg0 to any port 22 proto tcp ufw allow from 198.51.100.20 to any port 51820 proto udp ufw --force enable # the audit: nothing may be bound to a public address ss -tulpn | grep -Ev '10\.66\.0\.2|127\.0\.0|\[::1\]'
Si cette dernière commande affiche un service, c'est une fuite — corrigez l'adresse de liaison plutôt que d'ajouter une règle de pare-feu autour. Deux écouteurs ont le droit de survivre : WireGuard sur le port 51820, et sshd si vous ne l'avez pas encore déplacé sur le tunnel.
-
04
Terminez le TLS sur l'edge, et faites le proxy via le tunnel
Émettez le certificat public sur l'edge, pour les noms réellement utilisés par le public, et redirigez le proxy en amont vers l'adresse du tunnel. Le second bloc server n'est pas une décoration facultative : c'est lui qui empêche l'edge de servir votre site à un scanner qui se connecte par IP sans en-tête
Host, ce qui est exactement la manière dont la porte d'entrée se fait fingerprinter.# edge-01 — /etc/nginx/sites-available/example.com server { listen 443 ssl; http2 on; server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; add_header Strict-Transport-Security "max-age=63072000" always; location / { proxy_pass http://10.66.0.2:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_http_version 1.1; } } # anything that is not a known hostname gets nothing at all server { listen 80 default_server; listen 443 ssl default_server; ssl_reject_handshake on; return 444; } -
05
Redonnez à l'origine la vraie IP du client
Derrière un proxy, chaque requête arrive depuis
10.66.0.1. Si vous ne faites rien, vos logs d'accès deviennent inutilisables, la limitation de débit par IP freine le tunnel au lieu de l'attaquant, etfail2banfinit par bannir l'edge et met le site hors ligne — une façon authentiquement populaire de provoquer une panne en voulant durcir la sécurité. Faites confiance à l'en-tête forwarded, mais uniquement depuis l'adresse du tunnel, jamais depuis le reste du monde.# origin-01 — /etc/nginx/conf.d/realip.conf set_real_ip_from 10.66.0.1; real_ip_header X-Forwarded-For; real_ip_recursive off;
Faites l'équivalent dans l'application —
ProxyFixdans Flask,TRUSTED_PROXIESdans Laravel,set_real_ip_fromplus la liste de proxies de confiance propre au framework — et placez la limitation de débit sur l'edge, là où l'adresse réelle du client existe nativement :# edge-01 — /etc/nginx/nginx.conf (http block) limit_req_zone $binary_remote_addr zone=front:10m rate=20r/s; # then, inside the location block limit_req zone=front burst=40 nodelay;
-
06
Sortez le mail et l'egress de l'adresse de l'origine
Pointez le MX vers une machine qui a le droit d'être trouvée, envoyez le mail sortant via un relais plutôt que directement depuis l'origine, et faites basculer le renouvellement des certificats vers le challenge DNS-01 pour que rien n'ait à répondre sur le port 80. Décidez ensuite du sort du reste du trafic sortant. Pour tout faire passer par l'edge, élargissez le
AllowedIPsde l'origine et laissez l'edge faire du masquerade —wg-quickinstalle la règle fwmark qui garde le tunnel lui-même joignable, donc vous n'avez pas besoin d'écrire la route de l'endpoint à la main.# origin-01 — /etc/wireguard/wg0.conf, in [Peer] AllowedIPs = 0.0.0.0/0, ::/0 # edge-01 — forward and NAT the tunnel echo 'net.ipv4.ip_forward=1' > /etc/sysctl.d/99-fwd.conf && sysctl --system ufw route allow in on wg0 out on eth0 # in /etc/ufw/before.rules, above the *filter block: # *nat # :POSTROUTING ACCEPT [0:0] # -A POSTROUTING -s 10.66.0.0/24 -o eth0 -j MASQUERADE # COMMIT systemctl restart ufw && wg-quick down wg0 && wg-quick up wg0
Vérifiez-le ensuite depuis l'origine :
curl -s https://ifconfig.codoit renvoyer l'adresse de l'edge, pas la sienne. -
07
Traquez votre propre origine, puis rédigez le runbook
Attaquez-la comme le ferait quelqu'un d'autre. La première commande est la plus importante — si l'origine sert encore votre site quand on s'adresse directement à elle, rien de ce qui précède ne fonctionne encore.
# does the origin answer for your hostname? curl -sk --resolve example.com:443:203.0.113.10 https://example.com/ \ -o /dev/null -w '%{http_code}\n' # want: a timeout, not 200 # every hostname you have ever certified, from the public logs curl -s 'https://crt.sh/?q=%25.example.com&output=json' \ | grep -o '"name_value":"[^"]*"' | sort -u # records that never moved dig +short example.com A; dig +short example.com AAAA; dig +short example.com MX for h in www mail ftp webmail cpanel dev staging vpn monitor origin direct; do printf '%-9s %s\n' "$h" "$(dig +short $h.example.com | tr '\n' ' ')" doneEnvoyez-vous ensuite un mail depuis l'application et lisez la chaîne
Received:complète, puis collez un lien vers un hôte que vous contrôlez dans n'importe quelle fonctionnalité qui affiche des aperçus, et vérifiez quelle adresse est allée le chercher. Notez à quoi ressemble un résultat correct pour chaque test, et relancez le tout après chaque changement de DNS, chaque nouveau certificat et chaque nouvelle intégration. Si l'un d'eux fait ressortir l'origine, reconstruisez-la sur une adresse neuve — celle qui a été publiée est déjà archivée, et aucune règle de pare-feu ne la fera revenir en arrière.


