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

160 lines
7.6 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.
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/`.