Files
rvbox/test/rdp-access/README.md
T

171 lines
7.9 KiB
Markdown

# Interactive Windows VM access
This directory is an explicitly temporary, manual-recovery path to the Helium
Windows fixture desktop. It is for the small class of actions that require an
interactive UAC consent dialog. Normal setup, testing, collection, and reset
remain `scripts/windows/test-host` plus VirtualBox Guest Control.
The helper builds this private path:
```text
browser -- HTTPS/self-signed --> nginx + Guacamole containers
(IPv4 and, in public mode, a tracked IPv6 forward)
|
private Docker gateway
|
controller SSH watchdog/tunnel --> Helium 127.0.0.1:3389 --> VirtualBox VRDE --> VM console
```
Only the HTTPS listener can be made public, and that requires an explicit
`--bind 0.0.0.0`. The VirtualBox VRDE endpoint stays on Helium loopback and the
SSH tunnel binds only to the Docker network gateway; neither is publicly
exposed. Guacamole requires the fixture login before it forwards the entered
password to the VM. Clipboard, drives, printing, audio, microphone input, GFX,
and display-resize extensions are disabled because the fixture's VirtualBox
RDP4 server does not handle them reliably.
## Lifecycle
Run commands from the repository root. First prepare the disposable VM using a
dedicated run ID. This restores the snapshot, starts the VM headlessly, and
holds the exclusive fixture lease while the manual action is in progress:
```sh
scripts/windows/test-host prepare --run-id interactive-rdp
```
For browser access from another machine, deliberately expose the temporary
HTTPS listener and name the host or IP users will enter in the browser:
```sh
test/rdp-access/rdp-access up \
--bind 0.0.0.0 \
--public-host x1.example.net
```
The command prompts without echo for the fixture password. It stores only its
MD5 verifier in `test/rdp-access/.runtime/config/user-mapping.xml`, mode 600;
the plaintext password is not placed in a command line, environment variable,
log, or repository file. Browse to the printed `https://.../guacamole/` URL,
accept the short-lived self-signed certificate warning, and sign in as
`rvboxtest` with the fixture password.
For localhost-only use, omit `--bind` and `--public-host`. The default listener
is `127.0.0.1:5002`; use a local SSH forward or a browser on the controller.
Choose alternate ports with `--http-port` and `--tunnel-port` if either is in
use. The VM must already be running. `up` checks the documented VM/snapshot
identity but intentionally does not restore, start, stop, or reset the VM. It
starts a small host-side watchdog for the SSH master. Before printing the URL,
`up` waits until the HTTPS gateway can serve `/guacamole/`; this prevents a
browser login from racing Tomcat's Guacamole WAR deployment. The watchdog
reconnects after a transient Helium/SSH failure while preserving the same
Docker-gateway listener, so an already-open Guacamole session can recover
without restarting the containers. Its PID, stop marker, and diagnostic log
are kept under the ignored `.runtime/` directory.
All fixture-specific values have embedded, working defaults: the
`helium-remote` SSH alias, Helium's loopback VRDE endpoint (`127.0.0.1:3389`),
`rvboxtest`, the browser listener (`127.0.0.1:5002`), and the private tunnel
port (`54001`). They can be overridden without editing tracked files through
`RVBOX_TEST_VBOX_HOST`, `RDP_ACCESS_VRDE_HOST`, `RDP_ACCESS_VRDE_PORT`,
`RDP_ACCESS_WEB_USER`, `RDP_ACCESS_RDP_USER`, `RDP_ACCESS_BIND`,
`RDP_ACCESS_HTTP_PORT`, `RDP_ACCESS_TUNNEL_PORT`, and
`RDP_ACCESS_PUBLIC_HOST`, `RDP_ACCESS_IPV6_BIND`, and
`RDP_ACCESS_IPV6_FORWARD`. The helper deliberately limits the VRDE host to
Helium loopback (`127.0.0.1` or `localhost`) so an override cannot accidentally
turn the diagnostic server into a remote target.
Set `RDP_ACCESS_GUACD_LOG_LEVEL=debug` temporarily when collecting detailed
guacd/RDP negotiation diagnostics; the default is `info`.
When `--bind 0.0.0.0` is used, `up` also starts a tracked `socat` listener on
`[::]:$RDP_ACCESS_HTTP_PORT` and forwards it to the IPv4 gateway listener. This
matters when the public hostname has an AAAA record: some browsers choose IPv6
for the WebSocket even if the initial page used IPv4. The listener is recorded
under `.runtime/ipv6-forward.pid` and is removed by `down` and `clean`. Its
default bind is `::` (`RDP_ACCESS_IPV6_BIND`); set
`RDP_ACCESS_IPV6_FORWARD=never` to deliberately use IPv4 only, `always` to make
IPv6 startup a hard requirement, or leave the default `auto` for a warning and
an IPv4-only fallback when `socat` is unavailable. `status` reports
`ipv6_forward=active`, `active_external`, `inactive`, or `disabled`.
If `up` is run again while the Compose services are still running, it is
idempotent: an existing healthy tunnel is reused, and a missing tunnel is
recreated in place. `status` reports a helper-owned tunnel as
`private_tunnel=active` and a listener supplied by an external/interactive
SSH supervisor as `private_tunnel=active_external`. A missing listener is
reported as `private_tunnel=inactive`; inspect `.runtime/tunnel.log` and run
`up` again to trigger a bounded reconnect attempt.
The XML mapping intentionally uses Guacamole's `${GUAC_PASSWORD}` connection
parameter token. Guacamole resolves this to the password entered at web login;
it is not a host environment variable and must remain in the template.
After the interactive action, close the browser connection and remove the
temporary access path before releasing the fixture lease:
```sh
test/rdp-access/rdp-access down
scripts/windows/test-host reset --run-id interactive-rdp
```
`down` stops containers and the SSH watchdog/master/tunnel but retains the
one-day certificate and password verifier for a quick restart. To remove all
generated state, including the certificate, verifier, watchdog PID, and tunnel
log:
```sh
test/rdp-access/rdp-access clean
```
To also reclaim the exact Guacamole and nginx images when they have no
container dependency, use:
```sh
test/rdp-access/rdp-access clean --images
```
`clean --images` deliberately leaves `alpine:3.20` alone because it may be
shared by unrelated containers. Docker will refuse removal if any other
container still depends on an image.
## Operational checks and recovery
```sh
test/rdp-access/rdp-access status
test/rdp-access/rdp-access logs --tail=100
test/rdp-access/rdp-access url
```
If the browser reaches Guacamole but stays on “Waiting for response”, run
`status` first. Confirm `private_tunnel=active` (or
`active_external`) and, for a public dual-stack hostname,
`ipv6_forward=active` (or a known-good external forward). Then check the VM
state and the last lines of
`.runtime/tunnel.log`. A tunnel can be recreated without losing the Compose
stack by running `up` again. This helper already uses `security=rdp` and
disables Guacamole's GFX extension, which are required by the fixture's legacy
VRDE server. Do not switch the helper to native Windows RDP:
`TermService` is intentionally disabled in the baseline. If VRDE remains
unusable, stop this helper and use Guest Control for the deterministic portion
of the work; record the blocked interactive step in the native test report.
If guacd has a stale worker and the VM reports that multiple connections are
disabled, run:
```sh
test/rdp-access/rdp-access repair
```
`repair` restarts only guacd, retains the VM, SSH tunnel, gateway, and IPv6
forward, and does not alter the Windows guest. Close old browser tabs and sign
in again after it completes.
The helper requires Docker/Docker Compose, `socat` for the optional public
dual-stack forward, SSH access through the existing `helium-remote` alias, and
the fixture password file documented in
[`docs/testing-vm.md`](../../docs/testing-vm.md). It does not install host
packages, write credentials into Git, or alter VM identity/settings. The
authoritative `test-host prepare` step applies only the disposable
display/sleep keepalive needed by VirtualBox VRDE; all generated content is
ignored beneath `.runtime/`.