261 lines
13 KiB
Markdown
261 lines
13 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.
|
|
|
|
## Current fixture quick start
|
|
|
|
The following is the copy/paste path for the documented `rvbox-win10-test`
|
|
fixture. Run it from the repository root. The first command acquires the
|
|
exclusive VM lease, restores the clean administrator-enabled snapshot, starts
|
|
the VM, and applies the VRDE display keepalive. The second command starts the
|
|
Docker-only gateway at the currently published endpoint:
|
|
|
|
```sh
|
|
scripts/windows/test-host prepare --run-id interactive-rdp
|
|
test/rdp-access/rdp-access up \
|
|
--bind 0.0.0.0 \
|
|
--public-host x1.xcel.me
|
|
```
|
|
|
|
`up` prompts for the fixture password. If the password is not already in the
|
|
operator's secure notes, the test-only value is available only in the Helium
|
|
host's mode-600 file documented in [`docs/testing-vm.md`](../../docs/testing-vm.md).
|
|
For an unattended first start, pass that file through the existing SSH alias
|
|
without putting the value in an argument, environment variable, or log:
|
|
|
|
```sh
|
|
ssh helium-remote 'cat /home/cabbage/.local/share/rvbox-secrets/rvbox-win10-test.password' |
|
|
test/rdp-access/rdp-access up \
|
|
--bind 0.0.0.0 \
|
|
--public-host x1.xcel.me \
|
|
--web-password-stdin
|
|
```
|
|
|
|
The browser URL is `https://x1.xcel.me:5002/guacamole/`; accept the temporary
|
|
self-signed certificate warning and sign in as `rvboxtest`. The same command
|
|
is safe to rerun after a tunnel interruption. If the stack is already running,
|
|
`up` reuses it and repairs only missing forwarding; it does not consume stdin.
|
|
Use `--reset-auth` only after `down` when intentionally replacing the saved
|
|
verifier, for example:
|
|
|
|
```sh
|
|
test/rdp-access/rdp-access down
|
|
ssh helium-remote 'cat /home/cabbage/.local/share/rvbox-secrets/rvbox-win10-test.password' |
|
|
test/rdp-access/rdp-access up \
|
|
--bind 0.0.0.0 \
|
|
--public-host x1.xcel.me \
|
|
--reset-auth \
|
|
--web-password-stdin
|
|
```
|
|
|
|
Do not run `clean` or `test-host reset` while another agent owns the fixture
|
|
lease or has a live browser session. The generated verifier, certificate,
|
|
tunnel state, and logs are disposable and live only under the ignored
|
|
`test/rdp-access/.runtime/` directory.
|
|
|
|
Before a first `prepare`, run `scripts/windows/test-host status`. If it reports
|
|
`state=running`, another run may already own the fixture lease; do not restore
|
|
or reset it. Reuse that run's access path or coordinate with its owner. If it
|
|
reports `state=poweroff`, the quick-start sequence above is safe. The helper
|
|
does not guess at ownership or silently take over a running VM.
|
|
|
|
## 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.
|
|
|
|
For quick recovery, use this decision table:
|
|
|
|
| Need | Command | Effect |
|
|
| --- | --- | --- |
|
|
| Inspect everything | `test/rdp-access/rdp-access status` | Read-only VM, Compose, tunnel, and IPv6-forward state |
|
|
| Start or reconnect access | `test/rdp-access/rdp-access up --bind 0.0.0.0 --public-host x1.xcel.me` | Reuses healthy services; recreates only missing tunnel/forward |
|
|
| Browser says “Waiting for response” | `test/rdp-access/rdp-access status`; `test/rdp-access/rdp-access logs --tail=100`; if guacd is stale, `test/rdp-access/rdp-access repair` | Diagnoses tunnel/dual-stack issues; restarts only guacd |
|
|
| Print the current URL | `test/rdp-access/rdp-access url` | Read-only URL from the saved session |
|
|
| Stop temporary access | `test/rdp-access/rdp-access down` | Stops gateway, IPv6 forward, SSH watchdog/tunnel; retains verifier/cert |
|
|
| Reclaim generated state | `test/rdp-access/rdp-access clean` | Stops access and removes only `.runtime/` |
|
|
| Reclaim exact unused images too | `test/rdp-access/rdp-access clean --images` | Also removes owned Guacamole/nginx images; leaves shared Alpine intact |
|
|
| Return VM to clean baseline | `scripts/windows/test-host reset --run-id interactive-rdp` | Stops/ restores the exact leased VM snapshot and leaves it powered off |
|
|
|
|
The two scripts have a deliberate ownership boundary. `rdp-access` manages only
|
|
the temporary browser gateway, Guacamole/guacd containers, IPv6 forward, and
|
|
Helium SSH tunnel. `scripts/windows/test-host` manages the leased VM and its
|
|
service lifecycle. For a prepared run, the remaining VM-side management actions
|
|
are:
|
|
|
|
```sh
|
|
scripts/windows/test-host inspect --run-id interactive-rdp # SCM/read-only client state
|
|
scripts/windows/test-host logs --run-id interactive-rdp # bounded startup/client logs
|
|
scripts/windows/test-host collect --run-id interactive-rdp # bounded artifacts
|
|
scripts/windows/test-host stop --run-id interactive-rdp # stop service and request guest shutdown
|
|
scripts/windows/test-host recover --run-id interactive-rdp # inspect an interrupted stopped run
|
|
```
|
|
|
|
`stage`, `install`, `run`, and `logoff` are also available in the
|
|
`test-host --help` command reference for a complete native-service run; they
|
|
are not required merely to use the RDP gateway. `native-test recover` and
|
|
`native-test clean` are the corresponding whole-lane recovery/cleanup actions
|
|
when the Linux server stack is part of the run.
|
|
|
|
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/`.
|