Codex 6a1ee64b14 feat(run): sync UltraVNC password from run.bat + clearer noVNC errors
- run.bat: call scripts/ensure-vnc-password.ps1 so the password typed once
  also sets the UltraVNC service VNC password (UAC only when it differs);
  skip --spawn when 5900 is already taken (don't launch a 2nd winvnc).
- scripts/ensure-vnc-password.ps1: new - compares the password against
  %ProgramData%\UltraVNC\ultravnc.ini (reverse-engineered UltraVNC DES
  obfuscation) and only writes+restarts the service when it differs.
- scripts/set-vnc-password.bat: set UltraVNC service VNC password as admin
  (createpassword/setpasswd + verify, GUI fallback). Manual fallback.
- internal/server/static/vnc.html: don't let the generic 'disconnect'
  banner overwrite the specific 'securityfailure' reason (noVNC's
  disconnect event has no reason field, so it always said 'unknown').
- AGENTS.md: document the above (scripts, run.bat, UltraVNC service note).
2026-07-30 19:15:09 +03:00

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. Он делает всё сам и спрашивает только пароль:

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)

# 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)

./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)

Переопределить авто-поиск своей командой:

.\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-релея

Сгенерировать хэш пароля

web-vnc --gen-hash "ваш-пароль"
# напечатает, например:  pbkdf2-sha256$120000$<соль>$<ключ>

Формат самодокументируемый: pbkdf2-sha256$<итерации>$<base64-соль>$<base64-ключ>.

Запуск одной строкой

.\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 блокирует входящие подключения. Один раз выполните от имени администратора:

    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                    запуск в один клик (спрашивает только пароль)

Кросс-компиляция под другую ОС

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.

S
Description
No description provided
Readme
189 KiB
Languages
Go 46.7%
Batchfile 24%
PowerShell 19.4%
HTML 8.2%
Shell 1.7%