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)
Возможности
- Одна страница - Машины, WorkSpace (терминалы и файлы), Настройки; переходы без перезагрузки. Вкладки WorkSpace живут, пока открыта вкладка браузера: уход на другие страницы их не закрывает, перезагрузка сбрасывает (браузер сначала спросит). «Порты», «Статус» и «Изменить» машины открываются окном поверх текущей страницы - из списка машин или правым кликом по заголовку вкладки или панели.
- WorkSpace - вкладки на нескольких машинах сразу, до четырёх панелей в одной вкладке - терминал или файловый менеджер (перетаскиванием, с изменяемыми размерами), переименование вкладок. На телефоне - панель клавиш (Esc, Ctrl, Alt, стрелки и т.д.).
- SSH-терминал - в меню «+»: агент открывает SSH-соединение к другому хосту (по паролю,
ключу - можно загрузить файл - или своими ключами из
~/.ssh), шелл открывается во вкладке, а шаги подключения видны в статусе. Ключ хоста запоминается при первом входе (ssh_known_hostsрядом сstate.jsonагента); если он сменится, вход откажет. - Файлы - просмотр, редактор с подсветкой, hex-редактор, предпросмотр картинок, видео, PDF, баз SQLite с правкой (значение по клику, добавление и удаление строк, вкладка SQL; агент меняет базу через сам SQLite, так что работающая база не ломается); загрузка и скачивание до 512 МБ кусками по 4 МБ (проходит через Cloudflare); свойства (chmod/chown), zip/unzip.
- Файлы по SSH - в меню «+» › «Файловый менеджер» › «SSH-хост…»: машина подключается к хосту
и работает с файлами командами по SSH, SFTP не нужен. Есть всё, кроме редактора SQLite;
zip и распаковка - если на хосте установлены
zipиunzip. Оборвалось подключение - панель предложит переподключиться. - Перетаскивание файлов - строку (или все отмеченные) в другую файловую панель или в папку в ней: окно «Переместить / Копировать» у места броска, между машинами и SSH-хостами - с прогрессом. В терминал той же машины или того же SSH-хоста - вставляется путь в кавычках, в терминал другой - нельзя, с подсказкой почему. Задержать над заголовком вкладки - она откроется. Только на компьютере.
- Перемещение и копирование - внутри машины или, кнопкой «сеть» в том же окне, на другую машину или на 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.