Skip to content

Резервный туннель для обслуживания (mgmt-туннель)

Проблема

Если вы используете SSH-туннель через slug.dix.su:2222, а потом обновляете или перезапускаете dixsu-device-client на этом же устройстве — соединение рвётся и удалённый доступ пропадает. Устройство становится «глухим» до физического вмешательства.

Решение: два независимых туннеля

Держите на устройстве два отдельных device_client-сервиса с разными слагами:

Туннель Слаг Назначение
Основной mydevice Ваш сайт/сервис, порт 80
Резервный mydevice-mgmt SSH-доступ для обслуживания, порт 22

Обновление основного туннеля всегда делайте через mydevice-mgmt, не через основной. Mgmt-туннель трогайте только в крайнем случае.

Слаги PRO-тарифа позволяют до 2 дополнительных слагов, PERS — без ограничений. Создать дополнительный слаг можно в личном кабинете → «Мои туннели».


Настройка резервного сервиса (systemd)

# Создаём env-файл для mgmt-туннеля
cat > /etc/dixsu/device-client-mgmt.env << 'EOF'
SERVER_HOST=proxy.dix.su
SERVER_PORT=443
DEVICE_SLUG=mydevice-mgmt
DEVICE_TOKEN=<токен-второго-слага>
LOCAL_PORT=22
EOF
chmod 600 /etc/dixsu/device-client-mgmt.env

# Создаём отдельный systemd-юнит
cat > /etc/systemd/system/dixsu-device-client-mgmt.service << 'EOF'
[Unit]
Description=dix.su Device Client — mgmt tunnel (SSH)
After=network-online.target
Wants=network-online.target

[Service]
EnvironmentFile=/etc/dixsu/device-client-mgmt.env
ExecStart=/usr/local/bin/dixsu-device-client ${DEVICE_SLUG} ${DEVICE_TOKEN} ${LOCAL_PORT}
Restart=always
RestartSec=10
StartLimitIntervalSec=0

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable --now dixsu-device-client-mgmt

Настройка резервного сервиса (OpenRC / Alpine / postmarketOS)

# env-файл
cat > /etc/dixsu/device-client-mgmt.env << 'EOF'
SERVER_HOST=proxy.dix.su
SERVER_PORT=443
DEVICE_SLUG=mydevice-mgmt
DEVICE_TOKEN=<токен-второго-слага>
LOCAL_PORT=22
EOF
chmod 600 /etc/dixsu/device-client-mgmt.env

# init-скрипт
cat > /etc/init.d/dixsu-device-client-mgmt << 'EOF'
#!/sbin/openrc-run
description="dix.su Device Client — mgmt tunnel (SSH)"

depend() {
    need net
    after networkmanager
}

ENV_FILE=/etc/dixsu/device-client-mgmt.env

respawn
respawn_delay 5
respawn_max 0

start() {
    ebegin "Starting dixsu-device-client-mgmt"
    . "$ENV_FILE"
    start-stop-daemon --start \
        --background \
        --make-pidfile \
        --pidfile /run/dixsu-device-client-mgmt.pid \
        --stdout /var/log/dixsu-device-client-mgmt.log \
        --stderr /var/log/dixsu-device-client-mgmt.log \
        --env "SERVER_HOST=${SERVER_HOST}" \
        --env "SERVER_PORT=${SERVER_PORT}" \
        --exec /usr/local/bin/dixsu-device-client \
        -- "$DEVICE_SLUG" "$DEVICE_TOKEN" "$LOCAL_PORT"
    eend $?
}

stop() {
    ebegin "Stopping dixsu-device-client-mgmt"
    start-stop-daemon --stop --pidfile /run/dixsu-device-client-mgmt.pid
    eend $?
}
EOF
chmod +x /etc/init.d/dixsu-device-client-mgmt

rc-update add dixsu-device-client-mgmt default
rc-service dixsu-device-client-mgmt start

Процедура безопасного обновления бинаря

# 1. SSH через mgmt-туннель
ssh -p 2222 \
  -o "ProxyCommand=bash -c '{ echo DIXSU:mydevice-mgmt; cat; } | nc mydevice-mgmt.dix.su 2222'" \
  root@localhost

# 2. Скачать свежий бинарь (arm64 или x86_64_musl — в зависимости от архитектуры)
wget https://dix.su/downloads/device_client_linux_arm64 -O /tmp/dc-new
chmod +x /tmp/dc-new

# 3. Остановить основной туннель, обновить бинарь, очистить Burrito-кеш
rc-service dixsu-device-client stop
mv /tmp/dc-new /usr/local/bin/dixsu-device-client
rm -rf /root/.local/share/.burrito/device_client_erts-*/

# 4. Запустить — оба сервиса используют один и тот же бинарь
rc-service dixsu-device-client start

Mgmt-туннель при этом не прерывается: он не останавливался, а respawn автоматически поднимет его если что-то пойдёт не так.


Добавить respawn к уже работающему сервису

Если у вас один сервис без respawn (старая настройка):

OpenRC — добавьте в init-скрипт перед start():

respawn
respawn_delay 5
respawn_max 0

systemd — уже работает: Restart=always и RestartSec=10 включены по умолчанию в скрипте install-service.sh.


Альтернатива SSH: браузерный терминал через ttyd

ttyd — терминал прямо в браузере поверх HTTP/WebSocket. Работает на любом тарифе (включая SIMPLE) — открытый SSH-порт не нужен. Доступен только через личный кабинет dix.su (путь /auth/terminal/{slug}), защищён системным логином и паролем устройства.

Браузерный терминал работает в два этапа:

  • HTML-страница (/auth/terminal/{slug}) — грузится через HTTP-прокси: modulauth запрашивает у dixu_proxy Host: slug.dix.su, тот форвардит на LOCAL_PORT device_client. Значит LOCAL_PORT device_client должен быть 7681 — иначе вместо ttyd откроется основной сервис (или 404).
  • WebSocket (/auth/terminal/{slug}/ws) — идёт через TCP relay (dix.su:4781 → устройство:7681) независимо от LOCAL_PORT.

Поэтому нужен отдельный слаг с LOCAL_PORT=7681 — обычно mydevice-mgmt. Совместить ttyd и веб-сервис (порт 80) на одном слаге не получится.

Прокинуть SSH (порт 22) через порт 4781 нельзя: порт 4781 жёстко маппится на target-порт 7681. Для SSH нужен отдельный порт 4022.

Установка ttyd

#!/bin/sh
# Работает в bash и ash/Alpine
if command -v apk > /dev/null 2>&1; then
    apk update && apk add ttyd
elif command -v apt > /dev/null 2>&1; then
    apt update && apt install -y ttyd
elif command -v pacman > /dev/null 2>&1; then
    pacman -Sy --noconfirm ttyd
elif command -v dnf > /dev/null 2>&1; then
    dnf install -y ttyd
elif command -v yum > /dev/null 2>&1; then
    yum install -y ttyd
else
    # Универсально: скачать бинарь напрямую
    ARCH=$(uname -m)
    [ "$ARCH" = "aarch64" ] && ARCH="aarch64" || ARCH="x86_64"
    curl -fsSL "https://github.com/tsl0922/ttyd/releases/latest/download/ttyd.$ARCH" \
        -o /usr/local/bin/ttyd
    chmod +x /usr/local/bin/ttyd
fi

Сервис автозапуска — systemd

cat > /etc/systemd/system/ttyd.service <<'EOF'
[Unit]
Description=ttyd web terminal
After=network.target

[Service]
ExecStart=/usr/bin/ttyd --port 7681 --writable login
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now ttyd

Сервис автозапуска — OpenRC (Alpine Linux и другие)

cat > /etc/init.d/ttyd <<'EOF'
#!/sbin/openrc-run
description="ttyd web terminal"
command="/usr/bin/ttyd"
command_args="--port 7681 --writable login"
command_background=true
pidfile="/run/ttyd.pid"
depend() { need net; }
EOF
chmod +x /etc/init.d/ttyd
rc-update add ttyd default
rc-service ttyd start

Сервис автозапуска — macOS (LaunchDaemon)

cat > /Library/LaunchDaemons/su.dix.ttyd.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>su.dix.ttyd</string>
  <key>ProgramArguments</key>
  <array>
    <string>/usr/local/bin/ttyd</string>
    <string>--port</string><string>7681</string>
    <string>--writable</string><string>login</string>
  </array>
  <key>RunAtLoad</key><true/>
  <key>KeepAlive</key><true/>
</dict>
</plist>
EOF
launchctl load /Library/LaunchDaemons/su.dix.ttyd.plist

Установка и сервис — Windows (PowerShell, от администратора)

Скачать ttyd.exe заранее.

# Установка через winget
winget install tsl0922.ttyd

# Сервис через NSSM (рекомендуется)
winget install nssm
nssm install ttyd "C:\Windows\System32\ttyd.exe" "--port 7681 --writable cmd"
nssm set ttyd Start SERVICE_AUTO_START
nssm start ttyd

Запуск туннеля для ttyd

Запустите device_client с LOCAL_PORT=7681. Это может быть основной слаг или отдельный mgmt-слаг — на ваше усмотрение:

# Вариант А: отдельный mgmt-слаг (рекомендуется для независимости)
SERVER_HOST=proxy.dix.su SERVER_PORT=443 \
  device_client mydevice-mgmt <mgmt-токен> 7681

# Вариант Б: основной слаг (если устройство одно и параллельный сервис не нужен)
SERVER_HOST=proxy.dix.su SERVER_PORT=443 \
  device_client mydevice <основной-токен> 7681

После запуска откройте браузерный терминал в личном кабинете:

https://dix.su/auth/terminal/mydevice-mgmt

Браузер покажет запрос системного логина и пароля устройства (запрос ttyd).

Не путайте с https://mydevice-mgmt.dix.su — это страница вашего туннеля, а не браузерный терминал.

Для дополнительной защиты: установите ограничение allowed_ips для mgmt-слага в настройках личного кабинета.


Безопасность: держите mgmt-туннель замороженным

Рекомендация

Держите mgmt-туннель замороженным в обычное время и размораживайте только когда нужен доступ. Даже при защите на уровне dix.su открытый mgmt-туннель — это постоянно доступный шелл устройства.

Браузерный терминал (ttyd) не требует пароля по умолчанию: ttyd запускается от root и сразу даёт shell без авторизации. Замороженный mgmt-туннель закрывает доступ к нему полностью — прокси возвращает страницу «туннель заморожен» даже авторизованным пользователям.

Когда нужен терминал:

  1. Перейдите в личный кабинет → Мои туннели
  2. Нажмите Разморозить рядом с mgmt-слагом
  3. Выполните работу
  4. Снова нажмите Заморозить

Разморозка и заморозка занимают секунду. device_client при этом остаётся запущенным на устройстве — туннель переходит в нужное состояние мгновенно.

Для дополнительной защиты можно также добавить пароль в ttyd:

# systemd: добавьте --credential login:password в ExecStart
ExecStart=/usr/bin/ttyd --port 7681 --writable --credential myuser:mypassword login

# OpenRC: добавьте в command_args
command_args="--port 7681 --writable --credential myuser:mypassword login"

Или ограничьте доступ по IP в настройках mgmt-слага (личный кабинет → «Мои туннели» → Настройки слага → allowed_ips).