Рабочий стол за NAT: autossh, systemd и VNC

Белого IP нет, роутер провайдерский, а домашний компьютер нужен прямо сейчас: забытый файл, зависшая сборка, открытая вкладка. Отдавать доступ стороннему сервису не хочется. Решение известное: пусть домашний компьютер сам подключается к VPS и держит туннель, через который мы попадём обратно.
В статье соберём такой туннель так, чтобы он переживал обрывы и перезагрузки (autossh + systemd), пустим через него полноценный рабочий стол (VNC) и сведём подключение к одной команде ssh home. Отдельно разберём грабли, из-за которых туннель «вроде работает», а на деле нет.
Вводная: что и зачем
Итак, у нас есть:
• Домашний компьютер (Debian 13) за NAT провайдера, без белого IP. Хотим получить к нему доступ из любой точки мира — в том числе к графическому рабочему столу.
• VPS (Debian 13) с публичным IP. Он будет «точкой встречи»: домашний компьютер сам к нему подключится, а мы будем заходить на VPS и через него попадать дальше.
Схема:
[Вы] ──ssh :443──▶ [VPS] ◀──ssh :443 (исходящее)── [Дом]
127.0.0.1:44443 ══ туннель ══▶ :22 дома
VNC: localhost:5900 у вас ══ внутри SSH ══▶ 127.0.0.1:5900 домаКлючевая идея: домашний компьютер сам инициирует соединение с VPS. Поэтому:
• не нужен белый IP и проброс портов у провайдера;
• не нужно трогать настройки роутера;
• фаервол провайдера не мешает — исходящие соединения обычно разрешены.
Конструкцию сделаем отказоустойчивой: если туннель оборвётся (сменился IP, упал провайдер, перезагрузился VPS), он поднимется сам. VNC при этом не будет торчать в интернет — он доступен только через SSH.
Что нам понадобится
На VPS (Debian 13):
• публичный IP (в примерах — 203.0.113.10, адрес из документационного диапазона);
• SSH-сервер на порту 443 — этот порт почти везде открыт на выход, даже в гостиничных и корпоративных сетях;
• пользователь для подключения. Для простоты начнём с root, а в разделе о безопасности заменим его на отдельных пользователей.
На домашнем компьютере (Debian 13):
• autossh — для автоматического переподключения туннеля;
• x11vnc — для доступа к текущему рабочему столу;
• SSH-клиент и SSH-сервер (клиент в Debian есть по умолчанию, сервер нужно поставить);
• графический сеанс X11, а не Wayland. В Debian 13 GNOME по умолчанию запускается на Wayland, а x11vnc с ним не работает — будет чёрный экран. Выберите «GNOME on Xorg» на экране входа или пропишите WaylandEnable=false в /etc/gdm3/daemon.conf. Если хотите остаться на Wayland — смотрите в сторону встроенного в GNOME удалённого рабочего стола (RDP), wayvnc для wlroots-окружений или krfb для KDE; туннельная часть статьи от этого не меняется.
Устанавливаем пакеты:
# Домашний компьютер
sudo apt update
sudo apt install autossh x11vnc openssh-server
# VPS
sudo apt update
sudo apt install openssh-serverШаг 0. Готовим SSH-сервер на VPS
Переносим SSH на порт 443 и сразу учим сервер быстро замечать мёртвые подключения. В /etc/ssh/sshd_config на VPS:
Port 443
ClientAliveInterval 30
ClientAliveCountMax 3Зачем ClientAlive*: когда связь с домом рвётся, старый процесс sshd на VPS ещё долго не знает об этом и продолжает держать порт 44443. Новое подключение из дома не может занять порт, падает, перезапускается — и так несколько минут, пока старая сессия не умрёт по таймауту. С этими настройками VPS сам закроет мёртвую сессию примерно через полторы минуты.
Применяем, не закрывая текущую SSH-сессию, и проверяем вход на новый порт из второго окна:
sudo systemctl restart ssh
ssh -p 443 root@203.0.113.10⚠ Если на VPS есть фаервол (ufw, nftables, панель хостера) — откройте в нём
443/tcpдо перезапуска. Если на VPS включена сокет-активация SSH (ssh.socket), порт задаётся в ней, а не вsshd_config. И помните: 443 не «маскирует» SSH подHTTPS — DPIопределяет протокол по рукопожатию, а не по номеру порта. Порт выбран просто потому, что он почти везде открыт.
Шаг 1. SSH-доступ с домашнего компьютера на VPS по ключу
Домашний компьютер должен подключаться к VPS без пароля, иначе при каждом переподключении туннель будет ждать ввода, и вся автоматика теряет смысл.
Генерируем ключ на домашнем компьютере (если его ещё нет):
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ""
Копируем публичный ключ на VPS и проверяем, что пускает без пароля:
ssh-copy-id -p 443 root@203.0.113.10
ssh -p 443 root@203.0.113.10Заодно этот вход добавит ключ VPS в ~/.ssh/known_hosts — без этого сервис в шаге 4 не сможет подключиться.
Шаг 2. Проверяем reverse-туннель вручную
Прежде чем городить systemd, убедимся, что туннель вообще работает. На домашнем компьютере:
ssh -p 443 -R 44443:localhost:22 root@203.0.113.10Оставляем окно открытым. На VPS в другом окне смотрим, слушается ли порт:
sudo ss -tlnp | grep 44443Ожидаем что-то вроде:
LISTEN 0 128 127.0.0.1:44443 0.0.0.0:* users:(("sshd",pid=...,fd=...))Порт появился — туннель работает. Теперь с VPS можно зайти на домашний компьютер (vasvs — ваш пользователь дома):
ssh -p 44443 vasvs@localhost⚠ Важный нюанс. По умолчанию reverse-туннель слушает только 127.0.0.1 на VPS. Это правильно и безопасно: порт доступен только с самого VPS, а не из интернета. GatewayPorts yes не включаем.
Шаг 3. Держим туннель живым: autossh
Обычный SSH-туннель падает при первой же сетевой икоте. autossh запускает ssh и перезапускает его, если тот завершился:
autossh -M 0 -N \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-p 443 \
-R 44443:localhost:22 \
root@203.0.113.10Разберём флаги:
• -M 0 — отключаем встроенный мониторинг autossh (он использует отдельные порты и часто упирается в фаерволы). Живость соединения проверяет сам ssh.
• -N — не выполнять команды на удалённой стороне, только туннель.
• ServerAliveInterval=30 — каждые 30 секунд ssh отправляет keepalive.
• ServerAliveCountMax=3 — три keepalive подряд без ответа, и соединение считается мёртвым: ssh завершается, autossh поднимает новое.
• ExitOnForwardFailure=yes — если ssh не смог занять порт 44443 на VPS, соединение сразу закрывается. Без этого флага можно получить «тихий» туннель: ssh подключён, а порта на VPS нет. Эта мелочь экономит пару вечеров отладки.
• -p 443 — порт SSH на VPS.
• -R 44443:localhost:22 — собственно reverse-туннель.
Честная оговорка: с -M 0 под systemd autossh почти ничего не добавляет — перезапуск умеет и сам systemd (Restart=always), а проверку живости делает ssh. Можно смело заменить /usr/bin/autossh -M 0 на /usr/bin/ssh в unit-файле ниже, всё будет работать так же. autossh удобен, когда туннель запускается руками или из cron, без systemd.
Шаг 4. Оформляем туннель как systemd-сервис
Создаём /etc/systemd/system/reverse-ssh.service:
[Unit]
Description=Reverse SSH Tunnel to VPS
After=network-online.target
Wants=network-online.target
[Service]
User=vasvs
Environment=AUTOSSH_GATETIME=0
ExecStart=/usr/bin/autossh -M 0 -N \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-o ExitOnForwardFailure=yes \
-o BatchMode=yes \
-p 443 \
-R 44443:localhost:22 \
root@203.0.113.10
Restart=always
RestartSec=10
[Install]
WantedBy=multi-user.targetПояснения:
• User=vasvs — сервис работает от вашего пользователя, потому что ключ и known_hosts лежат в его ~/.ssh. От root ключ не найдётся.
• After= и Wants=network-online.target — стартуем, когда сеть действительно поднялась, а не только интерфейс.
• AUTOSSH_GATETIME=0 — без этого autossh завершается, если самое первое подключение не удалось (например, VPS ещё недоступен при загрузке).
• BatchMode=yes — ssh никогда не станет ждать пароль или подтверждение ключа хоста: либо подключился, либо ошибка в журнале.
• Restart=always и RestartSec=10 — перезапуск в любом случае, с паузой 10 секунд.
Активируем и проверяем:
sudo systemctl daemon-reload
sudo systemctl enable --now reverse-ssh.service
sudo systemctl status reverse-ssh.serviceДолжно быть active (running). На VPS снова проверяем sudo ss -tlnp | grep 44443 — если порт слушается, туннель работает и переживёт перезагрузку.
Шаг 5. VNC: полный рабочий стол
SSH-доступ есть, теперь графика. Задаём пароль VNC на домашнем компьютере:
x11vnc -storepasswd
Пароль сохранится в ~/.vnc/passwd. Это пароль именно на VNC, не путайте с паролем пользователя. Учтите, что VNC использует только первые 8 символов — основная защита здесь всё равно SSH.
Проверяем вручную, находясь в графическом сеансе:
x11vnc -forever -shared -localhost -rfbauth ~/.vnc/passwd -display :0
Флаги:
• -forever — не выходить после отключения клиента;
• -shared — разрешить нескольким клиентам подключаться одновременно;
• -localhost — принимать соединения только с localhost. Критично для безопасности: VNC не должен быть доступен снаружи, ходим к нему только через SSH;
• -rfbauth — файл с паролем;
• -display :0 — какой X-дисплей показывать. Номер может отличаться: проверьте echo $DISPLAY в терминале графического сеанса.
Во втором терминале убеждаемся, что VNC слушает только localhost:
ss -tlnp | grep 5900Должно быть 127.0.0.1:5900. Это то, что нужно.
Шаг 6. Автозапуск x11vnc
Создаём /etc/systemd/system/x11vnc.service :
[Unit]
Description=x11vnc server for reverse SSH access
After=display-manager.service
Wants=display-manager.service
[Service]
Type=simple
User=vasvs
ExecStart=/usr/bin/x11vnc -forever -shared -localhost \
-rfbauth /home/vasvs/.vnc/passwd -find
Restart=always
RestartSec=5
[Install]
WantedBy=graphical.targetВместо жёсткого -display :0 здесь -find: x11vnc сам найдёт X-дисплей пользователя и нужный файл авторизации, а если сеанса ещё нет — подождёт. Это важно, потому что GDM после входа часто поднимает сеанс пользователя не на :0, а на :1.
Сервис показывает ваш рабочий стол, то есть начинает работать после входа в графический сеанс. Если компьютер после перезагрузки стоит на экране входа, до рабочего стола вы не доберётесь — но SSH будет доступен. Проще всего включить автовход в настройках GDM (и блокировку экрана — чтобы дома рабочий стол не стоял открытым).
sudo systemctl daemon-reload
sudo systemctl enable --now x11vnc.service
sudo systemctl status x11vnc.serviceТеперь и туннель, и VNC поднимаются автоматически.
Шаг 7. Подключаемся из любой точки
Вариант А: одна команда через ProxyJump
ssh умеет сам «прыгать» через промежуточный сервер. На компьютере, с которого подключаетесь:
ssh -J root@203.0.113.10:443 -p 44443 -L 5900:localhost:5900 vasvs@localhostssh зайдёт на VPS и уже оттуда подключится к localhost:44443 — то есть в reverse-туннель. localhost здесь относится к VPS, а не к вашей машине. Одна команда — два прыжка.
Чтобы не набирать это каждый раз, добавим в ~/.ssh/config:
Host vps
HostName 203.0.113.10
Port 443
User root
Host home
HostName localhost
Port 44443
User vasvs
ProxyJump vps
HostKeyAlias home-desktop
LocalForward 5900 localhost:5900HostKeyAlias нужен, чтобы ключ домашнего компьютера не записался в known_hosts под безликим [localhost]:44443 и не конфликтовал с другими туннелями.
Теперь подключение выглядит так:
ssh home # терминал домашнего компьютера + проброс VNC
vncviewer localhost:5900 # во втором окне — рабочий столЕсли локально порт 5900 занят, возьмите другой: LocalForward 5901 localhost:5900 и vncviewer localhost:5901.
Вариант Б: два прыжка вручную (чтобы понять, как это работает)
На своём компьютере заходим на VPS и пробрасываем локальный 5900 на 5900 VPS:
ssh -p 443 -L 5900:localhost:5900 root@203.0.113.10В этом же сеансе, уже на VPS, заходим домой и пробрасываем 5900 VPS на 5900 домашнего компьютера:
ssh -p 44443 -L 5900:localhost:5900 vasvs@localhostПолучилась цепочка: ваш localhost:5900 → VPS:5900 → дом:5900. Во втором терминале запускаем vncviewer localhost:5900, вводим пароль VNC — и видим рабочий стол. Вариант А делает то же самое, но короче и без открытого порта 5900 на VPS.
Шаг 8. Безопасность
Схема рабочая, но root на VPS мы использовали только для простоты. Приведём её в порядок. Делайте изменения по одному и держите открытой рабочую SSH-сессию, пока не проверите новый вход.
1. Отдельный пользователь для туннеля. На VPS:
sudo adduser --disabled-password --gecos "" tunnel
sudo install -d -m 700 -o tunnel -g tunnel /home/tunnel/.sshВ /home/tunnel/.ssh/authorized_keys кладём публичный ключ домашнего компьютера (~/.ssh/id_ed25519.pub) с ограничениями:
restrict,port-forwarding,permitlisten="44443" ssh-ed25519 AAAA... homeЭтот ключ сможет только открыть reverse-туннель на порт 44443: ни shell, ни команд, ни других портов. Не забудьте sudo chown tunnel:tunnel и chmod 600 на файл. В reverse-ssh.service меняем root@... на tunnel@....
2. Отдельный пользователь для себя. Туннельный пользователь не умеет ничего, кроме туннеля, поэтому для входа на VPS заведите обычного пользователя с ключом вашего ноутбука и замените им root в блоке Host vps в ~/.ssh/config.
3. Закрываем root и пароли. Когда вход под своим пользователем проверен, в /etc/ssh/sshd_config на VPS:
PermitRootLogin no
PasswordAuthentication noИ sudo systemctl restart ssh.
4. fail2ban. Ставим sudo apt install fail2ban, но учтите две вещи: стандартный jail банит на порту 22, а в Debian нет /var/log/auth.log по умолчанию. Создаём /etc/fail2ban/jail.local:
[sshd]
enabled = true
port = 443
backend = systemdи перезапускаем: sudo systemctl restart fail2ban.
5. VNC — только через SSH. Флаг -localhost у x11vnc гарантирует, что VNC недоступен напрямую. Не убирайте его.
6. Обновления. И на VPS, и дома: sudo apt update && sudo apt upgrade. На VPS удобно включить unattended-upgrades.
Диагностика: что может пойти не так
Порт 44443 на VPS не слушается. Смотрим сервис на домашнем компьютере:
sudo systemctl status reverse-ssh
sudo journalctl -u reverse-ssh -fВ журнале будет точная причина. Частые варианты:
• ключ не скопирован на VPS или лежит не у того пользователя — с BatchMode=yes будет Permission denied;
• ключ VPS не в known_hosts пользователя vasvs — Host key verification failed, зайдите на VPS руками один раз;
• порт 44443 на VPS занят — remote port forwarding failed;
• фаервол на VPS не пропускает 443/tcp;
• в команде не тот порт SSH (-p 443 против -p 22).
Туннель после обрыва несколько минут не поднимается, в журнале remote port forwarding failed. Порт держит старая «мёртвая» сессия на VPS. Проверьте ClientAliveInterval и ClientAliveCountMax из шага 0.
VNC показывает чёрный экран. Почти всегда это Wayland — проверьте echo $XDG_SESSION_TYPE в графическом сеансе, там должно быть x11. Второй вариант — x11vnc подключился не к тому дисплею: посмотрите who и echo $DISPLAY и journalctl -u x11vnc.
VNC-клиент не подключается к localhost:5900. Проверьте, что:
• SSH-сессия с -L (или ssh home) открыта;
• порядок аргументов именно такой: -L локальный_порт:хост:удалённый_порт;
• на домашнем компьютере x11vnc действительно слушает 127.0.0.1:5900 (ss -tlnp | grep 5900).
Туннель живёт, но отваливается через несколько минут простоя. Где-то по пути есть idle-timeout. ServerAliveInterval=30 обычно решает проблему; если нет — уменьшите до 15 секунд.
Итог
Что мы получили:
• домашний Debian 13 сам поднимает reverse SSH-туннель на VPS и держит его живым благодаря autossh и systemd;
• VPS быстро закрывает мёртвые сессии, так что туннель восстанавливается за секунды, а не за минуты;
• x11vnc показывает рабочий стол, но слушает только localhost — снаружи он недоступен;
• из любой точки мира ssh home открывает терминал домашнего компьютера и проброс к его рабочему столу;
• всё переживает перезагрузки, смену IP и кратковременные обрывы сети.
Схема не претендует на корпоративный уровень, но для личного использования подходит отлично. Все компоненты стандартные, из репозиториев Debian.
Если хочется совсем без ручной возни — посмотрите в сторону Tailscale или ZeroTier. Но если хочется понимать, что происходит под капотом, и не зависеть от сторонних сервисов, reverse SSH и systemd остаются классикой, которая работает годами.
P.S. Если дома не Debian 13, отличия минимальны: замените apt на свой пакетный менеджер, пути к systemd-юнитам останутся прежними. x11vnc есть в большинстве репозиториев.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.