Перейти к основному содержимому

tuna ❤️ JetKVM

JetKVM — это компактное IP-KVM устройство на базе Rockchip RV1106 (ARM Cortex-A7). Оно позволяет удалённо управлять компьютером: видеть экран, управлять клавиатурой и мышью, монтировать образы дисков — всё через веб-интерфейс в браузере.

По умолчанию JetKVM доступен только в локальной сети. С помощью Tuna вы можете открыть доступ к нему через интернет — без «белого» IP, без VPN, без проброса портов на роутере.

Tuna + JetKVM
к сведению

Данная инструкция протестирована на JetKVM с системной версией v0.2.7 и выше. На более ранних версиях /userdata/init.d/ не поддерживается — обновите JetKVM через веб-интерфейс (Settings → Device → Check for updates).


Подготовка

Помимо установки инструкция по настройке разделена на 2 типа, в зависимости от наличия платной подписки Tuna.

  1. На бесплатном тарифе
  2. С платной подпиской

Что потребуется

  • JetKVM с системной версией v0.2.7 или новее
  • Developer Mode включен в веб-интерфейсе JetKVM (Settings → Advanced → Developer Mode)
  • SSH-ключ добавлен через веб-интерфейс JetKVM
  • Аккаунт на tuna.am (бесплатный или с подпиской)
  • Токен авторизации со страницы токена

Особенности системы JetKVM

JetKVM работает на кастомной embedded Linux (Buildroot + Rockchip SDK). Важные отличия от обычного Linux:

ОсобенностьЗначение
Оболочка/bin/sh (BusyBox ash). bash отсутствует
SSH-серверDropbear (нет scp, sftp, rsync)
Init-системаBusyBox init + скрипты в /userdata/init.d/
Persistent-раздел/userdata/ — сохраняется при OTA-обновлениях

Структура хранилища:

ПутьНазначениеСохраняется при OTA
/Корневая ФСНет
/userdata/Пользовательские данныеДа

Мы будем хранить tuna в /userdata/tuna/, чтобы бинарник и конфигурация сохранялись при обновлениях.

Подключение по SSH

Включите Developer Mode в веб-интерфейсе JetKVM и добавьте публичный RSA-ключ (Settings → Advanced → Developer Mode). Затем подключитесь:

ssh root@<IP-адрес-JetKVM>

Или откройте терминал в веб интерфейсе:

JetKVM terminal
подсказка

IP-адрес можно найти на встроенном экране JetKVM или в DHCP-таблице роутера.

Установка tuna на JetKVM

Подключитесь к JetKVM по SSH и установите бинарник:

mkdir -p /userdata/tuna
INSECURE=true INSTALL_DIR=/userdata/tuna sh -c "$(wget -qO- http://releases.tuna.am/tuna/get.sh)"
Почему INSECURE=true?

На JetKVM отсутствуют корневые CA-сертификаты, поэтому wget не может проверить SSL-сертификат сервера. Флаг INSECURE=true переключает скрипт на загрузку по HTTP. Это безопасно, так как бинарник tuna после загрузки можно проверить по контрольной сумме.

подсказка

Раздел /userdata сохраняется при OTA-обновлениях, поэтому tuna не придётся переустанавливать после обновления прошивки.

Сохраните токен авторизации:

/userdata/tuna/tuna --config=/userdata/tuna/.tuna.yml config save-token <ВАШ_ТОКЕН>
подсказка

Рекомендуем создать дополнительный токен для устройства.


Система автозапуска JetKVM

В отличие от NanoKVM, где автозапуск настраивается через /etc/inittab с директивой respawn, на JetKVM используются init-скрипты в директории /userdata/init.d/, каталог нужно создать.

mkdir -p /userdata/init.d

При загрузке init-система JetKVM выполняет все скрипты из /userdata/init.d/, чьи имена соответствуют паттерну S??* (например, S99tuna-http). Скрипты вызываются с аргументом start, при остановке — с аргументом stop.

Критические правила для init-скриптов

Несоблюдение этих правил может привести к тому, что устройство перестанет загружаться:

  1. Используйте #!/bin/sh — на JetKVM нет /bin/bash. Шебанг #!/bin/bash приведёт к ошибке и может заблокировать загрузку.
  2. Не используйте расширение .sh — файлы с расширением .sh включаются через source (а не вызываются как отдельный процесс). Команда exit в таком скрипте завершит весь init-процесс.
  3. Не блокируйте загрузку — все долгоживущие процессы должны запускаться в фоне с &. Если скрипт не возвращает управление, загрузка зависнет.
  4. Обрабатывайте аргумент start — init-система вызывает скрипт как S99tuna-http start. Без case "$1" скрипт может выполниться некорректно.
Нет автоматического перезапуска

В отличие от NanoKVM (где inittab respawn автоматически перезапускает упавший процесс), на JetKVM /etc/inittab перезаписывается при OTA-обновлениях. Поэтому мы используем цикл while true внутри init-скрипта для автоперезапуска при падении.

Тестируйте перед перезагрузкой

Всегда запускайте скрипт вручную (/userdata/init.d/S99tuna-http start) и убедитесь, что промпт вернулся. Если после запуска консоль «зависла» — скрипт заблокирует загрузку при перезагрузке.


Тип 1. Бесплатный тариф — HTTP-туннель

На бесплатном тарифе доступен HTTP-туннель с динамическим адресом. Этого достаточно, чтобы открыть веб-интерфейс JetKVM в интернет и управлять компьютером через браузер.

Ограничения бесплатного тарифа
  • Адрес туннеля меняется каждые 30 минут
  • Нельзя задать фиксированный поддомен
  • Текущий адрес всегда можно посмотреть в личном кабинете

Создание init-скрипта

cat << 'INITEOF' > /userdata/init.d/S99tuna-http
#!/bin/sh

PIDFILE=/tmp/tuna-http.pid
LOGFILE=/tmp/tuna-http.log

wait_network() {
i=0
while ! ip route | grep -q default; do
sleep 1
i=$((i + 1))
if [ "$i" -ge 60 ]; then
echo "$(date): network timeout" >> "$LOGFILE"
return 1
fi
done
return 0
}

start() {
echo "$(date): starting tuna-http" >> "$LOGFILE"
(
wait_network || exit 1
while true; do
/userdata/tuna/tuna \
--config=/userdata/tuna/.tuna.yml \
--log="$LOGFILE" \
http \
--inspect=false \
--basic-auth=tuna:SecurePasswd \
--https-redirect \
80 >> "$LOGFILE" 2>&1
echo "$(date): tuna-http exited, restarting in 5s" >> "$LOGFILE"
sleep 5
done
) &
echo $! > "$PIDFILE"
}

stop() {
if [ -f "$PIDFILE" ]; then
kill "$(cat "$PIDFILE")" 2>/dev/null
pkill -f "/userdata/tuna/tuna.*http" 2>/dev/null
rm -f "$PIDFILE"
fi
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
INITEOF
chmod +x /userdata/init.d/S99tuna-http

Пояснение init-скрипта

ЭлементОписание
S99tuna-httpИмя без .sh — init вызывает скрипт с аргументом start, а не включает через source
wait_network()Ожидает появления default route (максимум 60 секунд)
( ... ) &Субшелл запускается в фоне — не блокирует загрузку
while trueАвтоматический перезапуск tuna при падении (пауза 5 секунд)
echo $! > "$PIDFILE"Сохраняет PID субшелла для корректной остановки
pkill -fПри остановке убивает и обёртку, и дочерний процесс tuna

Пояснение флагов tuna

ФлагОбязательныйОписание
--inspect=falseНетОтключает инспектор трафика (не нужен для KVM), чтобы не расходовать RAM
--basic-auth=admin:ВашПарольНетДополнительная HTTP-авторизация (подробнее)
--https-redirectНетПеренаправляет HTTP на HTTPS
80ДаПорт веб-интерфейса JetKVM

Также можно ограничить доступ по IP-подсетям.

Проверка

Запустите скрипт вручную:

/userdata/init.d/S99tuna-http start

Убедитесь, что промпт вернулся (скрипт не заблокировал консоль). Проверьте, что процесс запущен:

ps | grep tuna

В выводе должна быть строка с /userdata/tuna/tuna ... http .... Если процесса нет — смотрите раздел Диагностика неполадок.

В логах вы увидите URL вашего туннеля:

cat /tmp/tuna-http.log

Также текущий адрес доступен в личном кабинете.


Тип 2. Подписка — SSH и HTTP туннели

С подпиской вы получаете:

  • Фиксированный поддомен для HTTP-туннеля — адрес не меняется
  • SSH-туннель — прямой доступ к консоли JetKVM из любой точки мира

HTTP-туннель с фиксированным адресом

cat << 'INITEOF' > /userdata/init.d/S99tuna-http
#!/bin/sh

PIDFILE=/tmp/tuna-http.pid
LOGFILE=/tmp/tuna-http.log

wait_network() {
i=0
while ! ip route | grep -q default; do
sleep 1
i=$((i + 1))
if [ "$i" -ge 60 ]; then
echo "$(date): network timeout" >> "$LOGFILE"
return 1
fi
done
return 0
}

start() {
echo "$(date): starting tuna-http" >> "$LOGFILE"
(
wait_network || exit 1
while true; do
/userdata/tuna/tuna \
--config=/userdata/tuna/.tuna.yml \
--log="$LOGFILE" \
http \
--subdomain=jetkvm \
--inspect=false \
--basic-auth=admin:ВашПароль \
--https-redirect \
80 >> "$LOGFILE" 2>&1
echo "$(date): tuna-http exited, restarting in 5s" >> "$LOGFILE"
sleep 5
done
) &
echo $! > "$PIDFILE"
}

stop() {
if [ -f "$PIDFILE" ]; then
kill "$(cat "$PIDFILE")" 2>/dev/null
pkill -f "/userdata/tuna/tuna.*http" 2>/dev/null
rm -f "$PIDFILE"
fi
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
INITEOF
chmod +x /userdata/init.d/S99tuna-http

Отличие от бесплатной версии — флаг --subdomain=jetkvm, который закрепляет за вами постоянный адрес. После перезапуска туннеля URL останется прежним.

к сведению

Поддомен из примера уже может быть занят. Зарезервируйте свой поддомен заранее в личном кабинете, затем используйте его в флаге --subdomain=ваш-поддомен.

Пояснение флагов HTTP-туннеля

ФлагОбязательныйОписание
--subdomain=jetkvmНетФиксирует постоянный адрес туннеля (только с подпиской)
--inspect=falseНетОтключает инспектор трафика (не нужен для KVM), чтобы не расходовать RAM
--basic-auth=admin:ВашПарольНетДополнительная HTTP-авторизация (подробнее)
--https-redirectНетПеренаправляет HTTP на HTTPS
80ДаПорт веб-интерфейса JetKVM

Также можно ограничить доступ по IP-подсетям.

SSH-туннель

SSH-туннель позволяет подключаться к консоли JetKVM через интернет. Это удобно для администрирования, обновления прошивки и диагностики.

cat << 'INITEOF' > /userdata/init.d/S98tuna-ssh
#!/bin/sh

PIDFILE=/tmp/tuna-ssh.pid
LOGFILE=/tmp/tuna-ssh.log

wait_network() {
i=0
while ! ip route | grep -q default; do
sleep 1
i=$((i + 1))
if [ "$i" -ge 60 ]; then
echo "$(date): network timeout" >> "$LOGFILE"
return 1
fi
done
return 0
}

start() {
echo "$(date): starting tuna-ssh" >> "$LOGFILE"
(
wait_network || exit 1
while true; do
/userdata/tuna/tuna \
--config=/userdata/tuna/.tuna.yml \
--log="$LOGFILE" \
ssh \
--port=jetkvm-ssh \
--record-session=false \
--password-auth=false >> "$LOGFILE" 2>&1
echo "$(date): tuna-ssh exited, restarting in 5s" >> "$LOGFILE"
sleep 5
done
) &
echo $! > "$PIDFILE"
}

stop() {
if [ -f "$PIDFILE" ]; then
kill "$(cat "$PIDFILE")" 2>/dev/null
pkill -f "/userdata/tuna/tuna.*ssh" 2>/dev/null
rm -f "$PIDFILE"
fi
}

case "$1" in
start) start ;;
stop) stop ;;
restart) stop; sleep 1; start ;;
*) echo "Usage: $0 {start|stop|restart}" ;;
esac
INITEOF
chmod +x /userdata/init.d/S98tuna-ssh

Пояснение флагов SSH-туннеля

ФлагОбязательныйОписание
--port=jetkvm-sshНетФиксирует порт SSH-туннеля. Без этого флага порт будет назначаться случайно при каждом запуске. Алиас для порта нужно зарезервировать заранее перед запуском.
--record-session=falseНетОтключает запись SSH-сессий на диск. На embedded устройствах с ограниченным хранилищем рекомендуется отключать, чтобы не занимать место.
--password-auth=falseНетЗапрещает подключение по паролю — доступ только по SSH-ключам (подробнее)

Также можно задать статичный логин и пароль или ограничить доступ по IP-подсетям.

Проверка туннелей

Запустите оба скрипта вручную:

/userdata/init.d/S99tuna-http start
/userdata/init.d/S98tuna-ssh start

Убедитесь, что оба процесса запущены:

ps | grep tuna

В выводе должны быть две строки — с http и ssh. Если какого-то процесса нет — смотрите раздел Диагностика неполадок.

Посмотрите логи:

cat /tmp/tuna-http.log
cat /tmp/tuna-ssh.log

В логах HTTP-туннеля будет URL, а в логах SSH-туннеля — инструкция для подключения с указанием адреса и порта.


Управление туннелями

Автоперезапуск при падении обеспечивается циклом while true внутри init-скрипта. Для управления туннелями используйте следующие команды.

Проверка статуса

ps | grep tuna

Остановка туннеля

# Остановить HTTP-туннель
/userdata/init.d/S99tuna-http stop

# Остановить SSH-туннель
/userdata/init.d/S98tuna-ssh stop

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

# Запустить HTTP-туннель
/userdata/init.d/S99tuna-http start

# Запустить SSH-туннель
/userdata/init.d/S98tuna-ssh start

Перезапуск туннеля

# Перезапустить HTTP-туннель
/userdata/init.d/S99tuna-http restart

# Перезапустить SSH-туннель
/userdata/init.d/S98tuna-ssh restart

Полное отключение автозапуска

Чтобы туннель не запускался при загрузке — переименуйте скрипт, убрав префикс S:

# Отключить автозапуск HTTP-туннеля
mv /userdata/init.d/S99tuna-http /userdata/init.d/disabled-tuna-http

# Включить обратно
mv /userdata/init.d/disabled-tuna-http /userdata/init.d/S99tuna-http

Восстановление после OTA-обновления

Бинарник, конфигурация и init-скрипты хранятся в /userdata/, который сохраняется при OTA-обновлениях. После обновления прошивки через веб-интерфейс JetKVM туннели продолжат работать без дополнительных действий.

к сведению

В отличие от NanoKVM, на JetKVM не нужно восстанавливать /etc/inittab после обновления — автозапуск через /userdata/init.d/ полностью переживает OTA.

При полной перепрошивке (когда /userdata был очищен) потребуется заново установить бинарник, сохранить токен и создать init-скрипты.


Дополнительные настройки безопасности

Зачем нужен --basic-auth

У JetKVM есть собственная авторизация в веб-интерфейсе. Однако, учитывая историю уязвимостей в IoT-устройствах, мы настоятельно рекомендуем добавить дополнительный уровень защиты. При открытии доступа через интернет лучше перестраховаться — флаг --basic-auth добавит стандартную HTTP-авторизацию (логин и пароль в браузере) ещё до того, как запрос дойдёт до JetKVM.

--basic-auth=mylogin:MyStr0ngP@ss
Важно

Задайте свои логин и пароль. Не используйте значения из примеров.

Аутентификация в SSH-туннеле

Вариант 1. По SSH-ключам (рекомендуется)

Флаг --password-auth=false отключает вход по паролю. Для подключения потребуется предварительно добавить публичный SSH-ключ в личном кабинете.

Подробная инструкция: Настройка SSH-ключей

warning

Туннельные ноды имеют публичный адрес. Злоумышленники могут просканировать порты и начать подбор пароля. Для долгоживущих SSH-туннелей настоятельно рекомендуем отключить парольную аутентификацию.

Вариант 2. Статичный логин и пароль

Если SSH-ключи не подходят, можно задать фиксированные учётные данные:

--auth=user:pass

Замените user и pass на свои значения.

Ограничение по IP-подсетям

Для HTTP и SSH туннелей можно ограничить доступ по IP-адресам.

Разрешить доступ только с определённых подсетей:

--cidr-allow="203.0.113.0/24"

Запретить доступ из определённых подсетей:

--cidr-deny="198.51.100.0/24"

Можно комбинировать — разрешить подсеть, но исключить отдельные адреса:

--cidr-allow="10.0.0.0/24" --cidr-deny="10.0.0.33/32"
Осторожно

Используйте фильтрацию по IP с осторожностью. Если вы ошибётесь с подсетью или ваш IP-адрес изменится — вы потеряете удалённый доступ к JetKVM. Убедитесь, что у вас есть альтернативный способ управления устройством (например, локальный доступ).


Диагностика неполадок

Устройство не загружается после добавления init-скрипта

Экстренное восстановление

Если JetKVM перестал загружаться после добавления скрипта в /userdata/init.d/, наиболее частые причины:

  1. Шебанг #!/bin/bash — bash на JetKVM отсутствует
  2. Скрипт блокирует загрузку — процесс не запущен в фоне (&)
  3. Расширение .sh — скрипт был включён через source, что сломало init-процесс
  4. Синтаксическая ошибка — прерывает выполнение init

Способы восстановления:

  • UART serial console — если есть доступ к UART-порту JetKVM, подключитесь через serial console (115200 baud) и удалите или переименуйте проблемный скрипт
  • Factory reset — нажмите и удерживайте кнопку сброса на JetKVM (внимание: это может очистить /userdata/)

Процесс tuna не запускается

Проверьте, что процесс присутствует в списке:

ps | grep tuna

Если в выводе нет строк с /userdata/tuna/tuna — проверьте:

  1. Скрипт существует и имеет права на выполнение:
ls -la /userdata/init.d/S9*tuna*
  1. Скрипт запускается без ошибок:
/userdata/init.d/S99tuna-http start
  1. Бинарник tuna работает:
/userdata/tuna/tuna version

Просмотр логов

Логи туннелей записываются в /tmp/ и доступны до перезагрузки устройства:

# Логи HTTP-туннеля
cat /tmp/tuna-http.log

# Логи SSH-туннеля
cat /tmp/tuna-ssh.log

Для наблюдения за логами в реальном времени:

tail -f /tmp/tuna-http.log

Типичные ошибки в логах

ОшибкаПричинаРешение
token is invalidНеверный или просроченный токенПовторите config save-token с актуальным токеном со страницы
Domain already reservedПоддомен занят другим пользователемВыберите другое имя через --subdomain=
port alias not foundАлиас порта не зарезервированЗарезервируйте алиас в личном кабинете
connection refusedНет доступа к интернетуПроверьте сеть: ping -c 3 8.8.8.8
exec format errorНеправильная архитектура бинарникаУбедитесь, что используете tuna_linux_arm (ARMv7)

Нет доступа к интернету

Проверьте, что JetKVM имеет сетевое подключение:

# Проверить наличие IP-адреса
ip addr

# Проверить маршрут по умолчанию
ip route

# Проверить доступ в интернет
ping -c 3 8.8.8.8

Логи пропали после перезагрузки

Логи хранятся в /tmp/, который очищается при перезагрузке. Это нормальное поведение. Если нужно сохранять логи между перезагрузками, измените путь в init-скриптах:

LOGFILE=/userdata/tuna/tuna-http.log
warning

Частая запись логов может сократить ресурс хранилища. Используйте это только для временной отладки.

Туннель работал и перестал

  1. Проверьте логи на наличие ошибок
  2. Убедитесь, что токен всё ещё действителен в личном кабинете
  3. Проверьте, что скрипт в /userdata/init.d/ на месте и исполняем:
ls -la /userdata/init.d/S9*tuna*

Ссылки


Мониторинг доступности

Помимо туннелей для удалённого доступа, вы можете настроить мониторинг доступности JetKVM. Heartbeat-мониторинг будет отправлять вам уведомления, если устройство потеряет связь с интернетом или перестанет отвечать.

Полная инструкция: Мониторинг доступности JetKVM