docs: README - SSH terminal, sending to other machines, PIN, passkeys, WebSocket transport, Cloudflare and nginx notes
This commit is contained in:
1 parent
18d6684c55
commit
467bd3cb4f
1 file changed
+96
-40
@@ -1,74 +1,130 @@
|
|||||||
# LPRA - LongPolling Remote Access
|
# LPRA - LongPolling Remote Access
|
||||||
|
|
||||||
Лёгкий self-hosted сервис для доступа к Linux-машинам **без белого IP** (за NAT).
|
Лёгкий self-hosted сервис для доступа к Linux-машинам **без белого IP** (за NAT).
|
||||||
Агент на клиенте сам исходяще подключается к серверу по HTTP long-polling; админ
|
Агент на клиенте сам исходяще подключается к серверу - по WebSocket, а где он не проходит,
|
||||||
в веб-панели видит список машин и открывает по каждой **терминал в браузере** (по умолчанию от root, пользователя можно сменить).
|
по HTTP long-polling. В веб-панели видно список машин, и по каждой есть терминал в браузере,
|
||||||
|
файловый менеджер, проброс портов и статус.
|
||||||
|
|
||||||
```
|
```
|
||||||
браузер ──WS──> [nginx :8445 TLS] ──> [Go server 127.0.0.1:8091] <──long-poll── агент (root, за NAT)
|
браузер ──HTTPS/WS──> [Cloudflare] ──> [nginx TLS] ──> [Go server 127.0.0.1:8091] <──WS / long-poll── агент (за NAT)
|
||||||
│
|
│ ▲
|
||||||
SQLite
|
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 минут после ввода.
|
||||||
|
Пять неверных попыток - выход из сессии.
|
||||||
|
|
||||||
## Компоненты
|
## Компоненты
|
||||||
- **Сервер** (`cmd/server`) - админ-панель + точка встречи для агентов. За nginx (TLS).
|
- **Сервер** (`cmd/server`) - панель и точка встречи для агентов. Ставится за nginx (TLS).
|
||||||
- **Агент** (`cmd/agent`) - Linux-бинарь, systemd-сервис от root, PTY по запросу.
|
- **Агент** (`cmd/agent`) - бинарь для Linux (amd64/arm64), systemd-сервис от root.
|
||||||
- **install.sh** - интерактивный установщик, отдаётся сервером как статика.
|
- **install.sh** - установщик агента, отдаётся сервером.
|
||||||
|
|
||||||
## Сборка
|
## Сборка
|
||||||
```sh
|
```sh
|
||||||
./build.sh
|
./build.sh
|
||||||
```
|
```
|
||||||
Получаются `bin/lpra-server` и `dist/agent-linux-{amd64,arm64}`.
|
Получаются `bin/lpra-server*` и `dist/agent-linux-{amd64,arm64}`.
|
||||||
|
Версия задаётся в одном месте - `VERSION.txt`, она вшивается в сервер и агент.
|
||||||
|
|
||||||
## Запуск сервера
|
## Запуск сервера
|
||||||
```sh
|
```sh
|
||||||
./bin/lpra-server -addr 127.0.0.1:8091 -dist ./dist -db ./lpra.db
|
./bin/lpra-server -addr 127.0.0.1:8091 -dist ./dist -db ./lpra.db
|
||||||
```
|
```
|
||||||
Поставьте перед ним nginx по `deploy/nginx.conf.example` (TLS, long-poll/WS).
|
| Флаг | Переменная | По умолчанию | |
|
||||||
Локально без TLS можно добавить `-insecure-cookies`.
|
|---|---|---|---|
|
||||||
|
| `-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 |
|
||||||
|
|
||||||
Первый запуск: откройте `/setup` - мастер создаст администратора. Дальше `/settings`:
|
Пример юнита - `deploy/lpra-server.service`.
|
||||||
адрес хоста (по умолчанию `https://localhost:8445`), enroll-секрет и готовая
|
|
||||||
команда установки.
|
|
||||||
|
|
||||||
## Установка агента на клиент
|
Первый запуск: откройте `/setup` - мастер создаст администратора. Дальше в `/settings`:
|
||||||
Одной командой (под root):
|
адрес хоста, enroll-секрет, короткий код установки и готовая команда.
|
||||||
```sh
|
|
||||||
curl -fsSL https://localhost:8445/install.sh | sudo HOST=https://localhost:8445 ENROLL_SECRET=<секрет> sh
|
### 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
|
||||||
|
Работает за проксирующим Cloudflare: WebSocket он пропускает, файлы идут кусками меньше лимита
|
||||||
|
на запрос, статика с хешем в имени кэшируется. Чтобы панель видела настоящие IP (ограничение
|
||||||
|
попыток входа и кода установки считается по адресу), включите в nginx real IP:
|
||||||
|
```nginx
|
||||||
|
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 букв и цифр, кнопка «Новый код» меняет его):
|
||||||
```sh
|
```sh
|
||||||
curl -fsSL https://localhost:8445/install.sh | sudo sh
|
curl -fsSL https://panel.example/i/<код> | sudo sh
|
||||||
```
|
```
|
||||||
|
Или полной:
|
||||||
|
```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` идёт через адрес панели (long-poll),
|
Правила создаются на странице машины «Порты». Режим `http` идёт через адрес панели,
|
||||||
режимы `tcp`/`udp` - через прямой туннель: агент держит исходящее соединение к порту туннеля хоста
|
`tcp`/`udp` - через прямой туннель: агент держит исходящее соединение к порту туннеля хоста,
|
||||||
(флаг `-tunnel` / `LPRA_TUNNEL_ADDR`).
|
по нему мультиплексируются все соединения.
|
||||||
|
|
||||||
Порт туннеля и хост, к которому для него подключаются агенты (например, IP вместо домена панели),
|
Порт туннеля и хост для агентов меняются в `/settings` → «Прямой туннель»; агенты забирают их
|
||||||
меняются в `/settings` → «Прямой туннель». Агенты забирают адрес с панели сами - при подключении и
|
сами, переустановка не нужна. Локальные `TUNNEL_ADDR` / `TUNNEL_PORT` в окружении агента имеют
|
||||||
при каждом опросе, переустановка не нужна. Локальные `TUNNEL_ADDR` / `TUNNEL_PORT` в окружении агента
|
приоритет (`TUNNEL_ADDR=off` отключает туннель).
|
||||||
имеют приоритет над настройкой панели (`TUNNEL_ADDR=off` отключает туннель).
|
|
||||||
|
|
||||||
Если на хосте активен ufw и сервер запущен от root, панель сама открывает порт хоста правила
|
Если на хосте активен ufw и сервер запущен от root, панель сама открывает порты правил и порт
|
||||||
(tcp для `http`/`tcp`, udp для `udp`) и порт туннеля для прямых режимов, а при удалении правила - закрывает.
|
туннеля, а при удалении правила закрывает. При `ProtectSystem=strict` в юните нужно
|
||||||
При `ProtectSystem=strict` в systemd-юните ufw нужны права на запись: `ReadWritePaths=/etc/ufw /run`.
|
`ReadWritePaths=/etc/ufw /run`.
|
||||||
|
|
||||||
## Версия
|
|
||||||
Версия задаётся в одном месте - `VERSION.txt`; файл вшивается в сервер и агент при сборке.
|
|
||||||
|
|
||||||
## Пользователь терминала
|
## Пользователь терминала
|
||||||
По умолчанию терминал открывается от пользователя, под которым запущен агент (на Linux - root).
|
По умолчанию терминал открывается от пользователя агента (root). В «Изменить машину» можно выбрать
|
||||||
В окне «Изменить машину» можно выбрать другого пользователя машины или создать нового
|
другого пользователя или создать нового (с паролем, по желанию с sudo) - если агент запущен от root.
|
||||||
(имя, пароль, опционально - добавить в sudo). Смена и создание работают, только если агент запущен от root.
|
Файловый менеджер работает от пользователя агента.
|
||||||
Файловый менеджер по-прежнему работает от пользователя агента.
|
|
||||||
|
|
||||||
## Безопасность
|
## Безопасность
|
||||||
- Пароли админов - bcrypt; сессии в HttpOnly/Secure/SameSite куках; CSRF на формах; rate-limit логина.
|
- Пароли админов - bcrypt; сессии в HttpOnly/Secure/SameSite куках; CSRF на формах;
|
||||||
- Агенты: общий enroll-секрет → индивидуальный токен (хранится хешем, сверка constant-time).
|
ограничение попыток входа, PIN и кода установки.
|
||||||
- Открытие терминала пишется в таблицу `audit` (терминал даёт root-доступ), как и смена/создание пользователя.
|
- Ключи доступа (WebAuthn) - вход без пароля; пароль можно выключить совсем.
|
||||||
|
- Агенты: общий enroll-секрет → индивидуальный токен (хранится хешем, сверка constant-time);
|
||||||
|
туннель авторизуется HMAC от токена.
|
||||||
|
- SSH-пароли и ключи передаются агенту один раз для подключения и нигде не сохраняются.
|
||||||
|
- Открытие терминала и SSH, перенос файлов на другие машины, смена пользователя пишутся в `audit`.
|
||||||
|
|
||||||
## Скриншоты
|
## Скриншоты
|
||||||
<img width="1256" height="709" alt="image" src="https://github.com/user-attachments/assets/5f0b30f1-d5e3-4b62-817e-4041641df6e3" />
|
<img width="1256" height="709" alt="image" src="https://github.com/user-attachments/assets/5f0b30f1-d5e3-4b62-817e-4041641df6e3" />
|
||||||
|
|||||||
Reference in new issue
Block a user