autostart-run.bat checked 5900 once at startup; at boot the UltraVNC service may not be up yet, so web-vnc either spawned a 2nd VNC server (conflict) or started serving on :8080 before the VNC server was ready, breaking browser connections. Now autostart-run.bat polls 127.0.0.1:5900 for up to ~30s (one PowerShell loop: TcpClient + Start-Sleep 1, exits on first accepted connection) and only then launches web-vnc.exe -- without --spawn when 5900 already listens, or spawning a local server (portable vnc\winvnc.exe / tvnserver.exe, else auto-detect) if it never came up. Docs (AGENTS.md, README.md) updated.
239 lines
15 KiB
Markdown
239 lines
15 KiB
Markdown
# 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` поднимает
|
||
gateway без запросов.
|
||
|
||
1. **Один раз** задайте пароль:
|
||
```bat
|
||
set-password.bat
|
||
```
|
||
Спросит пароль доступа, сгенерирует хэш и сохранит его вместе с паролем в
|
||
`webvnc.conf` (секрет, в git не коммитится), а затем синхронизирует им пароль
|
||
UltraVNC-сервиса (`scripts\ensure-vnc-password.ps1`, UAC только при смене).
|
||
Веб-пароль оказывается настроен, даже если синк UltraVNC упадёт или UAC отменят.
|
||
Используйте ASCII-пароль длиной до 8 символов (VNC-пароль = первые 8 байт).
|
||
|
||
2. **Запустите gateway**:
|
||
```bat
|
||
run.bat
|
||
```
|
||
`run.bat` собирает `web-vnc.exe` (нужен Go, если бинарника нет), встраивает
|
||
noVNC-клиент (качает при наличии интернета), находит/докачивает VNC-сервер,
|
||
читает пароль из `webvnc.conf` (больше не спрашивает), печатает адреса
|
||
(`http://localhost:8080` и `http://<каждый IPv4 машины>:8080`) и запускает
|
||
сервер с флагом `--spawn`. Если `127.0.0.1:5900` уже слушает (сервис UltraVNC) —
|
||
запускается без `--spawn`, подключаясь к существующему серверу.
|
||
|
||
После запуска откройте в браузере один из выведенных адресов,
|
||
введите тот же пароль — и попадёте на рабочий стол.
|
||
|
||
### Автостарт при загрузке ОС (вместо ручного `run.bat`)
|
||
|
||
```bat
|
||
set-password.bat :: один раз: пароль -> webvnc.conf
|
||
install-autostart.bat :: один раз (от админа): плановое задание «web-vnc»
|
||
```
|
||
`install-autostart.bat` регистрирует через `schtasks` задание (`ONSTART`, `SYSTEM`,
|
||
`HIGHEST`), которое при загрузке запускает `scripts\autostart-run.bat` —
|
||
неинтерактивный launcher, читающий `webvnc.conf` и не качающий ничего на старте.
|
||
Запустить сразу без перезагрузки: `schtasks /Run /TN "web-vnc"`.
|
||
Снять автостарт: `uninstall-autostart.bat` (от админа).
|
||
|
||
**Смена пароля:** `web-vnc.exe` читает пароль из `webvnc.conf` только при старте и не
|
||
перечитывает его в runtime. Поэтому `set-password.bat` (если задание «web-vnc» установлено)
|
||
перезапускает его через `restart-autostart.bat` (с elevation) — тот проверяет,
|
||
запущен ли `web-vnc.exe` (если да — стоп + старт, если нет — только старт). Рестарт
|
||
пропускается, если пароль не изменился. Для интерактивного `run.bat` просто перезапустите
|
||
его вручную.
|
||
|
||
## Быстрый старт вручную (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
|
||
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)
|
||
|
||
Пользовательские точки входа — в корне репозитория:
|
||
run.bat интерактивный запуск: собирает бинарник/noVNC/VNC, читает пароль из webvnc.conf (не спрашивает)
|
||
set-password.bat разовая настройка пароля -> webvnc.conf (хэш + VNC-пароль); синхронизирует UltraVNC
|
||
install-autostart.bat автостарт при загрузке ОС (schtasks ONSTART/SYSTEM) — от админа
|
||
uninstall-autostart.bat снять автостарт — от админа
|
||
restart-autostart.bat перезапустить задание «web-vnc» (чтобы применить новый пароль) — от админа
|
||
open-firewall.bat открыть порт 8080 в Windows Firewall (один раз, от админа)
|
||
webvnc.conf секрет: HASH= + VNC_PASSWORD= (создаётся set-password.bat, gitignored)
|
||
|
||
Внутренние помощники — в scripts/ (вызываются другими скриптами):
|
||
scripts/autostart-run.bat неинтерактивный launcher для автостарта: читает webvnc.conf, ждёт 5900 до ~30 c, затем запускает web-vnc (без --spawn, если VNC уже слушает)
|
||
scripts/ensure-vnc-password.ps1 синхронизирует VNC-пароль UltraVNC (вызывается из set-password.bat)
|
||
scripts/get-novnc.{ps1,sh} скачать noVNC во встроенную статику
|
||
scripts/get-vnc.ps1 скачать портативный VNC-сервер (UltraVNC) в папку vnc/
|
||
scripts/list-ips.ps1 список IPv4 машины (используется 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`. |