Что имеем и зачем всё это
У вас дома крутятся полезные сервисы: Home Assistant, Jellyfin, code-server или Nextcloud. Или вы только собираетесь их запустить. Пока вы дома, всё открывается по локальному IP. Но стоит выйти за пределы квартиры, и сервисы пропадают. Причина: провайдер выдал серый IP-адрес, то есть применяет CGNAT. Белый адрес получить либо нельзя (примерно, как советский дефицит), либо он стоит дополнительных денег.
Сначала разберёмся с железом. Если домашний сервер уже есть, отлично. Если только планируете, вот варианты под любой бюджет:
-
Российские одноплатники. Repka Pi или Элтай ЭсСи (Eltay SC) от новосибирской компании «Элрон» на базе процессора «Скиф». Прямые аналоги Raspberry Pi на отечественной элементной базе.
-
Китайские одноплатники. Orange Pi, Banana Pi, Radxa. Доступны, имеют активные русскоязычные сообщества, часто предлагают лучшие характеристики за меньшие деньги.
-
Старый ноутбук или ПК. Самый народный путь. Мощности даже 10–15-летнего устройства с избытком хватит для домашнего сервера. У меня старенький, 14-летний Acer, рука не поднялась выбросить, поцарапанный, со сколами, но старый конь борозды не портит.
Возникает резонный вопрос: почему бы просто не арендовать VPS и не перенести все сервисы туда? Формально можно. Но у домашнего сервера есть преимущества, которых нет у VPS за 300–500 рублей:
-
Дисковое пространство. На домашнем сервере у вас может быть терабайт фотографий, фильмов и музыки. VPS с таким объёмом диска стоит совсем других денег.
-
Локальный доступ без интернета. Home Assistant продолжит управлять умным домом, даже если интернет пропадет. А Jellyfin будет раздавать кино по локальной сети без задержек и без расхода трафика.
-
Приватность. Данные лежат у вас дома, а не в дата-центре. Для кого-то это важно, в том числе для меня.
-
Производительность. Старый ноутбук с i5 может быть мощнее бюджетного VPS с одним vCPU.
Поэтому мы не заменяем домашний сервер, а даём ему публичный адрес через недорогой VPS-бастион. Сам VPS выступает только как прокси и не хранит данные.
Что делаем. Мы арендуем VPS с публичным IP ( можно найти конфигурации за 300–500 рублей в месяц). На самом деле проверяйте актуальные цены у провайдеров, так как рынок очень динамичный. Этот VPS будет точкой входа, или бастионом. Домашний сервер сам установит SSH-соединение с VPS и скажет: «Всё, что придёт на твой порт 8123, пересылай мне на локальный порт 8123». Получается зашифрованный туннель из дома наружу, которому не страшен NAT провайдера.
Моя цель — показать не просто работающий, а безопасный и отказоустойчивый метод. Я расскажу, почему для ключа мы выбираем ed25519 с дополнительными раундами KDF, зачем отключаем пароль для пользователя туннеля, как autossh и systemd вместе держат соединение даже при обрывах сети. Всё это сделано, чтобы туннель не падал каждую ночь и чтобы злоумышленник не мог подобрать пароль или ключ.
Такой подход выгоден по нескольким причинам:
-
Не нужно открывать порты на роутере.
-
Трафик между вами и домом шифруется.
-
Работает с любым провайдером, даже если используется CGNAT.
-
Мы не зависим от сторонних сервисов (будь то заблокированный Cloudflare или отечественные решения вроде DDoS-Guard или Qrator Labs) и полностью контролируем свои данные.
Как работает обратный SSH-туннель
Механизм простой. Выполняя команду
ssh -R 8123:localhost:8123 tunneluser@vps_ip
домашний сервер просит удалённую сторону (VPS) начать слушать порт 8123 и всё, что на него приходит, отправлять обратно через это же SSH-соединение на локальный порт 8123 домашней машины.
Когда кто-то из интернета стучится на <IP_VPS>:8123, трафик проходит такой путь:
Клиент → VPS:порт 8123 → SSH-туннель → Домашний сервер:порт 8123
Соединение всегда инициируется изнутри дома наружу, поэтому NAT на стороне провайдера не мешает. Чтобы пробросить несколько сервисов, просто добавляют несколько ключей -R. Чтобы туннель сам восстанавливался после обрывов связи, используют autossh. Аutossh это умная обёртка над обычным SSH.
Шаг 1. Подготовка VPS
Подключаемся к VPS по SSH.
Первым делом открываем конфигурационный файл SSH-сервера:
sudo nano /etc/ssh/sshd_config
Нам нужно явно разрешить проброс портов на все сетевые интерфейсы. По умолчанию SSH пробрасывает порты только на loopback (127.0.0.1), и подключиться к ним извне не получится. Ищем или добавляем две строки:
GatewayPorts yesAllowTcpForwarding yes
Настройка sshd_config в редакторе nano
Сохраняем файл и перезагружаем sshd, чтобы настройки применились:
sudo systemctl restart sshd
Теперь создадим отдельного пользователя, от имени которого домашний сервер будет устанавливать туннель. Использовать для этого root или своего обычного пользователя небезопасно: если злоумышленник завладеет ключом, он не должен получить возможность выполнять команды на VPS. Пользователю tunneluser мы это запретим, но пока создадим его с обычной оболочкой, чтобы скопировать ключ.
Создаём пользователя и задаём ему временный пароль:
sudo adduser tunneluser --disabled-passwordsudo passwd tunneluser
Теперь с домашнего сервера копируем публичный ключ. На домашнем сервере выполните:
ssh-copy-id -i ~/.ssh/id_ed25519.pub tunneluser@<IP_VPS>
После ввода временного пароля ключ будет добавлен. Проверим, что вход работает:
ssh tunneluser@<IP_VPS> hostname
Появится имя VPS и сразу соединение закроется. Если ошибка тогда проверьте права на ~/.ssh и authorized_keys на VPS у пользователя tunneluser.
Теперь, когда ключ скопирован, меняем оболочку на /usr/sbin/nologin, чтобы пользователь не мог выполнять команды. Проброс портов через -R продолжит работать, потому что для этого не нужен shell.
sudo usermod -s /usr/sbin/nologin tunneluser
В некоторых дистрибутивах (включая Ubuntu) /usr/sbin/nologin отсутствует в файле /etc/shells. Тогда sshd при подключении с ключом может отказать, даже если выполняется только проброс. Добавим оболочку в список разрешённых:
echo /usr/sbin/nologin | sudo tee -a /etc/shells
Теперь от имени tunneluser создадим каталог .ssh и выставим правильные права. Временно переключимся в его окружение:
sudo su - tunneluser -s /bin/bashmkdir -p ~/.sshchmod 700 ~/.sshexit
Создание каталога .ssh для пользователя tunneluser
Запретим вход по паролю для tunneluser. Снова откроем /etc/ssh/sshd_config и в конец добавим:
Match User tunneluser PasswordAuthentication no
Перезагружаем sshd:
sudo systemctl restart sshd
Теперь tunneluser сможет зайти только по ключу, пароль не сработает.
Также установим пакет psmisc, если его нет. В минимальных образах VPS утилита fuser может отсутствовать, а она понадобится скрипту для освобождения портов после обрыва туннеля.
sudo apt install -y psmisc
Установка пакета psmisc и управление паролем tunneluser
На этом подготовка VPS завершена.
Шаг 2. Настройка домашнего сервера
Все следующие команды выполняются на вашем домашнем устройстве: Raspberry Pi, Orange Pi, старом ноутбуке и так далее.
2.1. Генерируем SSH-ключ
Для аутентификации на VPS без пароля нам нужна пара ключей. Рекомендую использовать алгоритм ed25519: он быстрее и безопаснее, чем RSA, а ключи получаются короткими. Параметр -a 100 задаёт количество раундов KDF (функции формирования ключа). Если вы решите защитить приватный ключ паролем, это значительно замедлит попытки его подбора. В нашем случае ключ будет без пароля, чтобы autossh мог работать автоматически, но привычка указывать -a 100 полезна на будущее.
ssh-keygen -t ed25519 -a 100 -C "home-to-vps-tunnel"
На запрос пароля нажимаем Enter дважды оставляем пустым.
2.2. Устанавливаем autossh
Обычный SSH не умеет перезапускать туннель при обрыве. Если связь пропадёт, порты на VPS останутся «висеть» в занятом состоянии, и новый туннель не поднимется. autossh решает эту проблему: он мониторит состояние соединения и при необходимости перезапускает процесс.
sudo apt updatesudo apt install -y autossh
2.3. Список сервисов для проброса
Чтобы не редактировать скрипт каждый раз, когда добавляется новый сервис, заведём отдельный файл конфигурации. В нём просто перечисляются пары портов: удалённый порт на VPS и локальный порт домашнего сервера.
sudo nano /etc/sshtunnels/services.conf
Пример содержимого:
# Home Assistant8123:8123# Jellyfin8096:8096# code-server8443:8443# SSH на домашний сервер (нестандартный порт, чтобы не мешать VPS)2222:22
Формат: удалённый_порт_на_VPS:локальный_порт_домашнего_сервера. Строки, начинающиеся с #, игнорируются.
Обратите внимание на проброс SSH: домашний сервер станет доступен снаружи на порту 2222. Убедитесь, что на домашнем сервере также настроена аутентификация только по ключу, а вход по паролю отключён.
2.4. Пишем скрипт для запуска туннеля
Создадим скрипт, который будет читать services.conf и формировать команду для autossh.
sudo nano /usr/local/bin/tunnel-manager.sh
Скрипт выглядит так (замените REMOTE_HOST и путь к ключу на свои):
#!/bin/bashREMOTE_USER="tunneluser"REMOTE_HOST="123.123.123.123" # ваш IP VPSSSH_KEY="/home/pi/.ssh/id_ed25519" # путь к приватному ключуSSH_PORT="22"CONFIG_FILE="/etc/sshtunnels/services.conf"FORWARD_OPTS=""while IFS=: read -r remote_port local_port; do # Пропускаем пустые строки и комментарии [[ -z "$remote_port" || "$remote_port" =~ ^# ]] && continue # Убиваем старые процессы на удалённом порту, если туннель упал и порт остался занят ssh -p "$SSH_PORT" -i "$SSH_KEY" "$REMOTE_USER@$REMOTE_HOST" \ "command -v fuser && fuser -k ${remote_port}/tcp 2>/dev/null || true" FORWARD_OPTS="$FORWARD_OPTS -R ${remote_port}:localhost:${local_port}"done < "$CONFIG_FILE"echo "Запускаем туннели к $REMOTE_HOST с параметрами: $FORWARD_OPTS"exec autossh -M 0 \ -o "ServerAliveInterval=30" \ -o "ServerAliveCountMax=3" \ -o "ExitOnForwardFailure=yes" \ -o "StrictHostKeyChecking=no" \ -o "GatewayPorts=yes" \ -N -T \ -i "$SSH_KEY" \ -p "$SSH_PORT" \ $FORWARD_OPTS \ "$REMOTE_USER@$REMOTE_HOST"
Скрипт запуска туннеля tunnel-manager.sh
tunnel-manager.sh. В строках REMOTE_HOST и SSH_KEY замените значения на свои. Поясню ключевые моменты.
-
command -v fuser && fuser -k ... || true— сначала проверяем, есть лиfuserна VPS. Если нет, ничего не делаем, и код возврата всегда нулевой. Иначе скрипт мог бы упасть при отсутствииfuser. -
ExitOnForwardFailure=yes— если по какой-то причине не удалось пробросить хотя бы один порт, SSH немедленно завершится. Тогда systemd зафиксирует падение и перезапустит скрипт. -
StrictHostKeyChecking=no— отключает проверку отпечатка ключа хоста. Мы доверяем своему VPS, иначе при первом подключении система спросила бы подтверждение и скрипт завис. Если хотите усилить безопасность, выполните на домашнем сервереssh-keyscan -H <IP_VPS> >> ~/.ssh/known_hostsи заменитеStrictHostKeyChecking=noнаStrictHostKeyChecking=accept-new. В этом случае туннель примет только сохранённый отпечаток. -
GatewayPorts=yes— на всякий случай дублирует глобальную настройку, гарантируя, что проброшенные порты будут слушать все интерфейсы. -
-N— не выполнять никаких команд на удалённой стороне, только проброс портов. -
-T— не выделять псевдотерминал, что уменьшает накладные расходы. -
ServerAliveInterval=30иServerAliveCountMax=3— каждые 30 секунд SSH отправляет keepalive-пакет. Если три пакета подряд не получат ответа, соединение считается разорванным, и autossh инициирует переподключение. -
-M 0— отключает встроенный мониторинговый порт autossh. Мы полагаемся на механизмы keepalive самого SSH и перезапуск через systemd.
Делаем скрипт исполняемым:
sudo chmod +x /usr/local/bin/tunnel-manager.sh
2.5. Создаём systemd-сервис
Чтобы туннель автоматически стартовал при загрузке системы, оформим его как службу systemd.
Пользователям Windows (WSL): стандартная установка WSL не использует systemd. Чтобы команды ниже работали, включите systemd: добавьте в /etc/wsl.conf строки:
[boot]systemd=true
Затем перезапустите WSL командой wsl --shutdown в PowerShell и снова откройте терминал Ubuntu. Если systemd не нужен, просто запускайте туннель вручную через nohup, но тогда он не будет стартовать автоматически при загрузке.
Создаём файл службы:
sudo nano /etc/systemd/system/autossh-tunnel.service
Содержимое:
[Unit]Description=Persistent SSH Reverse Tunnel to VPSAfter=network-online.targetWants=network-online.target[Service]Type=execExecStart=/usr/local/bin/tunnel-manager.shRestart=alwaysRestartSec=20StartLimitIntervalSec=0Environment="AUTOSSH_GATETIME=0"Environment="AUTOSSH_POLL=60"[Install]WantedBy=multi-user.target
Файл службы
Параметры:
-
After=network-online.target— служба стартует только после полной инициализации сети. -
Restart=always— перезапускать при любом завершении, кроме явной остановки черезsystemctl stop. -
RestartSec=20— ждать 20 секунд перед повторной попыткой. Небольшая пауза полезна, чтобы не заддосить VPS при быстро повторяющихся ошибках. -
AUTOSSH_GATETIME=0— считать первое соединение успешным сразу, без задержки. -
AUTOSSH_POLL=60— интервал, с которым autossh опрашивает состояние соединения (в дополнение к ServerAliveInterval).
Шаг 3. Запуск и проверка
Перед запуском туннеля на VPS нужно открыть порты, которые мы будем пробрасывать. Иначе туннель установится, но подключиться извне не получится. Выполните на VPS:
sudo ufw allow 8123/tcpsudo ufw allow 8096/tcpsudo ufw allow 8443/tcpsudo ufw allow 2222/tcpsudo ufw enable
Если вы пробрасываете дополнительные порты, откройте и их, например sudo ufw allow 8080/tcp.
Теперь на домашнем сервере активируем и запускаем службу:
sudo systemctl daemon-reloadsudo systemctl enable autossh-tunnelsudo systemctl start autossh-tunnel
Проверяем статус:
sudo systemctl status autossh-tunnel
Теперь возьмите телефон, отключите Wi-Fi (чтобы трафик шёл через мобильную сеть) и откройте браузер. В адресной строке введите http://<IP\_VPS>:8123. Должен открыться интерфейс Home Assistant. Аналогично проверьте Jellyfin на порту 8096 и другие сервисы.
Доступ к домашнему серверу через мобильную сеть
Для контроля можно проверить проброс на VPS:
ss -tulpn | grep 8123
Если не работает:
-
Убедитесь, что на домашнем сервере сервис действительно слушает нужный порт:
ss -tulpn | grep 8123. Еслиssотсутствует, используйтеnetstat -tulpn | grep 8123илиlsof -i :8123. -
Проверьте, не занят ли этот порт на самом VPS каким-то процессом:
ss -tulpn | grep 8123. -
Возможно, на VPS включен файрвол ufw, и порты закрыты. Разрешите их, как описано выше.
-
Если на VPS не найдена утилита
fuser, установите её:sudo apt install -y psmisc. Она нужна скрипту для очистки портов после обрыва туннеля.
Типичная ситуация: если VPS перезагрузился в тот момент, когда туннель был активен, порты на VPS могут на короткое время остаться в состоянии TIME_WAIT. autossh с настройками выше переподнимет соединение, но возможна задержка до минуты. Если порт долго не освобождается, зайдите на VPS и проверьте ss -tulpn | grep <порт>. При необходимости завершите зависший процесс вручную.
Шаг 4. Базовая безопасность
4.1. Файрвол на VPS
Мы уже открыли нужные порты в ufw. Если вы всегда подключаетесь с одного IP (например, с работы), можно дополнительно ограничить доступ к чувствительным сервисам:
sudo ufw allow from <ваш_статический_IP> to any port 8123
После настройки Caddy и переноса сервисов на HTTPS прямые порты рекомендуется закрыть (см. раздел 4.3).
4.2. Регулярная замена ключей
Раз в полгода генерируйте новую пару ключей на домашнем сервере и заменяйте публичный ключ на VPS в /home/tunneluser/.ssh/authorized_keys. Так вы перестрахуетесь на случай, если старый ключ скомпрометируют, то есть попросту угонят.
4.3. HTTPS для сервисов
Пока мы ходим по HTTP, трафик не зашифрован между клиентом и VPS. Чтобы это исправить, нужен обратный прокси с SSL. Самый простой вариант — Caddy, потому что он автоматически получает сертификаты Let’s Encrypt и требует минимум настроек.
Установка на VPS:
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-httpscurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpgcurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.listsudo apt updatesudo apt install caddy
Конфигурация (/etc/caddy/Caddyfile):
hass.my-domain.ru { reverse_proxy localhost:8123}jellyfin.my-domain.ru { reverse_proxy localhost:8096}code.my-domain.ru { reverse_proxy localhost:8443}
Конфигурация Caddy для HTTPS и обратного прокси
Примечание: для автоматического HTTPS нужен реальный домен, направленный A-записью на IP вашего VPS. Если домена пока нет, Caddy можно использовать как обычный обратный прокси. Минимальная конфигурация для теста:
:80 { reverse_proxy localhost:8080}
После привязки домена замените :80 на ваш поддомен (например, hass.my-domain.ru) — Caddy сам запросит сертификат и настроит HTTPS.
Перезапуск:
sudo systemctl restart caddy
Проверим локально, что Caddy проксирует запросы:
curl -I http://localhost:80
Проверка работы Caddy
Caddy сам запросит сертификаты, и через минуту ваши сервисы будут доступны по HTTPS с человеческими адресами. После того как Caddy заработал, прямые порты можно закрыть, чтобы сервисы не светились по HTTP:
sudo ufw delete allow 8123/tcpsudo ufw delete allow 8096/tcpsudo ufw delete allow 8443/tcp
Теперь трафик будет ходить только через зашифрованные соединения на 443 порт.
При желании можно добавить запрос пароля на уровне Caddy:
sudo apt install apache2-utilshtpasswd -c /etc/caddy/.htpasswd username
И в блок сервиса добавить:
hass.my-domain.ru { basicauth { username $2a$14$... } reverse_proxy localhost:8123}
Хэш пароля берётся из созданного файла.
Примечание: при использовании обратного прокси все сервисы будут видеть запросы как пришедшие с 127.0.0.1, что может мешать анализу логов. В продвинутых сценариях это исправляется добавлением в конфигурацию Caddy директивы trusted_proxies и пробросом реального IP клиента. Для базовой установки такое поведение обычно не критично.
Шаг 5. Задержка
Добавление VPS в цепочку неизбежно увеличивает пинг, но SSH-туннель вносит минимальный оверхед. Если VPS находится в том же регионе, что и вы, задержка вырастает на 2–8 миллисекунд. Это практически незаметно даже при просмотре видео.
Альтернативы
Если autossh и ручная настройка кажутся громоздкими, можно посмотреть на утилиты вроде rathole, frp или bore. Они тоже делают проброс портов, но требуют установки своего ПО как на домашний сервер, так и на VPS. Наш метод хорош тем, что на VPS не нужно ставить ничего, кроме штатного SSH-сервера, который уже есть в любой Ubuntu.
Итог
Теперь у вас есть полноценный доступ к домашним сервисам из любой точки мира. Всё, что для этого потребовалось: VPS за 300–500 рублей в месяц и полчаса на настройку. Туннель работает прозрачно и шифрует трафик по пути. Самое важное: мы сделали его устойчивым к обрывам и защищённым от самых очевидных атак. Надеюсь, этот туториал поможет вам наладить свою инфраструктуру без лишней головной боли. Если что-то пошло не так, заглядывайте в логи journalctl -u autossh-tunnel и пишите в комментариях, разберёмся вместе.
ссылка на оригинал статьи https://habr.com/ru/articles/1038186/