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.
19 KiB
19 KiB
AGENTS.md
Руководство для агентов (и людей), работающих с этим репозиторием.
Сопровождение этого файла (ВАЖНО)
- AGENTS.md должен отражать актуальное состояние кода. Это живой документ, а не одноразовая справка.
- При любом изменении, влияющем на: архитектуру, состав пакетов/файлов, флаги
CLI или env-переменные (
WEBVNC_*), протокол/поведение гейтвея, скрипты, кодировки файлов, процесс сборки/запуска или известные ограничения — обязательно обнови соответствующий раздел AGENTS.md в том же коммите. - Если заметил расхождение между AGENTS.md и реальным кодом (функция переименована, файл удалён, флаг изменён и т.п.) — сначала приведи AGENTS.md в соответствие с кодом, затем продолжай работу. Не оставляй устаревшие инструкции.
- Коммит, меняющий поведение/структуру, но не трогающий AGENTS.md при расхождении, считается неполным.
Что это
web-vnc — один Go-бинарник, который открывает доступ к рабочему столу (VNC)
через браузер (noVNC) с защитой по паролю. Гейтвей сам раздаёт noVNC-клиент,
проверяет пароль, ставит HMAC-сессию и прозрачно релеит WebSocket браузера в
TCP VNC-сервера (RFB). Опционально сам находит и запускает VNC-сервер.
Ключевые принципы (НЕ нарушать)
- Только стандартная библиотека Go. Внешних Go-модулей нет и быть не должно —
проект собирается офлайн (в среде сборки нет интернета/Go-proxy).
WebSocket (RFC 6455) и хэш пароля (PBKDF2-HMAC-SHA256) реализованы вручную
в
internal/relayиinternal/auth. - noVNC-клиент скачивается отдельно (
scripts/get-novnc.*) во встроенную статикуinternal/server/static/. Эти папки (core/,app/,vendor/,utils/,novnc-original.html) в git не коммитятся (см..gitignore). Наша собственная обёртка —internal/server/static/vnc.html(коммитится). - Пароль — один общий для всех (по требованию). Веб-пароль (PBKDF2) хранится
как
--password-hash; тот же пароль может передаваться VNC-серверу через--vnc-password/WEBVNC_VNC_PASSWORD, чтобы noVNC авторизовался автоматически (одно поле ввода для пользователя).
Сборка и запуск
# среда без интернета: Go уже установлен, прокси недоступен -> stdlib-only
$env:GOCACHE = "$env:TEMP\go-build" # дефолтный кэш бывает без прав на запись
go build -o web-vnc.exe ./cmd/web-vnc
.\web-vnc.exe --gen-hash "пароль" # напечатает хэш
.\web-vnc.exe --password-hash <хэш> --spawn
Проверка: go vet ./..., gofmt -l internal cmd (должно быть пусто).
Структура
cmd/web-vnc/main.go CLI: флаги, спавн VNC, запуск сервера, --gen-hash
internal/config флаги + env (WEBVNC_*)
internal/auth PBKDF2-HMAC-SHA256, HMAC session-cookie, rate-limit
internal/relay websocket.go — RFC6455 на stdlib; relay.go — WS<->TCP
internal/vncspawner кросс-ОС поиск/запуск VNC-сервера (build-теги по ОС)
internal/server HTTP-роуты, /api/status, middleware сессии, embed.FS
internal/server/static встроенные ассеты (vnc.html, favicon.ico + noVNC core/app/vendor)
Все пользовательские точки входа лежат в корне репозитория:
set-password.bat настройка пароля: спрашивает пароль, генерирует хэш и пишет webvnc.conf (HASH= + VNC_PASSWORD=, gitignored) — ДО синхронизации UltraVNC, чтобы веб-пароль был настроен даже если синк упадёт или UAC отменят; затем синхронизирует VNC-пароль UltraVNC (scripts/ensure-vnc-password.ps1). Если установлено задание «web-vnc» — перезапускает его через restart-autostart.bat (с elevation), чтобы живый gateway перезалил пароль; рестарт пропускается, только если пароль не изменился (PBKDF2-хэш всегда новый из-за соли, поэтому изменение детектится сравнением введённого пароля со старым VNC_PASSWORD из webvnc.conf). Саму проверку «запущен ли web-vnc.exe» делает restart-autostart.bat (elevated, надёжно видит SYSTEM-процесс).
run.bat интерактивный запуск в один клик: собирает бинарник/noVNC/VNC, читает пароль из webvnc.conf (НЕ спрашивает — если файла нет, просит сначала запустить set-password.bat); если 5900 уже занят (сервис UltraVNC) — не порождает второй VNC-сервер, а подключается к существующему
install-autostart.bat регистрирует плановое задание «web-vnc» (schtasks /SC ONSTART /RU SYSTEM /RL HIGHEST), запускающее scripts/autostart-run.bat при загрузке ОС; запускать от админа (самопрос elevation); требует уже созданный webvnc.conf.
uninstall-autostart.bat удаляет плановое задание «web-vnc» (от админа).
restart-autostart.bat перезапускает задание «web-vnc» (от админа, auto-elevate): проверяет tasklist, запущен ли web-vnc.exe — если да, делает schtasks /End + пауза 2 c + schtasks /Run; если нет — только schtasks /Run. Вызывается из set-password.bat, или вручную.
open-firewall.bat открыть порт 8080 в Windows Firewall (от админа, один раз).
webvnc.conf создаётся set-password.bat; содержит HASH=<pbkdf2-хэш> и VNC_PASSWORD=<тот же пароль для noVNC>; читается run.bat и scripts/autostart-run.bat; в git не коммитится (.gitignore).
Внутренние помощники (вызываются другими скриптами, не пользователем) — в scripts/:
scripts/autostart-run.bat НЕинтерактивный launcher для автостарта (нет пауз/запросов): читает webvnc.conf, ПОЛЛИТ 127.0.0.1:5900 до ~30 c (UltraVNC-сервис может ещё подниматься при загрузке) и только потом запускает web-vnc.exe — без --spawn если 5900 уже слушает, иначе спавнит локальный VNC-сервер (portable vnc\\winvnc.exe/tvnserver.exe или авто-детект). Ничего не качает на старте. Вызывается плановым заданием из install-autostart.bat.
scripts/ensure-vnc-password.ps1 синхронизирует VNC-пароль UltraVNC с введённым (пишет только при несовпадении; UAC только при смене) — вызывается из set-password.bat.
scripts/get-novnc.{ps1,sh} скачать noVNC
scripts/get-vnc.ps1 скачать портативный UltraVNC в vnc/ (с верификацией zip)
scripts/list-ips.ps1 список IPv4 машины (используется run.bat)
Соглашения и подводные камни
- Кодировка файлов
.bat: текст всех.bat— English ASCII, окончания строк CRLF, без BOM и безchcp— ASCII читаетсяcmdодинаково при любой OEM-кодировке, так что кириллица вecho/set /p/for /fбольше не нужна. Это намеренный отход от кириллицы в bat-файлах (см. коммит «Translate run.bat and open-firewall.bat to English ASCII»). Исторически кириллица в.batтребовала UTF-8 с BOM +chcp 65001 >nulпосле@echo off(BOM заставлялcmdчитать файл как UTF-8; без BOM +chcp 65001вcmdдавал????, т.к. cmd читает файл по OEM-кодировке, а не поchcp). Альтернатива без BOM — системная опция «Beta: Use Unicode UTF-8 for worldwide language support» (OEM=65001), но она влияет на всю систему (reboot, могут ломаться legacy-приложения под cp866/cp1251). Если когда-нибудь вернёшь кириллицу в.bat— пиши через[System.IO.File]::WriteAllText(path, content -replace "(?<!\r)\n","rn", [Text.UTF8Encoding]::new($true))(с BOM). Для English ASCII — то же, но[Text.UTF8Encoding]::new($false)(без BOM).- Go-файлы: UTF-8 без BOM, LF. То же самое правило — не использоватьSet-Content -Encoding UTF8(ставит BOM). Используй[System.IO.File]::WriteAllText(path, content, [Text.UTF8Encoding]::new($false)). .gitignore: бинарники игнорируются root-anchored (/web-vnc,/web-vnc.exe), иначе шаблонweb-vncловит пакетcmd/web-vnc/.- vncspawner: функции поиска VNC-сервера определены через build-теги
(
detect_windows.go/detect_linux.go/detect_darwin.go), каждый файл определяетfunc candidates() []Candidate. Не ссылаться на платформо-специфичные функции из общих файлов. - Тесты в этой среде: интернет отсутствует; проверяй через локальные
заглушки (TCP-эхо-сервер на 5900,
Get-NetIPAddressчасто пуст —list-ips.ps1имеет фолбэк наipconfig). Запущенный web-vnc.exe из фонового job может остаться «зомби» (Stop-Process иногда access denied); используй разные порты для тестов и по возможности запускай killable-способом. - favicon.ico: браузер запрашивает
/favicon.icoна каждой странице (включая форму логина). Роут/favicon.icoзарегистрирован вне session- middleware (чтобы без сессии не редиректил на /login и не давал 404) и отдаёт встроенныйinternal/server/static/favicon.ico(иконка-монитор, коммитится; не входит в скачиваемые noVNC-ассеты). На логине и vnc.html есть<link rel="icon" href="/favicon.ico">. - Secure context (TLS) в noVNC: noVNC (
core/rfb.js,app/ui.js) печатаетLog.Error("noVNC requires a secure context (TLS). Expect crashes!")при!window.isSecureContext— т.е. на plain-http по LAN-IP. Для обычной VNC- password-авторизации это безобидно: noVNC использует чистый-JS DES изcore/des.jsи НЕ нуждается вcrypto.subtle. Чтобы не пугать пользователя красной ошибкой в консоли, обёрткаvnc.htmlДО импортаcore/rfb.jsоборачиваетconsole.errorфильтром, гасящим только эту строку (всё остальное проходит в оригинальныйconsole.error). Файлы noVNC (core/, app/, …) НЕ правятся — они gitignored и перезаливаютсяscripts/get-novnc.*. Корректный способ сделать контекст действительно secure — HTTPS (self-signed), но это отдельная фича; пока — фильтр в vnc.html. - Hijack WebSocket:
internal/relayсам делает апгрейд черезhttp.Hijacker; гейтвей НЕ использует gorilla/websocket.
Автостарт (запуск при загрузке ОС)
Чтобы gateway поднимался сам при включении машины (а не интерактивно через run.bat),
пароль должен быть настроен заранее и не запрашиваться при старте. Поэтому процедура:
- Один раз запустить
set-password.bat— он спрашивает пароль, генерирует хэш и пишетwebvnc.conf(HASH + VNC_PASSWORD), а уже ПОТОМ синхронизирует VNC-пароль UltraVNC (scripts/ensure-vnc-password.ps1) — так веб-пароль оказывается настроен, даже если синк упадёт или UAC отменят. Файлwebvnc.conf— секрет, в git не коммитится (см..gitignore). - Один раз (от админа) запустить
install-autostart.bat— регистрирует плановое заданиеweb-vncчерезschtasks /Create /SC ONSTART /RU SYSTEM /RL HIGHEST, которое при загрузке ОС запускаетscripts/autostart-run.bat. scripts/autostart-run.bat— неинтерактивный launcher: читаетwebvnc.conf, при необходимости собираетweb-vnc.exe(если есть Go), затем опрашивает127.0.0.1:5900до ~30 c (один PowerShell-проект крутит цикл try/TcpClient +Start-Sleep 1; выходит сразу, как только порт принял соединение) — UltraVNC-сервис при загрузке может ещё подниматься. Если5900слушает — запускаетweb-vnc.exe --password-hash <HASH> --listen :8080без--spawn(подключается к существующему серверу, не плодя второй). Если за ~30 c не поднялось — спавнит локальный VNC-сервер (--spawn-command "vnc\winvnc.exe -run"если есть portable, иначе--spawnс авто-детектом) и запускает web-vnc. На старте ничего не качает. Задание держит процесс живым в foreground.- Снять автостарт:
uninstall-autostart.bat(от админа) —schtasks /Delete.
Замечания:
- Задание выполняется от
SYSTEM, поэтому VNC-сервер, отдавающий рабочий стол, должен быть отдельным сервисом (UltraVNCuvnc_service) —autostart-run.batего НЕ порождает, а лишь подключается к127.0.0.1:5900. Портативныйvnc\winvnc.exeиспользуется только если 5900 ещё не слушает. - Смена пароля на ходу:
web-vnc.exeчитает хэш иWEBVNC_VNC_PASSWORDодин раз при старте (config.Parse/auth.New) и никогда не перечитываетwebvnc.conf. Поэтомуset-password.batпосле перезаписиwebvnc.confсам перезапускает задание «web-vnc» черезrestart-autostart.bat(elevation): тот проверяетtasklist, запущен лиweb-vnc.exe, и делаетschtasks /End+/Run(или только/Run, если процесс не запущен). Рестарт пропускается, если пароль не изменился (детект по старомуVNC_PASSWORDизwebvnc.conf, т.к. PBKDF2-хэш всегда новый из-за соли). Если задание не установлено —set-password.batтолько пишетwebvnc.confи подсказываетinstall-autostart.bat. Для интерактивногоrun.batнового пароля — просто перезапусти его вручную.
Что делать дальше (известные TODO)
- Надёжный авто-скачиватель VNC-сервера (URL UltraVNC на SourceForge нестабилен —
get-vnc.ps1теперь верифицирует zip-магию и даёт фолбэк на ручную установку). - На Windows захват экрана может требовать запуск VNC-сервера от администратора.
- VNC-сервер требует свой пароль;
--vnc-passwordпередаёт его noVNC автоматически, но пароль VNC-сервера нужно один раз настроить под тот же. UltraVNC как сервис (uvnc_service, LocalSystem): пароль хранится в%ProgramData%\UltraVNC\ultravnc.ini; задать его обычным пользователем через tray-иконку молча не получается (нет прав на запись).set-password.batсинхронизирует этот пароль сам черезscripts/ensure-vnc-password.ps1(пишет только при несовпадении; UAC только при смене пароля). VNC-пароль = первые 8 байт, поэтому используй ASCII-пароль <= 8 символов и вset-password.bat, и в VNC-сервере.