feat: web-vnc single-binary browser VNC gateway with password access
- Go stdlib-only gateway: serves noVNC client, password auth, WS<->TCP relay - auth: PBKDF2-HMAC-SHA256 password hash, HMAC session cookies, login rate limit - relay: hand-written RFC 6455 WebSocket + transparent RFB bridge - vncspawner: cross-OS VNC server detection/launch (Windows/Linux/macOS) - server: /login /logout /vnc /api/status routes, session middleware, embed.FS - scripts: get-novnc, get-vnc (zip-verified), list-ips, open-firewall - run.bat: one-click launcher (asks only password), lists access IPs - README/AGENTS.md (Russian), .gitignore (root-anchored binaries)
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
# web-vnc
|
||||
|
||||
Один бинарник, который открывает доступ к рабочему столу (VNC) через браузер
|
||||
(noVNC) с защитой по **паролю**. Программа `web-vnc`:
|
||||
|
||||
- раздаёт HTML5-клиент noVNC,
|
||||
- пускает зрителя по общему паролю,
|
||||
- прокидывает WebSocket браузера к локальному VNC-серверу (RFB поверх WS),
|
||||
- и (по желанию) сама запускает VNC-сервер.
|
||||
|
||||
Написано на чистом Go, **только стандартная библиотека** — никаких внешних
|
||||
Go-модулей, собирается офлайн.
|
||||
|
||||
```
|
||||
Браузер (noVNC) ──ws──▶ web-vnc (один бинарник) ──tcp RFB──▶ VNC-сервер (127.0.0.1:5900)
|
||||
├─ вход по паролю (PBKDF2-HMAC-SHA256)
|
||||
├─ HMAC session-cookie
|
||||
└─ прозрачный релей WS↔TCP
|
||||
```
|
||||
|
||||
## Самый простой запуск (Windows) — одним батником
|
||||
|
||||
В папке проекта есть `run.bat`. Он делает всё сам и спрашивает **только пароль**:
|
||||
|
||||
```bat
|
||||
run.bat
|
||||
```
|
||||
|
||||
Что делает `run.bat`:
|
||||
1. Если `web-vnc.exe` отсутствует — собирает его (нужен установленный Go).
|
||||
Если Go нет — подсказывает, что сделать.
|
||||
2. Если noVNC-клиент ещё не встроен — пытается его скачать (нужен интернет).
|
||||
Без интернета продолжит работу, но в браузере будет страница-заглушка.
|
||||
3. Спрашивает пароль доступа.
|
||||
4. Генерирует из него хэш.
|
||||
5. Ищет VNC-сервер: если он не установлен — пытается скачать портативный
|
||||
UltraVNC (`scripts\get-vnc.ps1`) и запускать его.
|
||||
6. Выводит список всех адресов для подключения (`http://localhost:8080` и
|
||||
`http://<каждый IPv4 машины>:8080`) и запускает сервер с флагом `--spawn`.
|
||||
|
||||
После запуска откройте в браузере один из выведенных адресов,
|
||||
введите тот же пароль — и попадёте на рабочий стол.
|
||||
|
||||
## Быстрый старт вручную (Windows)
|
||||
|
||||
```powershell
|
||||
# 1. Подтянуть noVNC-клиент во встроенную статику (один раз)
|
||||
.\scripts\get-novnc.ps1
|
||||
|
||||
# 2. Собрать бинарник
|
||||
go build -o web-vnc.exe .\cmd\web-vnc
|
||||
|
||||
# 3. Сгенерировать хэш пароля
|
||||
$hash = .\web-vnc.exe --gen-hash "ваш-пароль"
|
||||
|
||||
# 4. Запустить (сам найдёт/запустит VNC-сервер и поднимется на :8080)
|
||||
.\web-vnc.exe --password-hash $hash --spawn
|
||||
```
|
||||
|
||||
## Быстрый старт (Linux / macOS)
|
||||
|
||||
```bash
|
||||
./scripts/get-novnc.sh
|
||||
go build -o web-vnc ./cmd/web-vnc
|
||||
hash=$(./web-vnc --gen-hash "ваш-пароль")
|
||||
./web-vnc --password-hash "$hash" --spawn
|
||||
```
|
||||
|
||||
## Как это работает
|
||||
|
||||
1. Пользователь открывает `http://хост:8080/` и попадает на `/login`.
|
||||
2. Вводит общий пароль. Он сверяется с **солёным PBKDF2-HMAC-SHA256**-хэшем
|
||||
(120 000 итераций). При успехе сервер ставит **HMAC-подписанную,
|
||||
HttpOnly**-куку сессии (по умолчанию 8 ч, без серверного хранилища).
|
||||
3. Браузер загружает `/vnc.html` (только с валидной сессией). Клиент noVNC
|
||||
открывает WebSocket на `/vnc`.
|
||||
4. `web-vnc` проверяет сессию **до** апгрейда WebSocket и затем прозрачно
|
||||
релеит байты между WebSocket и локальным VNC-сервером
|
||||
(`127.0.0.1:5900` по умолчанию). RFB-протокол проходит нетронутым,
|
||||
как у `websockify`.
|
||||
|
||||
> VNC-сервер должен слушать на **loopback (127.0.0.1)** и может работать
|
||||
> **без VNC-пароля** — защита по паролю теперь на веб-гейтвее. Если же ваш
|
||||
> VNC-сервер требует свой пароль, noVNC спросит и его.
|
||||
|
||||
## Авто-запуск VNC-сервера (`--spawn`)
|
||||
|
||||
С `--spawn` программа ищет установленный VNC-сервер и запускает его как
|
||||
дочерний процесс (отдельно, без окна на Windows), после чего подключается к нему.
|
||||
|
||||
| ОС | Что ищет |
|
||||
|---------|-------------------------------------------------------|
|
||||
| Windows | UltraVNC (`winvnc.exe`), TightVNC (`tvnserver.exe`) |
|
||||
| Linux | `x11vnc`, `tigervncserver` / `Xvnc` |
|
||||
| macOS | встроенный Screen Sharing (`kickstart`) |
|
||||
|
||||
Переопределить авто-поиск своей командой:
|
||||
|
||||
```powershell
|
||||
.\web-vnc.exe --password-hash $hash --spawn-command "C:\Path\To\winvnc.exe -run"
|
||||
```
|
||||
|
||||
Без `--spawn` убедитесь, что VNC-сервер уже слушает по адресу из `--vnc`.
|
||||
|
||||
## Настройка
|
||||
|
||||
Все флаги дублируются переменными окружения (`WEBVNC_*`).
|
||||
|
||||
| Флаг | Env | По умолчанию | Описание |
|
||||
|--------------------|---------------------------|--------------------|------------------------------------------------|
|
||||
| `--listen` | `WEBVNC_LISTEN` | `:8080` | адрес HTTP/WS |
|
||||
| `--vnc` | `WEBVNC_VNC` | `127.0.0.1:5900` | адрес вышестоящего VNC-сервера |
|
||||
| `--password-hash` | `WEBVNC_PASSWORD_HASH` | (обязателен) | хэш PBKDF2 из `--gen-hash` |
|
||||
| `--session-secret` | `WEBVNC_SESSION_SECRET` | случайный при старте | HMAC-ключ для подписи куки сессии |
|
||||
| `--session-ttl` | `WEBVNC_SESSION_TTL` | `8h` | время жизни куки сессии |
|
||||
| `--spawn` | `WEBVNC_SPAWN` | false | авто-запуск найденного VNC-сервера |
|
||||
| `--spawn-command` | `WEBVNC_SPAWN_COMMAND` | (нет) | явная команда запуска VNC-сервера |
|
||||
| `--web-root` | `WEBVNC_WEB_ROOT` | (встроенные) | раздавать статику с диска вместо embed |
|
||||
| `--novnc-path` | | `/vnc.html` | путь страницы noVNC-клиента |
|
||||
| `--relay-path` | | `/vnc` | endpoint WebSocket-релея |
|
||||
|
||||
### Сгенерировать хэш пароля
|
||||
|
||||
```bash
|
||||
web-vnc --gen-hash "ваш-пароль"
|
||||
# напечатает, например: pbkdf2-sha256$120000$<соль>$<ключ>
|
||||
```
|
||||
|
||||
Формат самодокументируемый:
|
||||
`pbkdf2-sha256$<итерации>$<base64-соль>$<base64-ключ>`.
|
||||
|
||||
### Запуск одной строкой
|
||||
|
||||
```powershell
|
||||
.\web-vnc.exe --password-hash (. \web-vnc.exe --gen-hash "secret") --spawn
|
||||
```
|
||||
|
||||
## Безопасность
|
||||
|
||||
- **Без TLS**: рассчитано на **приватную сеть**. Если выставляете наружу —
|
||||
поставьте перед ним reverse-proxy с TLS (nginx/caddy).
|
||||
- Логин с лимитом попыток (5 в минуту на IP, в памяти).
|
||||
- Кука сессии подписана `--session-secret`. Задайте фиксированный
|
||||
`--session-secret`, чтобы сессии переживали перезапуск (иначе секрет
|
||||
меняется при каждом старте, и старые сессии инвалидируются).
|
||||
- VNC-сервер держите на `127.0.0.1`, чтобы до него нельзя было достучаться
|
||||
в обход гейтвея.
|
||||
|
||||
|
||||
## Устранение неполадок
|
||||
|
||||
- **После ввода пароля — ошибка / нет картинки.**
|
||||
Значит, веб-гейтвей не смог подключиться к VNC-серверу (по адресу `--vnc`,
|
||||
по умолчанию `127.0.0.1:5900`). На странице теперь показывается понятное
|
||||
сообщение вместо криптографической ошибки noVNC.
|
||||
Решение: должен работать VNC-сервер, который отдаёт рабочий стол:
|
||||
- Windows: установите **UltraVNC** или **TightVNC**, либо запустите
|
||||
`scripts\get-vnc.ps1` (скачает портативный UltraVNC в папку `vnc\`, и
|
||||
`run.bat` сам его запустит).
|
||||
- Linux: `x11vnc` или `TigerVNC`.
|
||||
- macOS: встроенный Screen Sharing.
|
||||
Важно: VNC-сервер должен слушать на `127.0.0.1:5900`. На Windows для захвата
|
||||
экрана может потребоваться запуск от имени администратора (UAC).
|
||||
|
||||
- **С другого компьютера страница не открывается (таймаут/недоступно).**
|
||||
По умолчанию Windows Firewall блокирует входящие подключения. Один раз
|
||||
выполните от имени администратора:
|
||||
```bat
|
||||
scripts\open-firewall.bat
|
||||
```
|
||||
Это откроет входящий TCP-порт 8080. (`run.bat` выводит адреса и подсказку.)
|
||||
|
||||
- **noVNC-клиент не встроен (страница-заглушка).**
|
||||
Запустите `scripts\get-novnc.ps1` (нужен интернет), пересоберите и перезапустите.
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
cmd/web-vnc/main.go точка входа CLI: флаги, спавн VNC, запуск сервера
|
||||
internal/config конфигурация (флаги + env)
|
||||
internal/auth хэш пароля PBKDF2, HMAC-куки сессии, rate-limit
|
||||
internal/relay WebSocket на stdlib (RFC 6455) + мост WS↔TCP
|
||||
internal/vncspawner кросс-ОС поиск и запуск VNC-сервера
|
||||
internal/server HTTP-роуты, middleware сессии, встроенная статика
|
||||
internal/server/static встроенные веб-ассеты (vnc.html + core/app/vendor noVNC)
|
||||
scripts/get-novnc.{ps1,sh} скачать noVNC во встроенную статику
|
||||
scripts/get-vnc.ps1 скачать портативный VNC-сервер (UltraVNC) в папку vnc/
|
||||
scripts/list-ips.ps1 список IPv4 машины (используется run.bat)
|
||||
scripts/open-firewall.bat открыть порт 8080 в Windows Firewall (один раз, от админа)
|
||||
run.bat запуск в один клик (спрашивает только пароль)
|
||||
```
|
||||
|
||||
## Кросс-компиляция под другую ОС
|
||||
|
||||
```bash
|
||||
GOOS=linux GOARCH=amd64 go build -o web-vnc-linux ./cmd/web-vnc
|
||||
GOOS=windows GOARCH=amd64 go build -o web-vnc.exe ./cmd/web-vnc
|
||||
GOOS=darwin GOARCH=arm64 go build -o web-vnc-macos ./cmd/web-vnc
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT. Встроенные ассеты noVNC сохраняют свою лицензию (MPL-2.0) —
|
||||
см. `internal/server/static/LICENSE` после запуска `get-novnc`.
|
||||
Reference in New Issue
Block a user