Как создать onion-сайт на VPS с Ubuntu
Пошаговая настройка v3 onion-сервиса через Tor и Nginx на Ubuntu. Узнайте, как привязать сервер к loopback и устранить утечки, которые могут раскрыть ваш реальный IP-адрес.
Что вы создаете
Onion-сайт — это обычный веб-сервер, который отвечает только через сеть Tor. Установите tor, добавьте две строки в /etc/tor/torrc, узнайте адрес, который сгенерировал tor, а затем привяжите nginx к 127.0.0.1, чтобы сервер не отвечал на публичном IP-адресе. Установка занимает десять минут. Остальная часть этого руководства посвящена списку утечек, так как onion-сайты чаще всего терпят неудачу из-за того, что их собственная конфигурация прямо указывает на оператора.
Tor начинался как "the onion router" (луковый маршрутизатор), и onion-сервис — это сервис, доступный только через него. Адрес версии 3 состоит из 56 символов, за которыми следует .onion; эти символы представляют собой ed25519 публичный ключ сервиса, контрольную сумму и байт версии, закодированные в base32. Адреса версии 2 (16 символов) были удалены из сети в 2021 году, поэтому всё, что вы создаете сегодня, является v3. Адрес и есть ключ, что влечет за собой два последствия. Соединение шифруется и аутентифицируется от начала до конца без участия центра сертификации, а потеря файла ключа означает безвозвратную потерю адреса.
Ваш сервер никогда не принимает входящие соединения. Tor выбирает несколько ретрансляторов в качестве точек входа (introduction points), загружает подписанный дескриптор на серверы каталогов и встречается с каждым посетителем на ретрансляторе встречи (rendezvous relay), который выбрал сам посетитель. Каждое из этих соединений является исходящим с вашего сервера. Нет портов, которые нужно открывать, и нет DNS-записей, которые нужно публиковать. Если вы хотите, чтобы тот же VPS приносил пользу сети, это другая задача с противоположной структурой, так как obfs4 bridge принимает входящие соединения от клиентов из цензурируемых сетей и требует открытого порта.
Установка Tor из репозитория Tor Project
Ubuntu выпускает пакет tor в репозитории universe, но обычно сохраняет версию, актуальную на момент заморозки выпуска. Собственный репозиторий Tor Project отслеживает текущий стабильный выпуск. Именно он нужен для компонента, который определяет, останется ли ваш адрес вашим. Наличие пакета в дистрибутиве ничего не говорит о том, продолжается ли его разработка. Это стоит проверять для любого сервиса, который вы размещаете самостоятельно, поскольку в Searx не было новых коммитов с 2023 года, а SearXNG продолжает развивать этот проект.
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Запись в репозитории использует формат deb822, а Suites должно соответствовать кодовому имени вашего дистрибутива Ubuntu. Считайте его из файла /etc/os-release, вместо того чтобы вводить вручную: неверное кодовое имя приведет к тому, что репозиторий будет успешно разрешаться, но не предоставит пакетов для вашего релиза.
. /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Пакет deb.torproject.org-keyring поддерживает актуальность ключа подписи, поэтому ротация ключей не нарушит работу apt update в будущем. Проверьте, что tor запустился и установил соединение с сетью:
tor --version
sudo journalctl -u tor@default -n 20Журнал должен заканчиваться сообщением Bootstrapped 100% (done): Done. Если tor завис на этапе Bootstrapped 10%, значит, отсутствует исходящий маршрут. Проверьте сетевой брандмауэр провайдера и собственные правила исходящего трафика: sudo ufw status verbose должен показывать allow (outgoing) в качестве шлюза по умолчанию.
Далее важны два имени. Пакет запускает tor от имени пользователя debian-tor, а работающий юнит называется tor@default.service, так как tor.service в Debian и Ubuntu является оберткой над экземпляром службы. Запрашивайте статус и логи по имени экземпляра, чтобы всегда получать данные о реальном процессе.
Настройка onion-сервиса в torrc
Добавьте две строки в /etc/tor/torrc:
HiddenServiceDir /var/lib/tor/onion_site/
HiddenServicePort 80 127.0.0.1:8080В HiddenServiceDir Tor хранит ключи и адрес для этого сервиса. Не создавайте этот каталог самостоятельно. Tor создаст его при запуске с необходимыми правами доступа и владельцем. Если вы создадите каталог от имени root, это станет первой причиной сбоя из списка ниже.
HiddenServicePort состоит из двух частей, и их путаница — самая частая ошибка. Первое число — это порт, к которому подключается посетитель внутри туннеля, поэтому 80 — это стандарт, и нет причин его менять. Вторая часть — это локальный адрес, на который Tor перенаправляет трафик. Пустой HiddenServicePort 80 перенаправляет на 127.0.0.1:80, поэтому лучше явно указать адрес и использовать высокоуровневый порт, чтобы избежать конфликтов с сервисами, уже слушающими 80 порт.
sudo systemctl restart tor@default
sudo ls -l /var/lib/tor/onion_site/В каталоге должны находиться hostname, hs_ed25519_public_key, hs_ed25519_secret_key и пустой каталог authorized_clients.
Получение вашего .onion адреса
sudo cat /var/lib/tor/onion_site/hostnameВы получите одну строку: 56 символов в кодировке base32 и .onion. Эта строка является полным идентификатором сайта. Никто не назначает его, никто не может его передать, и никто не может его отобрать, пока у вас есть файл ключа. Скопируйте его сейчас, так как он потребуется для всех настроек ниже. В остальной части этого руководства он обозначается как <your-address>.onion.
Обслуживание сайта через nginx, привязанный к 127.0.0.1
sudo apt install -y nginx
sudo install -d -m 755 /srv/onionЗапишите /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Теперь проверьте два условия непосредственно с сервера. Первое: nginx отвечает на onion-имя, которое в точности соответствует заголовку Host, отправляемому Tor:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/Второе: он отвечает только там и больше нигде:
sudo ss -tlnp | grep 8080В столбце адреса должно быть указано 127.0.0.1:8080. Если там отображается 0.0.0.0:8080 или *:8080, ваш onion-сайт также доступен из публичного интернета, что является первым пунктом в списке утечек. Строка listen 8080; без указания адреса означает привязку ко всем интерфейсам, что является настройкой по умолчанию.
Откройте адрес в Tor Browser. Первая загрузка занимает несколько секунд, пока клиент получает ваш дескриптор и строит цепочку соединения (rendezvous circuit).
Собственная документация The Tor Project рекомендует использовать unix-сокет вместо loopback-порта: HiddenServicePort 80 unix:/var/run/tor/onion_site.sock, при этом nginx должен прослушивать этот путь. К сокету невозможно обратиться с другого хоста, даже если позже на сервере появится второй сетевой интерфейс. Сложность заключается в правах доступа к файлам, так как nginx создает сокет, а tor подключается к нему от имени debian-tor, поэтому оба пользователя должны иметь доступ к директории. Использование loopback с проверкой вывода ss проще в настройке, и именно этот способ подразумевается в остальной части руководства.
При работе сайта через loopback серверу не требуются входящие правила для этого порта. Оставьте открытым только порт 22 для себя и запретите всё остальное (стандартные настройки ufw, которые стоит применить на VPS). Помните, что межсетевой экран не отменяет работу сервиса, который привязан к 0.0.0.0, он лишь фильтрует пакеты, доходящие до него. Контейнеры делают эту ситуацию более критичной, так как публикация порта Docker создает правила iptables в обход ufw, поэтому -p 8080:80 делает ваш onion-бэкенд доступным по публичному IP, даже если ufw сообщает, что порт закрыт. Публикуйте порты контейнеров как -p 127.0.0.1:8080:80.
Утечки, деанонимизирующие onion-сайт
Tor скрывает местоположение сервера. Tor не скрывает то, что сервер передает. Каждый пункт ниже — это данные, которые публикует ваш стек. Анализ любого инструмента обеспечения приватности с точки зрения его реальных границ, а не репутации — это тот же подход, который позволяет понять что именно скрывает self-hosted SearXNG: только ваш IP от поисковых систем, и ничего более.
Тот же сайт, отвечающий на публичном IP
Это главная ошибка. Сканеры непрерывно индексируют HTTP-ответы всех доступных адресов, и эти результаты публичны и доступны для поиска. Если вы отдаете одну и ту же страницу на публичном IP и на onion-адресе, их связка находится одним запросом: одинаковый заголовок, хеш favicon, ETag, порядок заголовков. Строка listen 127.0.0.1:8080; выше — это решение. Проверьте это с другой машины, а не с самого сервера:
curl -sv --max-time 5 http://<your-public-ip>:8080/Connection refused или таймаут — правильный результат. Любой HTML означает, что сайт публичен. Если на этом же сервере работает сайт в обычном интернете (clearnet), выделите для него отдельный root и добавьте явный блок default_server в публичный listener, чтобы несовпадающий заголовок Host никогда не перенаправлялся на onion-vhost.
Баннеры версий
curl -sI http://127.0.0.1:8080/ | grep -i '^server'Стандартный nginx отвечает Server: nginx/1.24.0. Эта строка версии вместе с точным порядком других заголовков является отпечатком, который связывает ваш onion-сайт с обычным хостом. server_tokens off; сокращает его до Server: nginx. Это не удаляет заголовок, а в nginx нет встроенной директивы для полного удаления, поэтому модуль headers-more — стандартное решение, если вы хотите его убрать. PHP добавляет X-Powered-By, пока вы не установите expose_php = Off. etag off; относится к тому же списку, так как nginx формирует ETag на основе времени изменения и размера файла, поэтому одни и те же файлы, скопированные на два сервера, будут иметь одинаковый ETag на обоих.
Абсолютные URL, указывающие на ваш clearnet-домен
Тег rel="canonical", Open Graph og:url, RSS-лента, карта сайта, письмо для сброса пароля, жестко прописанный URL логотипа. Любой из них называет clearnet-сайт внутри страницы, отдаваемой через onion. Используйте относительные пути, такие как /static/logo.svg, и позвольте приложению считывать базовый URL из хоста запроса, а не из константы. Редиректы — это та же ошибка в другом месте: return 301 https://example.com$request_uri; в блоке catch-all отправляет посетителя onion-сайта на ваш реальный домен, а заголовок Location сразу выдает им ответ.
TLS-сертификат, общий с clearnet-сайтом
Onion-адрес аутентифицирует себя сам, так как адрес является публичным ключом, поэтому http:// через onion-соединение уже зашифровано из конца в конец, и Tor Browser считает его безопасным контекстом. Установка вашего существующего сертификата на onion-vhost публикует связь между ними, так как каждый доверенный сертификат записывается в логи Certificate Transparency, а эти логи публичны, постоянны и доступны для поиска по имени. Оставьте сертификаты Let's Encrypt на clearnet-vhost, а onion-vhost оставьте на обычном HTTP.
Сторонние шрифты и аналитика
Шрифт с CDN или скрипт аналитики. Браузер посетителя загружает каждый из них напрямую, поэтому третья сторона узнает, что кто-то загрузил вашу страницу, и обычно — какую именно. К тому же, строгие уровни безопасности Tor Browser в любом случае блокируют такие запросы, что приводит к поломке верстки. Размещайте все необходимые ресурсы локально.
Несовпадение заголовка Host
Если server_name не совпадает с заголовком Host, который отправляет tor, nginx переходит к серверу по умолчанию для этого адреса прослушивания. На сервере с одним vhost это незаметно, так как единственный блок server является и сервером по умолчанию. Добавьте clearnet-vhost позже, и onion-запросы могут начать попадать на него, вместе с его каноническими тегами и редиректами. Повторно запускайте проверку curl -H 'Host: ...' после каждого изменения nginx и используйте grep для поиска вашего реального домена:
curl -s -H 'Host: <your-address>.onion' http://127.0.0.1:8080/ | grep -o 'https\?://[^"]*' | sort -uПонимание того, какой процесс владеет каким сокетом — большая часть этой работы (как работают порты и слушающие сокеты в Linux).
Что остается в логах
Каждый запрос приходит с 127.0.0.1, поэтому nginx нечего записывать в качестве адреса посетителя, и access_log off; ничего вам не стоит. Приложение, работающее поверх него — другой вопрос, так как заказ, адрес электронной почты или метаданные загруженного файла — это то, с чем работаете вы. Ваши собственные привычки также имеют значение: администрирование сервера через незащищенный логин находится вне того, что защищает Tor, поэтому рассматривайте укрепление SSH на том же VPS как часть этой сборки.
Создайте резервную копию закрытого ключа, так как он определяет адрес
/var/lib/tor/onion_site/hs_ed25519_secret_key является сервисом. Здесь нет регистратора и механизмов восстановления. Если вы потеряете ключ, адрес будет утрачен навсегда. Скопируйте его, и любой, у кого окажется копия, сможет размещать свой контент по вашему адресу; отозвать доступ будет невозможно.
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Зашифруйте этот архив (gpg -c onion-keys.tgz) и переместите его с сервера. Для восстановления на новом VPS потребуется этот архив и права доступа, которые ожидает 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Тот же адрес появится на новом оборудовании через минуту или две после того, как tor повторно опубликует дескриптор. Это вся процедура миграции: не требуется ни изменение DNS, ни перевыпуск сертификатов.
Onion-Location, если сайт также доступен в обычном интернете
Если onion-адрес предназначен для удобства, а не для скрытности, его следует анонсировать в vhost обычного сайта:
add_header Onion-Location http://<your-address>.onion$request_uri;После этого Tor Browser отображает кнопку .onion available в адресной строке и предлагает переключиться. Заголовок учитывается только в том случае, если страница в обычном интернете отдается по HTTPS, а значение является корректным onion-URL.
Здесь есть нюанс с правилами nginx. Директивы add_header наследуются блоком location только в том случае, если в этом блоке нет собственных директив такого типа. Поэтому блок location с собственным add_header молча игнорирует Onion-Location. Повторите директиву внутри этого блока или храните все заголовки ответов в одном месте. Публикация этого заголовка намеренно связывает два сайта, что правильно для зеркала, но недопустимо для ресурсов, которые должны оставаться несвязанными.
Красивые адреса
mkp224o генерирует пары ключей до тех пор, пока не будет получен адрес, начинающийся с заданного вами префикса. Это поиск методом перебора, поэтому настраивать здесь нечего, кроме самого префикса и времени, которое вы готовы потратить на ожидание.
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Каждый найденный результат сохраняется в onionkeys/<address>.onion/, содержащем hostname и hs_ed25519_secret_key. Установите его: остановите tor, скопируйте этот каталог поверх вашего HiddenServiceDir, а затем примените те же chown и chmod 700, что и при восстановлении выше.
Длина префикса определяет все затраты. Адрес представлен в формате base32, поэтому каждый дополнительный символ увеличивает ожидаемое количество ключей в 32 раза. Короткий префикс можно подобрать на ноутбуке. Длинный префикс не получится подобрать ни на каком имеющемся у вас оборудовании. Кроме того, использование красивого префикса приучает пользователей обращать внимание только на первые несколько символов вместо всего адреса целиком, а именно на этой привычке строятся фишинговые копии onion-сайтов.
Типичные сбои и сообщения об ошибках
Отсутствует файл hostname после перезапуска. Tor не запустился или отказался использовать директорию. sudo journalctl -u tor@default -n 50 указывает на это:
/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?Так выглядит директория, созданная вручную. Исправьте владельца и права доступа или удалите директорию и позвольте Tor создать её самостоятельно.
Tor Browser показывает Onionsite Not Found (0xF0). Клиент не смог получить дескриптор, поэтому для сети по этому адресу ничего не опубликовано. Убедитесь, что Tor запущен и завершил процесс bootstrap, сверьте введённый адрес с sudo cat /var/lib/tor/onion_site/hostname посимвольно, затем проверьте системное время. Tor требует точного времени для публикации и проверки дескрипторов, а timedatectl должен сообщать System clock synchronized: yes.
Адрес разрешается, но страница не загружается. Tor завершил процедуру rendezvous, но произошёл сбой на последнем этапе — при передаче трафика от Tor к nginx. Этот этап является локальным, поэтому в логах Tor записей нет. Выполните curl -sI http://127.0.0.1:8080/ на сервере. Connection refused означает, что nginx не запущен или слушает адрес, отличный от того, на который указывает HiddenServicePort.
Страница загружается, но все ссылки ведут на ваш реальный домен. В шаблонах используются абсолютные URL. Выполните проверку grep -o 'https\?://[^"]*', описанную выше, и исправьте то, что она выведет, прежде чем публиковать адрес.
Всё работает, но перестаёт после перезагрузки. Перезагрузите сервер один раз намеренно, прежде чем полагаться на доступность сайта, затем выполните sudo systemctl status tor@default и sudo systemctl status nginx. Сервис, запущенный вручную, выглядит так же, как и включённый в автозагрузку, до тех пор, пока машина не перезагрузится.
FAQ
Нужно ли открывать порт в брандмауэре для Tor onion-сервиса?
Нет. Демон tor устанавливает только исходящие соединения: к серверам каталогов, точкам ввода (introduction points) и каждому узлу встречи (rendezvous relay). Входящие правила не требуются, а сам веб-сервер слушает только 127.0.0.1. Оставьте настройки ufw в состоянии default deny для входящего трафика, разрешив только SSH. Это свойство означает, что onion-сервис работает на машине за NAT (network address translation), даже если у неё нет публичного IP-адреса.
Почему я не могу открыть свой .onion-адрес в Tor Browser?
Проверяйте работоспособность от сервера к клиенту. sudo journalctl -u tor@default -n 50 должен показывать Bootstrapped 100% (done): Done, затем curl -sI http://127.0.0.1:8080/ на сервере должен возвращать строку состояния. Сравните введенный адрес с содержимым файла hostname: один неверный символ делает адрес другим сервисом. Ошибка Onionsite Not Found (0xF0) означает, что дескриптор для адреса не найден. Обычно это происходит, если tor не запущен или системное время установлено неверно.
Можно ли перенести onion-сайт на новый сервер, сохранив прежний адрес?
Да. Адрес вычисляется на основе hs_ed25519_secret_key, поэтому скопируйте всю папку HiddenServiceDir на новую машину, установите владельца debian-tor и права доступа 700, затем запустите tor. Адрес станет доступен сразу после повторной публикации дескриптора; обновлять DNS-записи не нужно. Если вы потеряете этот файл, адрес будет невозможно восстановить. Создайте зашифрованную резервную копию вне сервера сразу после создания сервиса.
Нужен ли onion-сайту HTTPS-сертификат?
Нет. 56-символьный адрес является открытым ключом сервиса, поэтому соединение уже зашифровано и аутентифицировано на всем пути. Tor Browser считает http:// на домене .onion безопасным контекстом. Использование вашего обычного сертификата для onion-хоста вреднее, чем его отсутствие: журналы Certificate Transparency публичны и навсегда фиксируют, какие доменные имена используют один и тот же сертификат. Единственная причина покупать сертификат для .onion — это подтверждение бренда от центра сертификации, но такая связь публична по своей сути.