2026-10-15 16:38:23 +03:00
2026-10-15 16:38:23 +03:00
2026-10-13 00:36:19 +03:00
2026-10-13 00:24:16 +03:00

LPRA - LongPolling Remote Access

Лёгкий self-hosted сервис для доступа к Linux-машинам без белого IP (за NAT). Агент на клиенте сам исходяще подключается к серверу - по WebSocket, а где он не проходит, по HTTP long-polling. В веб-панели видно список машин, и по каждой есть терминал в браузере, файловый менеджер, проброс портов и статус.

браузер ──HTTPS/WS──> [Cloudflare] ──> [nginx TLS] ──> [Go server 127.0.0.1:8091] <──WS / long-poll── агент (за NAT)
                                                              │         ▲
                                                            SQLite      └──── прямой TCP/UDP-туннель :8446 (проброс tcp/udp)

Возможности

  • Терминал - вкладки на нескольких машинах сразу, до четырёх терминалов в одной вкладке (перетаскиванием, с изменяемыми размерами), переименование вкладок. На телефоне - панель клавиш (Esc, Ctrl, Alt, стрелки и т.д.).
  • SSH-терминал - в меню «+»: агент открывает SSH-соединение к другому хосту (по паролю, ключу - можно загрузить файл - или своими ключами из ~/.ssh), шелл открывается во вкладке, а шаги подключения видны в статусе. Ключ хоста запоминается при первом входе (ssh_known_hosts рядом с state.json агента); если он сменится, вход откажет.
  • Файлы - просмотр, редактор с подсветкой, hex-редактор, предпросмотр картинок, видео, PDF, таблиц SQLite; загрузка и скачивание до 512 МБ кусками по 4 МБ (проходит через Cloudflare); свойства (chmod/chown), zip/unzip.
  • Перемещение и копирование - внутри машины или, кнопкой «сеть» в том же окне, на другую машину или на SSH-хост: папка уходит потоком tar без промежуточных файлов, с прогрессом, скоростью и отменой. «Переместить» удаляет исходник после успешной отправки. Правила для существующего: сообщить / заменить / оставить как есть.
  • Проброс портов - http через панель, tcp/udp через прямой туннель.
  • Статус - нагрузка, память, диски, сеть, тест скорости канала до сервера.
  • Вход - пароль и/или ключи доступа (passkeys); пароль можно выключить, когда есть ключ.
  • PIN-код (необязательный, 4 цифры) - спрашивается в каждой новой вкладке браузера, но не при перезагрузке. На устройстве можно не спрашивать его 5/15/30/60 минут после ввода. Пять неверных попыток - выход из сессии.

Пользователи и роли

  • Администратор видит всё: все машины, настройки, пользователей, ufw.
  • Пользователь видит только выданные ему машины (терминал, файлы, порты, статус, «Изменить» без удаления); настроек, других пользователей и чужих машин для него нет.

Роль и машины задаются в «Настройки → Пользователи» (квадратная кнопка у пользователя открывает список машин). Там же галочка «Пароль» включает или выключает вход по паролю (выключить можно, когда у человека есть ключ доступа), а «Закрепить» не даёт ему поменять это самому. Последнего администратора нельзя удалить или понизить.

Компоненты

  • Сервер (cmd/server) - панель и точка встречи для агентов. Ставится за nginx (TLS).
  • Агент (cmd/agent) - бинарь для Linux (amd64/arm64), systemd-сервис от root.
  • install.sh - установщик агента, отдаётся сервером.

Сборка

./build.sh

Получаются bin/lpra-server* и dist/agent-linux-{amd64,arm64}. Версия задаётся в одном месте - VERSION.txt, она вшивается в сервер и агент.

Запуск сервера

./bin/lpra-server -addr 127.0.0.1:8091 -dist ./dist -db ./lpra.db
Флаг Переменная По умолчанию
-addr LPRA_ADDR 127.0.0.1:8091 адрес HTTP (за nginx)
-db LPRA_DB lpra.db база SQLite
-dist LPRA_DIST dist бинарники агента для /download/
-tunnel LPRA_TUNNEL_ADDR :8446 прямой туннель TCP+UDP (пусто - выключен)
-insecure-cookies LPRA_INSECURE_COOKIES=1 куки без Secure - только для локального запуска без TLS

Пример юнита - deploy/lpra-server.service.

Первый запуск: откройте /setup - мастер создаст администратора. Дальше в /settings: адрес хоста, enroll-секрет, короткий код установки и готовая команда.

nginx

Конфиг - deploy/nginx.conf.example. Обязательно:

  • заголовки Upgrade / Connection (через map $http_upgrade $connection_upgrade) - без них WebSocket не пройдёт, и агенты и терминалы останутся на long-polling (работает, но медленнее на больших выводах);
  • proxy_read_timeout больше 25 с (удержание long-poll), proxy_buffering off.

Прямой адрес (без Cloudflare)

В «Настройки → Адрес хоста» можно задать второй, прямой адрес - например https://direct.lpra.example.com, DNS-запись без проксирования. Тогда:

  • агенты, не достучавшись до основного адреса 30 секунд, переходят на прямой и каждые 5 минут проверяют, не ожил ли основной; уже установленные агенты получают прямой адрес с сервера сами (он хранится в state.json);
  • установщик берёт адреса с того домена, с которого его скачали: с основного - агент работает через основной с резервом на прямой, с прямого - наоборот (и бинарник качается с прямого); если первый адрес не отвечает, бинарник качается со второго. В «Полных вариантах» есть короткая команда и через прямой адрес;
  • в «Изменить → Связь с сервером» у клиента, подключённого через прямой адрес, после статуса стоит «(direct)»;
  • если хост туннеля не задан, агентам отдаётся хост прямого адреса;
  • ключи доступа основного домена работают и на его поддомене.

Cloudflare

Работает за проксирующим Cloudflare: WebSocket он пропускает, файлы идут кусками меньше лимита на запрос, статика с хешем в имени кэшируется. Чтобы панель видела настоящие IP (ограничение попыток входа и кода установки считается по адресу), включите в nginx real IP:

real_ip_header CF-Connecting-IP;
set_real_ip_from 173.245.48.0/20;   # и остальные диапазоны с https://www.cloudflare.com/ips/

Прямой туннель (:8446) - это не HTTP, через проксирующий Cloudflare он не идёт: в «Настройки → Прямой туннель» укажите для агентов IP хоста или DNS-имя без проксирования.

Установка агента

Короткой командой из /settings (код из 3-24 букв и цифр, кнопка «Новый код» меняет его):

curl -fsSL https://panel.example/i/<код> | sudo sh

Или полной:

curl -fsSL https://panel.example/install.sh | sudo HOST=https://panel.example ENROLL_SECRET=<секрет> sh

Без параметров установщик спросит секрет, а если агент уже стоит - предложит удалить. Обновить агента можно кнопкой в панели («Изменить» → обновить).

Связь агента с сервером

В «Изменить» → «Связь с сервером» для каждой машины:

  • auto (по умолчанию) - WebSocket; если он не подключается, long-polling ровно минуту, потом снова попытка WebSocket;
  • ws - только WebSocket;
  • lp - только long-polling.

Проброс портов и прямой туннель

Правила создаются на странице машины «Порты». Режим http идёт через адрес панели, tcp/udp - через прямой туннель: агент держит исходящее соединение к порту туннеля хоста, по нему мультиплексируются все соединения.

Порт туннеля и хост для агентов меняются в /settings → «Прямой туннель»; агенты забирают их сами, переустановка не нужна. Локальные TUNNEL_ADDR / TUNNEL_PORT в окружении агента имеют приоритет (TUNNEL_ADDR=off отключает туннель).

Если на хосте активен ufw и сервер запущен от root, панель сама открывает порты правил и порт туннеля, а при удалении правила закрывает. При ProtectSystem=strict в юните нужно ReadWritePaths=/etc/ufw /run.

Пользователь терминала

По умолчанию терминал открывается от пользователя агента (root). В «Изменить машину» можно выбрать другого пользователя или создать нового (с паролем, по желанию с sudo) - если агент запущен от root. Файловый менеджер работает от пользователя агента.

Безопасность

  • Пароли админов - bcrypt; сессии в HttpOnly/Secure/SameSite куках; CSRF на формах; ограничение попыток входа, PIN и кода установки.
  • Ключи доступа (WebAuthn) - вход без пароля; пароль можно выключить совсем.
  • Агенты: общий enroll-секрет → индивидуальный токен (хранится хешем, сверка constant-time); туннель авторизуется HMAC от токена.
  • SSH-пароли и ключи передаются агенту один раз для подключения и нигде не сохраняются.
  • Открытие терминала и SSH, перенос файлов на другие машины, смена пользователя пишутся в audit.

Скриншоты

image image image image
S
Description
Создан в GitHub 13 окт., 2026, перенесен 19 окт., 2026
Readme MIT
62 MiB
0 Stars 1 Watchers 0 Forks
Languages
Go 52%
JavaScript 31%
CSS 8.2%
HTML 7.1%
Shell 1.2%
Other 0.5%