# 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$<итерации>$$`. ### Запуск одной строкой ```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`.