Come ospitare un sito .onion sul tuo VPS
Configura un servizio onion v3 su Ubuntu con Tor e nginx su loopback. Scopri come chiudere i leak che possono collegare l'indirizzo al tuo IP pubblico.
Cosa stai costruendo
Un sito onion è un normale server web che risponde soltanto attraverso la rete Tor. Installa Tor, aggiungi due righe a /etc/tor/torrc, leggi l'indirizzo che Tor scrive per te, quindi configura nginx in ascolto su 127.0.0.1, in modo che nessun servizio risponda sull'IP pubblico. L'installazione richiede dieci minuti. Il resto di questa guida riguarda l'elenco dei possibili leak, perché un sito onion di solito non funziona correttamente quando la sua configurazione rimanda direttamente all'operatore.
Tor è nato come "the onion router" e un onion service è un servizio raggiungibile soltanto attraverso Tor. Un indirizzo di versione 3 contiene 56 caratteri seguiti da .onion. Questi caratteri sono la chiave pubblica ed25519 del servizio, un checksum e un byte di versione, codificati in base32. Gli indirizzi di versione 2, composti da 16 caratteri, sono stati rimossi dalla rete nel 2021. Di conseguenza, tutto ciò che generi oggi è v3. L'indirizzo è la chiave, con due conseguenze. La connessione è cifrata e autenticata end-to-end senza coinvolgere un'autorità di certificazione. Se perdi il file della chiave, perdi definitivamente anche l'indirizzo.
Il server non accetta mai connessioni in ingresso. Tor sceglie alcuni relay come punti di introduzione, carica un descrittore firmato sui directory server e stabilisce la connessione con ogni visitatore tramite un relay rendezvous scelto dal visitatore. Tutte queste connessioni sono in uscita dal server. Non devi aprire alcuna porta né pubblicare alcun record DNS.
Installare Tor dal repository del Tor Project
Ubuntu include un pacchetto tor in universe, ma resta vicino alla versione disponibile quando il rilascio è stato congelato. Il repository ufficiale del Tor Project segue la versione stabile corrente. È la scelta corretta per il componente che determina se il tuo indirizzo rimane sotto il tuo controllo.
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/nullLa voce del repository usa il formato deb822 e Suites deve essere il codename di Ubuntu. Leggilo da /etc/os-release invece di inserirlo manualmente. Un codename errato produce un repository che viene risolto correttamente, ma che non contiene pacchetti per il tuo rilascio.
. /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-keyringIl pacchetto deb.torproject.org-keyring mantiene aggiornata la chiave di firma, quindi una rotazione della chiave non interromperà apt update tra un anno. Verifica che tor sia stato avviato e abbia raggiunto la rete:
tor --version
sudo journalctl -u tor@default -n 20Il journal dovrebbe terminare con Bootstrapped 100% (done): Done. Un processo tor fermo su Bootstrapped 10% non dispone di un percorso in uscita. Controlla quindi il firewall di rete del provider e le tue regole di egress: sudo ufw status verbose dovrebbe mostrare allow (outgoing) come impostazione predefinita.
Da questo punto in poi sono importanti due nomi. Il pacchetto esegue tor con l'utente debian-tor e l'unità in esecuzione è tor@default.service, perché tor.service su Debian e Ubuntu è un wrapper per l'istanza. Richiedi stato e log usando il nome dell'istanza: otterrai sempre il processo effettivo.
Configura il servizio onion in torrc
Aggiungi due righe a /etc/tor/torrc:
HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080HiddenServiceDir è la directory in cui tor conserva le chiavi e l'indirizzo di questo servizio. Non crearla manualmente. Tor la crea all'avvio con il proprietario e i permessi necessari. Una directory creata come root causa il primo errore dell'elenco seguente.
HiddenServicePort è composto da due parti, che spesso vengono confuse al primo tentativo. Il primo numero è la porta a cui un visitatore si connette all'interno del tunnel. 80 è quindi il valore previsto e non c'è motivo di modificarlo. La seconda parte è l'indirizzo locale a cui tor inoltra il traffico. Un semplice HiddenServicePort 80 inoltra a 127.0.0.1:80. Specificare l'indirizzo completo e usare una porta alta mantiene il virtual host onion separato da qualsiasi servizio già in ascolto sulla porta 80.
sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/L'elenco dovrebbe contenere hostname, hs_ed25519_public_key, hs_ed25519_secret_key e una directory authorized_clients vuota.
Leggere l'indirizzo .onion
sudo cat /var/lib/tor/onion_site/hostnameViene restituita una riga: 56 caratteri base32 e .onion. Questa stringa rappresenta l'identità completa del sito. Nessuno la assegna, nessuno può trasferirla e nessuno può sottrarla finché si conserva il file della chiave crittografica. Copiatela ora, perché tutte le configurazioni seguenti ne hanno bisogno. Nel resto della guida viene indicata come <your-address>.onion.
Servire il sito da nginx in ascolto su 127.0.0.1
sudo apt install -y nginx
sudo install -d -m 755 /srv/onionScrivere /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 nginxOra verificare due aspetti dal server. Il primo è che nginx risponda per il nome onion, che corrisponde esattamente all'header Host che tor invierà:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/Il secondo è che risponda su quell'indirizzo e su nessun altro:
sudo ss -tlnp | grep 8080La colonna dell'indirizzo deve riportare 127.0.0.1:8080. Se riporta 0.0.0.0:8080 o *:8080, il sito onion è esposto anche su Internet pubblico: questo è il primo elemento dell'elenco delle perdite. Una riga listen 8080; senza indirizzo esegue il bind su tutte le interfacce, comportamento predefinito.
Aprire l'indirizzo in Tor Browser. Il primo caricamento richiede alcuni secondi, mentre il client recupera il descriptor e crea un circuito rendezvous.
La documentazione ufficiale di Tor Project preferisce un socket Unix a una porta di loopback: HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, con nginx in ascolto su quel percorso. Un socket non è raggiungibile da altri host, anche se in seguito il server dispone di una seconda interfaccia. Lo svantaggio riguarda i permessi sui file: nginx crea il socket e tor vi si connette come debian-tor, quindi i due utenti devono avere accesso alla directory. Il loopback con un output ss verificato è più semplice da configurare correttamente ed è l'approccio adottato nel resto di questa guida.
Con il sito sul loopback, il server non richiede alcuna regola in ingresso per questo servizio. Lasciare aperta la porta 22 per l'accesso amministrativo e negare tutte le altre (le impostazioni predefinite di ufw da applicare su un VPS). Ricordare che un firewall non annulla un servizio che esegue il bind su 0.0.0.0: filtra soltanto i pacchetti che raggiungono il firewall. I container rendono il problema più evidente, perché la pubblicazione di una porta Docker scrive regole iptables prima di ufw; di conseguenza, -p 8080:80 espone il backend onion sull'IP pubblico anche se ufw continua a segnalare la porta come negata. Pubblicare le porte dei container come -p 127.0.0.1:8080:80.
Le perdite che deanonimizzano un sito onion
Tor nasconde la posizione del server. Tor non nasconde ciò che il server comunica. Ogni elemento riportato di seguito viene pubblicato dal tuo stack.
Lo stesso sito che risponde sull'IP pubblico
È il caso che causa più problemi. Gli scanner indicizzano continuamente la risposta HTTP di ogni indirizzo instradabile, e questi risultati sono pubblici e ricercabili. Se servi la stessa pagina sull'IP pubblico e sull'indirizzo onion, collegarli richiede una sola query: stesso titolo, stesso hash della favicon, stesso ETag, stesso ordine degli header. La riga listen 127.0.0.1:8080; precedente risolve il problema. Verificalo da un'altra macchina, non dal server:
curl -sv --max-time 5 http://<your-public-ip>:8080/Connection refused o un timeout sono il risultato corretto. Qualsiasi HTML indica che il sito è pubblico. Se il server esegue anche un sito clearnet, assegna a quel vhost una propria root e mantieni un blocco default_server esplicito sul listener pubblico, in modo che un header Host non corrispondente non possa mai ricadere sul vhost onion.
Banner delle versioni
curl -sI http://127.0.0.1:8080/ | grep -i '^server'Un nginx predefinito risponde con Server: nginx/1.24.0. Questa stringa della versione, insieme all'ordine esatto degli altri header, crea un'impronta che collega il sito onion al tuo host clearnet. server_tokens off; la riduce a Server: nginx. Non rimuove l'header e nginx non dispone di una direttiva integrata per farlo, quindi il modulo headers-more è la soluzione abituale se vuoi eliminarlo. PHP aggiunge X-Powered-By finché non imposti expose_php = Off. Anche etag off; va incluso nello stesso elenco, perché nginx costruisce l'ETag usando l'ora di modifica e la dimensione del file; di conseguenza, gli stessi file copiati su due server producono lo stesso ETag su entrambi.
URL assoluti che puntano al dominio clearnet
Un tag rel="canonical", un og:url Open Graph, un feed RSS, una sitemap, un'email per la reimpostazione della password o un URL hardcoded per un logo. Ognuno di questi elementi inserisce il sito clearnet in una pagina servita tramite onion. Usa percorsi relativi alla root, come /static/logo.svg, e fai leggere all'applicazione l'URL di base dall'host della richiesta invece di usare una costante. I redirect costituiscono lo stesso errore in un altro punto: return 301 https://example.com$request_uri; in un blocco catch-all invia il visitatore onion al tuo dominio reale, mentre l'header Location gli fornisce direttamente la risposta.
Un certificato TLS condiviso con il sito clearnet
Un indirizzo onion si autentica da solo, perché l'indirizzo è la chiave pubblica; pertanto, http:// su una connessione onion è già crittografato end-to-end e Tor Browser lo considera un contesto sicuro. Installare il certificato esistente sul vhost onion pubblica il collegamento tra i due siti, perché ogni certificato considerato attendibile pubblicamente viene registrato nei log di Certificate Transparency, che sono pubblici, permanenti e ricercabili per nome. Mantieni i certificati Let's Encrypt sul vhost clearnet e lascia il vhost onion su HTTP semplice.
Font e strumenti di analisi di terze parti
Un font proveniente da una CDN (content delivery network) o uno script di analisi. Il browser del visitatore recupera direttamente ciascun elemento, quindi il fornitore terzo viene a sapere che qualcuno ha caricato la tua pagina e, di norma, quale pagina. Inoltre, i livelli di sicurezza più restrittivi di Tor Browser bloccano comunque la richiesta, lasciando un layout danneggiato. Ospita localmente ogni asset necessario alla pagina.
Incongruenza nell'header Host
Se server_name non corrisponde all'header Host inviato da tor, nginx ricade sul server predefinito per quell'indirizzo di ascolto. Su un server con un solo vhost questo non è visibile, perché l'unico blocco server è anche quello predefinito. Se in seguito aggiungi un vhost clearnet, le richieste onion possono iniziare a finire su quello, inclusi i relativi tag canonici e redirect. Esegui nuovamente il controllo curl -H 'Host: ...' dopo ogni modifica a nginx e cerca nel risultato il tuo dominio reale:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -uSapere quale processo possiede ogni socket costituisce gran parte di questo lavoro (come funzionano le porte e i socket in ascolto su Linux).
Cosa rimane nei log
Ogni richiesta arriva da 127.0.0.1, quindi nginx non ha alcun indirizzo del visitatore da registrare e access_log off; non ti costa nulla. L'applicazione che si trova sopra nginx è una questione diversa, perché devi gestire tu un ordine, un indirizzo email o i metadati di un file caricato. Anche le tue abitudini contano: amministrare il server tramite un accesso non protetto adeguatamente è al di fuori di ciò che Tor protegge. Considera quindi l'hardening di SSH sullo stesso VPS parte integrante di questa configurazione.
Esegui il backup della chiave privata, perché è l'indirizzo
/var/lib/tor/onion_site/hs_ed25519_secret_key è il servizio. Non esistono registrar né procedure di recupero. Se la perdi, l'indirizzo non è più disponibile. Copiala: chiunque disponga della copia può pubblicare contenuti propri al tuo indirizzo e tu non puoi revocare nulla.
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@defaultCifra l'archivio (gpg -c onion-keys.tgz) e trasferiscilo fuori dal server. Il ripristino su un nuovo VPS consiste nel ripristinare l'archivio e la proprietà prevista da 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/hostnameLo stesso indirizzo torna disponibile sul nuovo hardware uno o due minuti dopo che tor ripubblica il descriptor. Questa è l'intera migrazione: nessuna modifica DNS e nessuna nuova emissione del certificato.
Onion-Location, quando il sito è disponibile anche sulla clearnet
Se l'indirizzo onion è una comodità e non un requisito di riservatezza, pubblicizzalo dal virtual host clearnet:
add_header Onion-Location http://<your-address>.onion$request_uri;Tor Browser mostra quindi un pulsante .onion available nella barra degli indirizzi e propone il passaggio. L'header viene considerato solo quando la pagina clearnet è servita tramite HTTPS e il valore è un URL onion valido.
Qui si applica una regola di nginx. Le direttive add_header vengono ereditate da un blocco location solo quando quel blocco non ne dichiara di proprie. Di conseguenza, un location con un proprio add_header elimina silenziosamente Onion-Location. Ripeti la direttiva in quel blocco oppure mantieni tutti gli header di risposta in un unico punto. La pubblicazione deliberata di questo header collega i due siti. È corretta per un mirror, ma non per un sito che deve rimanere non collegato.
Indirizzi personalizzati
mkp224o genera coppie di chiavi finché una di esse produce un indirizzo che inizia con il prefisso richiesto. È una ricerca a forza bruta, quindi non occorre configurare altro oltre al prefisso e al tempo che si è disposti ad attendere.
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 blogOgni risultato viene salvato in onionkeys/<address>.onion/, che contiene hostname e hs_ed25519_secret_key. Per installarlo, arrestare tor, copiare quella directory nel percorso HiddenServiceDir, quindi applicare gli stessi chown e chmod 700 usati per il ripristino precedente.
La lunghezza del prefisso determina interamente il costo. L'indirizzo usa base32, quindi ogni carattere aggiuntivo moltiplica per 32 il numero atteso di chiavi. Un prefisso breve può essere generato su un laptop. Uno lungo non verrà generato in tempi utili su nessun sistema di propria disponibilità. Inoltre, un prefisso personalizzato abitua i lettori a riconoscere soltanto i primi caratteri invece dell'intero indirizzo. È proprio questa l'abitudine sfruttata dalle copie contraffatte dei siti onion per il phishing.
Modalità di errore e stringhe visualizzate
Dopo il riavvio non esiste alcun file hostname. Tor non è stato avviato oppure ha rifiutato la directory. sudo journalctl -u tor@default -n 50 ne indica il percorso:
/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?Questo è l’aspetto di una directory creata manualmente. Correggi il proprietario e i permessi, oppure elimina la directory e lascia che sia tor a crearla.
Tor Browser mostra Onionsite Not Found (0xF0). Il client non ha potuto recuperare un descriptor, quindi per la rete quell’indirizzo non pubblica alcun servizio. Verifica che tor sia in esecuzione e abbia completato il bootstrap, confronta carattere per carattere l’indirizzo inserito con sudo cat /var/lib/tor/onion_site/hostname, quindi controlla l’orologio. Tor richiede un’ora precisa per pubblicare e convalidare i descriptor e timedatectl dovrebbe restituire System clock synchronized: yes.
L’indirizzo viene risolto, ma la pagina non viene mai caricata. Tor ha completato il rendezvous, quindi l’ultimo hop, da tor a nginx, non è riuscito. Questo hop è locale, quindi il log di tor rimane vuoto. Esegui curl -sI http://127.0.0.1:8080/ sul server. Connection refused indica che nginx è arrestato oppure è in ascolto su un indirizzo diverso da quello indicato da HiddenServicePort.
La pagina viene caricata, ma ogni link rimanda al dominio reale. Il problema sono gli URL assoluti nei template. Esegui il controllo grep -o 'https\?://[^"]*' indicato sopra e correggi il risultato prima di condividere l’indirizzo.
Funziona, poi smette di funzionare dopo un riavvio. Riavvia intenzionalmente il server una volta prima di affidarti al sito, quindi esegui sudo systemctl status tor@default e sudo systemctl status nginx. Un servizio avviato manualmente appare identico a uno abilitato finché la macchina non viene riavviata.
FAQ
Devo aprire una porta nel firewall per un servizio onion Tor?
No. Il demone tor stabilisce soltanto connessioni in uscita verso i server directory, i punti di introduzione e ciascun relay di rendezvous. Non è quindi necessaria alcuna regola in ingresso e il server Web stesso resta in ascolto su 127.0.0.1. Mantieni ufw con il criterio predefinito di rifiuto del traffico in ingresso, consentendo SSH. La stessa caratteristica permette a un servizio onion di funzionare su una macchina dietro NAT (network address translation), anche senza alcun IP pubblico.
Perché non riesco a raggiungere il mio indirizzo .onion in Tor Browser?
Procedi dall'esterno verso il server. sudo journalctl -u tor@default -n 50 dovrebbe mostrare Bootstrapped 100% (done): Done. Sul server, curl -sI http://127.0.0.1:8080/ dovrebbe quindi restituire una riga di stato. Confronta poi l'indirizzo immesso con il file hostname, perché un solo carattere errato identifica semplicemente un servizio diverso. Onionsite Not Found (0xF0) indica che non è stato trovato alcun descriptor per l'indirizzo. Di solito significa che tor non è in esecuzione oppure che l'orologio di sistema non è corretto.
Posso spostare il mio sito onion su un nuovo server mantenendo lo stesso indirizzo?
Sì. L'indirizzo deriva da hs_ed25519_secret_key. Copia quindi l'intero HiddenServiceDir sul nuovo server, imposta come proprietario debian-tor e la modalità 700, quindi avvia tor. L'indirizzo torna attivo quando il descriptor viene ripubblicato e non è necessario aggiornare alcun record DNS. Se perdi quel file, l'indirizzo non può essere recuperato. Esegui quindi un backup crittografato fuori dal server il giorno stesso in cui lo crei.
Un sito onion richiede un certificato HTTPS?
No. L'indirizzo di 56 caratteri è la chiave pubblica del servizio. La connessione è quindi già crittografata e autenticata end-to-end, e Tor Browser considera http:// su un nome .onion un contesto sicuro. Riutilizzare il certificato clearnet sull'host virtuale onion è peggio che non usare alcun certificato, perché i log Certificate Transparency sono pubblici e registrano in modo permanente quali nomi condividono un certificato. L'unico motivo per acquistare un certificato per un nome .onion è ottenere una garanzia sul brand da una CA che li rilascia. Tale associazione è pubblica per sua progettazione.