SSD Nodes Learn 🎉 VPS dès $5.50/mois
Guides Matt ConnorPar Matt Connor

Héberger un site .onion sur votre VPS avec Tor

Créez un service onion v3 sur Ubuntu avec Tor et nginx sur loopback. Découvrez les fuites qui peuvent relier l’adresse .onion à votre IP publique.

Ce que vous allez mettre en place

Un site onion est un serveur web classique qui répond uniquement sur le réseau Tor. Installez Tor, ajoutez deux lignes dans /etc/tor/torrc, lisez l’adresse que Tor écrit pour vous, puis configurez nginx pour écouter sur 127.0.0.1 afin qu’aucun service ne réponde sur l’IP publique. L’installation prend dix minutes. Le reste de ce guide porte sur les fuites, car un site onion échoue généralement lorsque sa propre configuration renvoie directement vers l’opérateur.

Tor était à l’origine appelé « the onion router », et un service onion est un service accessible uniquement par son intermédiaire. Une adresse de version 3 comporte 56 caractères suivis de .onion. Ces caractères correspondent à la clé publique ed25519 du service, à une somme de contrôle et à un octet de version, encodés en base32. Les adresses de version 2 (16 caractères) ont été supprimées du réseau en 2021. Tout ce que vous générez aujourd’hui est donc en v3. L’adresse est la clé, ce qui a deux conséquences. La connexion est chiffrée et authentifiée de bout en bout, sans intervention d’une autorité de certification. Si vous perdez le fichier de clé, vous perdez définitivement l’adresse.

Votre serveur n’accepte jamais de connexion entrante. Tor sélectionne quelques relais comme points d’introduction, téléverse un descripteur signé vers les serveurs d’annuaire, puis rencontre chaque visiteur via un relais rendezvous choisi par celui-ci. Toutes ces connexions sont sortantes depuis votre serveur. Vous n’avez aucun port à ouvrir ni aucun enregistrement DNS à publier.

Installer Tor depuis le dépôt du Tor Project

Ubuntu fournit le paquet tor dans universe, mais sa version reste proche de celle qui était disponible lorsque la version d’Ubuntu a été figée. Le dépôt officiel du Tor Project suit la dernière version stable. C’est ce qu’il vous faut pour le composant qui détermine si votre adresse reste la vôtre.

sudo apt update
sudo apt install -y apt-transport-https gnupg wget
KEYURL=https://deb.torproject.org/torproject.org/A3C4F0F979CAA22CDBA8F512EE8CBC9E886DDD89.asc
wget -qO- "$KEYURL" | gpg --dearmor | sudo tee /usr/share/keyrings/deb.torproject.org-keyring.gpg >/dev/null

L’entrée du dépôt utilise le format deb822, et Suites doit être le nom de code de votre version d’Ubuntu. Lisez-le depuis /etc/os-release au lieu de le saisir, car un nom de code incorrect donne un dépôt qui se résout correctement, mais qui ne contient aucun paquet pour votre version.

. /etc/os-release
sudo tee /etc/apt/sources.list.d/tor.sources >/dev/null <<EOF
Types: deb deb-src
URIs: https://deb.torproject.org/torproject.org/
Suites: $VERSION_CODENAME
Components: main
Signed-By: /usr/share/keyrings/deb.torproject.org-keyring.gpg
EOF
sudo apt update
sudo apt install -y tor deb.torproject.org-keyring

Le paquet deb.torproject.org-keyring maintient à jour la clé de signature. Une rotation de clé ne cassera donc pas apt update dans un an. Vérifiez que tor a démarré et a atteint le réseau :

tor --version
sudo journalctl -u tor@default -n 20

Le journal doit se terminer par Bootstrapped 100% (done): Done. Un processus tor bloqué à Bootstrapped 10% n’a aucune route sortante. Vérifiez donc le pare-feu réseau de votre fournisseur et vos propres règles de trafic sortant : sudo ufw status verbose doit afficher allow (outgoing) comme route par défaut.

Deux noms sont importants à partir de maintenant. Le paquet exécute tor avec l’utilisateur debian-tor, et l’unité en cours d’exécution est tor@default.service, car tor.service sur Debian et Ubuntu est un wrapper autour de l’instance. Demandez l’état et les journaux avec le nom de l’instance pour obtenir systématiquement le processus réel.

Configurer le service onion dans torrc

Ajoutez deux lignes à /etc/tor/torrc :

HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080

HiddenServiceDir contient les clés et l’adresse que Tor utilise pour ce service. Ne le créez pas vous-même. Tor le crée au démarrage avec le propriétaire et les permissions nécessaires. Un répertoire créé par root provoque la première erreur de la liste ci-dessous.

HiddenServicePort comporte deux parties, et les inverser est l’erreur la plus fréquente au début. Le premier nombre est le port auquel un visiteur se connecte à l’intérieur du tunnel. 80 correspond donc au comportement attendu et il n’y a aucune raison de le modifier. La seconde partie est l’adresse locale vers laquelle Tor transfère ce trafic. Un simple HiddenServicePort 80 transfère vers 127.0.0.1:80. En indiquant explicitement l’adresse et en utilisant un port élevé, le virtual host onion n’entre pas en conflit avec un service qui écoute déjà sur 80.

sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/

Le répertoire doit contenir hostname, hs_ed25519_public_key, hs_ed25519_secret_key et un répertoire authorized_clients vide.

Lire votre adresse .onion

sudo cat /var/lib/tor/onion_site/hostname

Une ligne s’affiche : 56 caractères base32 et .onion. Cette chaîne constitue l’identité complète du site. Personne ne l’attribue, ne peut la transférer ni vous la retirer tant que vous détenez le fichier de clé. Copiez-la maintenant, car toutes les configurations ci-dessous en ont besoin. Dans la suite de ce guide, elle est notée <your-address>.onion.

Servir le site avec nginx lié à 127.0.0.1

sudo apt install -y nginx
sudo install -d -m 755 /srv/onion

Écrivez /etc/nginx/sites-available/onion :

server {
    listen 127.0.0.1:8080;
    server_name <your-address>.onion;

    root /srv/onion;
    index index.html;

    server_tokens off;
    etag off;
    access_log off;
    error_log /var/log/nginx/onion.error.log error;
}
echo '<h1>hello from the onion</h1>' | sudo tee /srv/onion/index.html
sudo ln -s /etc/nginx/sites-available/onion /etc/nginx/sites-enabled/onion
sudo nginx -t
sudo systemctl reload nginx

Vérifiez maintenant deux points depuis le serveur. Le premier est que nginx répond pour le nom onion. Il s’agit exactement de l’en-tête Host que tor enverra :

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/

Le second est qu’il répond à cette adresse, et nulle part ailleurs :

sudo ss -tlnp | grep 8080

La colonne d’adresse doit afficher 127.0.0.1:8080. Si elle affiche 0.0.0.0:8080 ou *:8080, votre site onion est également accessible depuis Internet, ce qui constitue le premier élément de la liste des fuites. Une ligne listen 8080; sans adresse lie le service à toutes les interfaces. C’est le comportement par défaut.

Ouvrez l’adresse dans Tor Browser. Le premier chargement prend quelques secondes, le temps que le client récupère votre descripteur et établisse un circuit de rendezvous.

La documentation du Tor Project recommande plutôt un socket Unix qu’un port loopback : HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, nginx écoutant sur ce chemin. Un socket ne peut absolument pas être atteint depuis un autre hôte, même si la machine reçoit ultérieurement une deuxième interface. En contrepartie, il faut gérer les permissions des fichiers, car nginx crée le socket et tor s’y connecte en tant que debian-tor. Les deux utilisateurs doivent donc avoir accès au répertoire concerné. Le loopback avec une sortie ss vérifiée est plus simple à configurer correctement. C’est l’approche retenue dans la suite de ce guide.

Avec le site lié au loopback, la machine n’a besoin d’aucune règle entrante pour ce service. Gardez 22 ouvert pour votre accès et refusez le reste (les valeurs par défaut ufw à définir sur un VPS). N’oubliez pas qu’un pare-feu n’annule pas un service qui se lie à 0.0.0.0. Il filtre uniquement les paquets qui atteignent le pare-feu. Les conteneurs rendent ce point plus important, car la publication d’un port Docker écrit des règles iptables avant ufw. Ainsi, -p 8080:80 place votre backend onion sur l’adresse IP publique alors que ufw indique toujours que le port est refusé. Publiez les ports des conteneurs avec -p 127.0.0.1:8080:80.

Les fuites qui désanonymisent un site onion

Tor masque l’emplacement du serveur. Il ne masque pas ce que le serveur envoie. Chaque élément ci-dessous est publié par votre propre stack.

Le même site répond sur votre IP publique

C’est le cas qui piège le plus souvent. Les scanners indexent en continu la réponse HTTP de chaque adresse routable, et ces résultats sont publics et consultables. Servez la même page sur votre IP publique et sur votre adresse onion : les relier ne demande qu’une requête, avec le même titre, le même hash de favicon, le même ETag et le même ordre d’en-têtes. La ligne listen 127.0.0.1:8080; ci-dessus corrige le problème. Vérifiez-le depuis une autre machine, et non depuis le serveur :

curl -sv --max-time 5 http://<your-public-ip>:8080/

Connection refused ou un timeout est le résultat attendu. La présence de HTML signifie que le site est public. Si la machine héberge aussi un site clearnet, donnez à ce vhost son propre root et conservez un bloc default_server explicite sur le listener public, afin qu’un en-tête Host sans correspondance ne puisse jamais être transmis au vhost onion.

Bannières de version

curl -sI http://127.0.0.1:8080/ | grep -i '^server'

Un nginx par défaut répond Server: nginx/1.24.0. Cette chaîne de version, associée à l’ordre exact des autres en-têtes, constitue une empreinte qui permet de relier votre onion à votre hôte clearnet. server_tokens off; la réduit à Server: nginx. Cette directive ne supprime pas l’en-tête, et nginx ne possède aucune directive intégrée pour le faire. Le module headers-more est donc la solution habituelle si vous voulez le supprimer. PHP ajoute X-Powered-By jusqu’à ce que vous définissiez expose_php = Off. etag off; doit figurer dans la même liste, car nginx construit l’ETag à partir de la date de modification et de la taille du fichier. Les mêmes fichiers copiés sur deux serveurs produisent donc le même ETag sur les deux serveurs.

URL absolues pointant vers votre domaine clearnet

Une balise rel="canonical", un og:url Open Graph, un flux RSS, un sitemap, un e-mail de réinitialisation du mot de passe ou une URL de logo codée en dur. Un seul de ces éléments suffit à nommer le site clearnet dans une page servie sur l’onion. Utilisez des chemins relatifs à la racine, tels que /static/logo.svg, et faites lire à l’application son URL de base depuis l’hôte de la requête plutôt que depuis une constante. Les redirections posent le même problème ailleurs : return 301 https://example.com$request_uri; dans un bloc catch-all envoie le visiteur onion vers votre domaine réel, et l’en-tête Location lui donne directement la réponse.

Un certificat TLS partagé avec le site clearnet

Une adresse onion s’authentifie elle-même, car l’adresse est la clé publique. Ainsi, http:// sur une connexion onion est déjà chiffré de bout en bout et Tor Browser le traite comme un contexte sécurisé. Installer votre certificat existant sur le vhost onion publie le lien entre les deux, car chaque certificat approuvé publiquement est enregistré dans les journaux Certificate Transparency, qui sont publics, permanents et consultables par nom. Conservez les certificats Let's Encrypt sur le vhost clearnet et laissez le vhost onion en HTTP simple.

Polices et analytics tiers

Une police provenant d’un CDN (content delivery network) ou un script d’analytics. Le navigateur du visiteur récupère directement chacun de ces éléments. Le tiers apprend donc que quelqu’un a chargé votre page et, généralement, quelle page. Les niveaux de sécurité plus stricts de Tor Browser bloquent en outre la requête, ce qui laisse une mise en page cassée. Hébergez vous-même chaque ressource nécessaire à la page.

Incompatibilité de l’en-tête Host

Si server_name ne correspond pas à l’en-tête Host envoyé par tor, nginx utilise par défaut le serveur par défaut associé à cette adresse d’écoute. Sur une machine avec un seul vhost, le problème est invisible, car l’unique bloc server est également le serveur par défaut. Si vous ajoutez ensuite un vhost clearnet, les requêtes onion peuvent commencer à y aboutir, avec ses balises canoniques et ses redirections. Relancez le contrôle curl -H 'Host: ...' après chaque modification de nginx et recherchez votre domaine réel dans le résultat avec grep :

curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -u

Savoir quel processus possède quel socket constitue l’essentiel de ce travail (fonctionnement des ports et des sockets en écoute sous Linux).

Ce qui reste dans les journaux

Chaque requête arrive depuis 127.0.0.1. nginx ne peut donc enregistrer aucune adresse de visiteur et access_log off; ne vous coûte rien. L’application située au-dessus est un autre sujet : une commande, une adresse e-mail ou les métadonnées d’un fichier téléversé sont sous votre responsabilité. Vos propres habitudes comptent également. Administrer la machine via une connexion non durcie ne relève d’aucune protection de Tor. Considérez donc le durcissement de SSH sur le même VPS comme une partie de cette configuration.

Sauvegardez la clé privée, car elle constitue l’adresse

/var/lib/tor/onion_site/hs_ed25519_secret_key correspond au service. Il n’existe ni registrar ni mécanisme de récupération. Si vous la perdez, l’adresse disparaît. Copiez-la, et toute personne qui détient la copie peut diffuser son propre contenu à votre adresse, sans aucun moyen de révoquer quoi que ce soit.

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -czf onion-keys.tgz onion_site
sudo chmod 600 onion-keys.tgz
sudo systemctl start tor@default

Chiffrez cette archive (gpg -c onion-keys.tgz) et transférez-la hors du serveur. La restauration sur un nouveau VPS consiste à restaurer l’archive et la propriété attendue par tor :

sudo systemctl stop tor@default
sudo tar -C /var/lib/tor -xzf onion-keys.tgz
sudo chown -R debian-tor:debian-tor /var/lib/tor/onion_site
sudo chmod 700 /var/lib/tor/onion_site
sudo systemctl start tor@default
sudo cat /var/lib/tor/onion_site/hostname

La même adresse réapparaît sur le nouveau matériel une ou deux minutes après que tor a republié le descripteur. C’est toute la migration : aucun changement DNS et aucun renouvellement de certificat.

Onion-Location lorsque le site est également accessible sur le clearnet

Si l’adresse onion est un accès pratique et non un secret, publiez-la depuis le vhost clearnet :

add_header Onion-Location http://<your-address>.onion$request_uri;

Tor Browser affiche alors un bouton .onion available dans la barre d’adresse et propose de basculer vers cette adresse. L’en-tête n’est pris en compte que si la page clearnet est servie en HTTPS et si la valeur est une URL onion valide.

Une règle nginx est importante ici. Les directives add_header sont héritées par un bloc location uniquement si celui-ci ne définit aucune directive de ce type. Un bloc location qui possède son propre add_header supprime donc silencieusement Onion-Location. Répétez la directive dans ce bloc ou regroupez tous les en-têtes de réponse au même endroit. La publication volontaire de cet en-tête relie les deux sites. C’est correct pour un miroir, mais pas pour un service qui doit rester non lié.

Adresses personnalisées

mkp224o génère des paires de clés jusqu’à ce que l’une d’elles produise une adresse commençant par le préfixe demandé. Il s’agit d’une recherche par force brute. Il n’y a donc rien à configurer en dehors du préfixe et du délai que vous êtes prêt à attendre.

sudo apt install -y git gcc libc6-dev libsodium-dev make autoconf
git clone https://github.com/cathugger/mkp224o
cd mkp224o
./autogen.sh
./configure --enable-amd64-51-30k
make
./mkp224o -d onionkeys blog

Chaque résultat est placé dans onionkeys/<address>.onion/, qui contient hostname et hs_ed25519_secret_key. Installez-en un en arrêtant Tor, en copiant ce répertoire dans votre HiddenServiceDir, puis en appliquant les mêmes chown et chmod 700 que lors de la restauration ci-dessus.

La longueur du préfixe constitue l’intégralité du coût. L’adresse est encodée en base32. Chaque caractère supplémentaire demandé multiplie donc par 32 le nombre attendu de clés à tester. Un préfixe court peut être trouvé sur un ordinateur portable. Un préfixe long ne sera trouvé sur aucun équipement que vous possédez. Un préfixe personnalisé incite également les lecteurs à reconnaître les premiers caractères plutôt que l’adresse entière. C’est précisément l’habitude exploitée par les copies de sites onion utilisées pour l’hameçonnage.

Modes d’échec et messages affichés

Aucun fichier hostname après le redémarrage. Tor n’a pas démarré ou a refusé le répertoire. sudo journalctl -u tor@default -n 50 l’indique :

/var/lib/tor/onion_site/ is not owned by this user (debian-tor, 108) but by root (0). Perhaps you are running Tor as the wrong user?

C’est l’apparence d’un répertoire créé manuellement. Corrigez le propriétaire et le mode, ou supprimez le répertoire et laissez tor le créer.

Tor Browser affiche Onionsite Not Found (0xF0). Le client n’a pas pu récupérer de descripteur. Pour le réseau, rien n’est donc publié à cette adresse. Vérifiez que tor fonctionne et a terminé son bootstrap, comparez l’adresse saisie avec sudo cat /var/lib/tor/onion_site/hostname caractère par caractère, puis vérifiez l’horloge. Tor a besoin d’une heure exacte pour publier et valider les descripteurs, et timedatectl doit renvoyer System clock synchronized: yes.

L’adresse est résolue, mais la page ne se charge jamais. Tor a terminé le rendezvous, puis a échoué sur le dernier saut, entre tor et nginx. Ce saut est local, donc le journal de tor reste silencieux. Exécutez curl -sI http://127.0.0.1:8080/ sur le serveur. Connection refused signifie que nginx est arrêté ou qu’il écoute sur une adresse différente de celle indiquée par HiddenServicePort.

La page se charge et tous les liens renvoient vers votre domaine réel. Les modèles contiennent des URL absolues. Exécutez le contrôle grep -o 'https\?://[^"]*' indiqué plus haut et corrigez ce qu’il affiche avant de communiquer l’adresse.

Tout fonctionne, puis s’arrête après un redémarrage. Redémarrez volontairement la machine une fois avant de vous fier au site, puis exécutez sudo systemctl status tor@default et sudo systemctl status nginx. Un service démarré manuellement ressemble exactement à un service activé jusqu’au redémarrage de la machine.

FAQ

Dois-je ouvrir un port dans mon pare-feu pour un service onion Tor ?

Non. Le daemon tor établit uniquement des connexions sortantes vers les serveurs d’annuaire, ses points d’introduction et chaque relais de rendez-vous. Aucune règle entrante n’est donc nécessaire, et le serveur web lui-même écoute sur 127.0.0.1. Laissez ufw en refus par défaut pour le trafic entrant et autorisez SSH. Cette caractéristique permet aussi à un service onion de fonctionner sur une machine derrière un NAT (network address translation), sans aucune adresse IP publique.

Pourquoi ne puis-je pas accéder à mon adresse .onion dans Tor Browser ?

Partez du serveur et vérifiez chaque élément. sudo journalctl -u tor@default -n 50 doit afficher Bootstrapped 100% (done): Done. Ensuite, curl -sI http://127.0.0.1:8080/ exécuté sur le serveur doit renvoyer une ligne d’état. Comparez ensuite l’adresse saisie avec le fichier hostname, car un seul caractère incorrect désigne simplement un autre service. Onionsite Not Found (0xF0) signifie qu’aucun descripteur n’a été trouvé pour cette adresse. Cela indique généralement que tor ne fonctionne pas ou que l’horloge système est incorrecte.

Puis-je déplacer mon site onion vers un nouveau serveur et conserver la même adresse ?

Oui. L’adresse est dérivée de hs_ed25519_secret_key. Copiez donc l’intégralité de HiddenServiceDir sur le nouveau serveur, attribuez-lui la propriété et le mode 700 avec debian-tor, puis démarrez tor. L’adresse redevient active lorsque le descripteur est publié à nouveau. Aucun enregistrement DNS ne doit être modifié. Si vous perdez ce fichier, l’adresse est irrécupérable. Sauvegardez-le donc sous forme chiffrée, hors du serveur, le jour même de sa création.

Un site onion a-t-il besoin d’un certificat HTTPS ?

Non. L’adresse de 56 caractères est la clé publique du service. La connexion est donc déjà chiffrée et authentifiée de bout en bout, et Tor Browser considère http:// sur un nom .onion comme un contexte sécurisé. Réutiliser votre certificat clearnet sur le vhost onion est pire que ne rien faire, car les journaux Certificate Transparency sont publics et enregistrent définitivement les noms qui partagent un certificat. La seule raison d’acheter un certificat pour un nom .onion est de renforcer la confiance dans la marque avec une autorité de certification qui en délivre. Ce lien est public par conception.