Что имеем и зачем всё это
У вас дома крутятся полезные сервисы: Home Assistant, 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 продолжит управлять умным домом, даже если интернет пропадет. А фильмы и музыка будут доступны по локальной сети без задержек и без расхода трафика.
Приватность. Данные лежат у вас дома, а не в дата-центре. Для кого-то это важно, в том числе для меня.
Производительность. Старый ноутбук с 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 yes
AllowTcpForwarding yesНастройка sshd_config в редакторе nano

Сохраняем файл и перезагружаем sshd, чтобы настройки применились:
sudo systemctl restart sshdТеперь создадим отдельного пользователя, от имени которого домашний сервер будет устанавливать туннель. Использовать для этого root или своего обычного пользователя небезопасно: если злоумышленник завладеет ключом, он не должен получить возможность выполнять команды на VPS. Пользователю tunneluser мы это запретим, но пока создадим его с обычной оболочкой, чтобы скопировать ключ.
Создаём пользователя и задаём ему временный пароль:
sudo adduser tunneluser --disabled-password
sudo 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/bash
mkdir -p ~/.ssh
chmod 700 ~/.ssh
exitСоздание каталога .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 update
sudo apt install -y autossh2.3. Список сервисов для проброса
Чтобы не редактировать скрипт каждый раз, когда добавляется новый сервис, заведём отдельный файл конфигурации. В нём просто перечисляются пары портов: удалённый порт на VPS и локальный порт домашнего сервера.
sudo nano /etc/sshtunnels/services.confПример содержимого:
# Home Assistant
8123:8123
# Jellyfin
8096:8096
# code-server
8443:8443
# SSH на домашний сервер (нестандартный порт, чтобы не мешать VPS)
2222:22Формат: удалённый_порт_на_VPS:локальный_порт_домашнего_сервера. Строки, начинающиеся с #, игнорируются.
Обратите внимание на проброс SSH: домашний сервер станет доступен снаружи на порту 2222. Убедитесь, что на домашнем сервере также настроена аутентификация только по ключу, а вход по паролю отключён.
2.4. Пишем скрипт для запуска туннеля
Создадим скрипт, который будет читать services.conf и формировать команду для autossh.
sudo nano /usr/local/bin/tunnel-manager.shСкрипт выглядит так (замените REMOTE_HOST и путь к ключу на свои):
#!/bin/bash
REMOTE_USER="tunneluser"
REMOTE_HOST="123.123.123.123" # ваш IP VPS
SSH_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.sh2.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 VPS
After=network-online.target
Wants=network-online.target
[Service]
Type=exec
ExecStart=/usr/local/bin/tunnel-manager.sh
Restart=always
RestartSec=20
StartLimitIntervalSec=0
Environment="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/tcp
sudo ufw allow 8096/tcp
sudo ufw allow 8443/tcp
sudo ufw allow 2222/tcp
sudo ufw enableЕсли вы пробрасываете дополнительные порты, откройте и их, например sudo ufw allow 8080/tcp.
Теперь на домашнем сервере активируем и запускаем службу:
sudo systemctl daemon-reload
sudo systemctl enable autossh-tunnel
sudo 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-https
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo 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/tcp
sudo ufw delete allow 8096/tcp
sudo ufw delete allow 8443/tcpТеперь трафик будет ходить только через зашифрованные соединения на 443 порт.
При желании можно добавить запрос пароля на уровне Caddy:
sudo apt install apache2-utils
htpasswd -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 и пишите в комментариях, разберёмся вместе.

























