test: add temporary Guacamole fixture access helper

This commit is contained in:
2026-09-09 09:00:06 +00:00
parent 7e82f2c26f
commit 8985457d37
10 changed files with 506 additions and 0 deletions
+2
View File
@@ -20,6 +20,8 @@ Read the documents in this order:
8. [Provisioned Windows test VM](testing-vm.md) — exact fixture identity, 8. [Provisioned Windows test VM](testing-vm.md) — exact fixture identity,
host/guest access, endpoints, snapshots, credentials contract, reset host/guest access, endpoints, snapshots, credentials contract, reset
procedure, and known limitations. procedure, and known limitations.
9. [Interactive VM access](../test/rdp-access/README.md) — temporary,
self-signed HTTPS browser gateway for the rare manual UAC recovery step.
The wire authority is in [`../protos/rvbox/v1`](../protos/rvbox/v1): The wire authority is in [`../protos/rvbox/v1`](../protos/rvbox/v1):
`common.proto` contains shared data types, `agent.proto` contains the `common.proto` contains shared data types, `agent.proto` contains the
+8
View File
@@ -847,6 +847,14 @@ The normal active `rvboxtest` session remains the target for execution-role
tests. The consent-prompt branch itself remains an interactive UAC test; an tests. The consent-prompt branch itself remains an interactive UAC test; an
invisible Guest Control session must never answer it. invisible Guest Control session must never answer it.
When that bounded manual step is necessary, use the tracked Docker-only
[`test/rdp-access`](../test/rdp-access/README.md) helper. It starts a
self-signed HTTPS Guacamole gateway only after `test-host prepare` holds the
fixture lease; VRDE remains loopback-only on Helium and its SSH tunnel is bound
only to the helper's private Docker gateway. Stop the helper before the normal
`test-host reset`. It is a recovery interface, not a product component or a
replacement for Guest Control/native test automation.
For this provisioned lane, the approved host-only credential-file location is For this provisioned lane, the approved host-only credential-file location is
`/home/cabbage/.local/share/rvbox-secrets/rvbox-win10-test.password`. It must `/home/cabbage/.local/share/rvbox-secrets/rvbox-win10-test.password`. It must
remain mode `0600`, is never read into a repository process, and is supplied to remain mode `0600`, is never read into a repository process, and is supplied to
+6
View File
@@ -85,6 +85,12 @@ VirtualBox 7.2.16 does not handle reliably, and earlier probes included
headless-server crashes. Use Guest Control for deterministic setup, execution, headless-server crashes. Use Guest Control for deterministic setup, execution,
and collection. Do not expose the VM's RDP endpoints beyond the test LAN. and collection. Do not expose the VM's RDP endpoints beyond the test LAN.
For the rare interactive UAC/manual-recovery step, use the Docker-only helper
in [`test/rdp-access`](../test/rdp-access/README.md). It creates a temporary
self-signed HTTPS Guacamole gateway while retaining VRDE on Helium loopback and
the SSH tunnel on a private Docker gateway. Follow its full lease/prepare/up/
down/reset lifecycle; it is not an alternative to the native test controller.
## Snapshots and reset contract ## Snapshots and reset contract
Three clean snapshots exist and must be retained. `baseline-clean-administrator` Three clean snapshots exist and must be retained. `baseline-clean-administrator`
+5
View File
@@ -138,6 +138,11 @@ than treating it as a stable endpoint. VRDE is enabled only on Helium loopback
at `127.0.0.1:3389` for diagnostics, while native Windows RDP is disabled in at `127.0.0.1:3389` for diagnostics, while native Windows RDP is disabled in
the baseline. the baseline.
Interactive browser access is a deliberately temporary recovery path only. See
[`test/rdp-access`](../test/rdp-access/README.md) for the Docker-only,
self-signed HTTPS Guacamole lifecycle; it must be started only after the native
fixture controller has prepared and leased the VM, and stopped before reset.
The canonical headless VirtualBox/Guest Control adapter is The canonical headless VirtualBox/Guest Control adapter is
`scripts/windows/test-host`. It is a POSIX controller script because the `scripts/windows/test-host`. It is a POSIX controller script because the
fixture's VirtualBox host is Arch Linux and has no PowerShell runtime. The fixture's VirtualBox host is Arch Linux and has no PowerShell runtime. The
+1
View File
@@ -0,0 +1 @@
.runtime/
+118
View File
@@ -0,0 +1,118 @@
# 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
|
private Docker gateway
|
controller SSH 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.
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`. 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.
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 master/tunnel but retains the one-day
certificate and password verifier for a quick restart. To remove all generated
state, including the certificate and verifier:
```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”, verify
that the VM is running and the private tunnel is active with `status`. 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, 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 settings. The tracked files
are Docker-only configuration and the controller script; all generated content
is ignored beneath `.runtime/`.
+46
View File
@@ -0,0 +1,46 @@
services:
guacd:
image: guacamole/guacd:1.6.0
guacamole:
image: guacamole/guacamole:1.6.0
depends_on:
- guacd
environment:
GUACD_HOSTNAME: guacd
GUACD_PORT: "4822"
volumes:
- ${RDP_ACCESS_RUNTIME_DIR}/config:/etc/guacamole:ro
certgen:
image: alpine:3.20
environment:
RDP_ACCESS_CERT_NAME: ${RDP_ACCESS_CERT_NAME}
RDP_ACCESS_CERT_SAN: ${RDP_ACCESS_CERT_SAN}
RDP_ACCESS_HOST_UID: ${RDP_ACCESS_HOST_UID}
RDP_ACCESS_HOST_GID: ${RDP_ACCESS_HOST_GID}
volumes:
- ${RDP_ACCESS_RUNTIME_DIR}/tls:/tls
entrypoint: /bin/sh
command:
- -ec
- >-
apk add --no-cache openssl >/dev/null &&
(test -s /tls/cert.pem && test -s /tls/key.pem &&
openssl x509 -checkend 3600 -noout -in /tls/cert.pem) ||
(umask 077 &&
openssl req -x509 -newkey rsa:3072 -sha256 -nodes -days 1
-keyout /tls/key.pem -out /tls/cert.pem
-subj /CN=$${RDP_ACCESS_CERT_NAME}
-addext subjectAltName=$${RDP_ACCESS_CERT_SAN} &&
chown $${RDP_ACCESS_HOST_UID}:$${RDP_ACCESS_HOST_GID} /tls /tls/cert.pem /tls/key.pem)
gateway:
image: nginx:1.27-alpine
depends_on:
- guacamole
ports:
- ${RDP_ACCESS_BIND}:${RDP_ACCESS_HTTP_PORT}:8443
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf:ro
- ${RDP_ACCESS_RUNTIME_DIR}/tls:/etc/nginx/tls:ro
+19
View File
@@ -0,0 +1,19 @@
server {
listen 8443 ssl;
server_name _;
ssl_certificate /etc/nginx/tls/cert.pem;
ssl_certificate_key /etc/nginx/tls/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
location / {
proxy_pass http://guacamole:8080;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_buffering off;
}
}
+280
View File
@@ -0,0 +1,280 @@
#!/bin/sh
# Temporary browser access to the Helium fixture's loopback-only VirtualBox
# VRDE endpoint. This is a manual-recovery helper, never a normal test channel.
set -eu
helper_dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
repo_root=$(CDPATH= cd -- "$helper_dir/../.." && pwd)
runtime_dir=$helper_dir/.runtime
compose_file=$helper_dir/compose.yaml
mapping_template=$helper_dir/user-mapping.xml.in
project=rvbox-rdp-access
: "${RDP_ACCESS_BIND:=127.0.0.1}"
: "${RDP_ACCESS_HTTP_PORT:=5002}"
: "${RDP_ACCESS_TUNNEL_PORT:=54001}"
: "${RDP_ACCESS_PUBLIC_HOST:=localhost}"
: "${RDP_ACCESS_WEB_USER:=rvboxtest}"
: "${RDP_ACCESS_RDP_USER:=rvboxtest}"
: "${RVBOX_TEST_VBOX_HOST:=helium-remote}"
: "${RDP_ACCESS_VRDE_HOST:=127.0.0.1}"
: "${RDP_ACCESS_VRDE_PORT:=3389}"
usage() {
cat <<'EOF'
usage: test/rdp-access/rdp-access ACTION [OPTIONS]
Actions:
up start a private VRDE SSH tunnel and self-signed HTTPS Guacamole
status show gateway, tunnel, and fixture status without changing anything
url print the current browser URL
logs follow or print Compose logs (pass Docker Compose log options)
down stop containers and the private SSH tunnel; retain generated state
clean run down and delete generated state; pass --images to also remove
the exact unused Guacamole/nginx images
up options:
--bind ADDRESS listener address (default 127.0.0.1; use 0.0.0.0 only
for a temporary, deliberately public endpoint)
--http-port PORT HTTPS listener port (default 5002)
--tunnel-port PORT private VRDE tunnel port (default 54001)
--public-host NAME browser-visible hostname or IP for the URL and cert SAN
--web-user USER Guacamole and Windows account (default rvboxtest)
--reset-auth discard the saved password hash and prompt again
--web-password-stdin read the password once from stdin instead of prompting
Environment equivalents: RDP_ACCESS_BIND, RDP_ACCESS_HTTP_PORT,
RDP_ACCESS_TUNNEL_PORT, RDP_ACCESS_PUBLIC_HOST, RDP_ACCESS_WEB_USER,
RDP_ACCESS_RDP_USER, RVBOX_TEST_VBOX_HOST, RDP_ACCESS_VRDE_HOST, and
RDP_ACCESS_VRDE_PORT.
EOF
}
fail() { printf '%s\n' "rdp-access: $*" >&2; exit 2; }
safe_name() {
case $2 in ''|*[!A-Za-z0-9.-]*) fail "$1 contains unsupported characters" ;; esac
}
safe_port() {
case $2 in ''|*[!0-9]*) fail "$1 must be a port number" ;; esac
[ "$2" -ge 1024 ] && [ "$2" -le 65535 ] || fail "$1 must be between 1024 and 65535"
}
safe_bind() {
case $1 in 127.0.0.1|0.0.0.0) ;; *) fail "--bind must be 127.0.0.1 or 0.0.0.0" ;; esac
}
compose() {
RDP_ACCESS_RUNTIME_DIR=$runtime_dir \
RDP_ACCESS_BIND=$RDP_ACCESS_BIND \
RDP_ACCESS_HTTP_PORT=$RDP_ACCESS_HTTP_PORT \
RDP_ACCESS_CERT_NAME=$RDP_ACCESS_PUBLIC_HOST \
RDP_ACCESS_CERT_SAN=$cert_san \
RDP_ACCESS_HOST_UID=$(id -u) \
RDP_ACCESS_HOST_GID=$(id -g) \
docker compose --project-name "$project" -f "$compose_file" "$@"
}
socket_path=$runtime_dir/ssh-control.socket
session_file=$runtime_dir/session.env
cert_name_file=$runtime_dir/cert-name
stop_tunnel() {
if [ -S "$socket_path" ]; then
ssh -S "$socket_path" -O exit "$RVBOX_TEST_VBOX_HOST" >/dev/null 2>&1 || true
fi
rm -f "$socket_path"
}
gateway_for_network() {
docker network inspect --format '{{(index .IPAM.Config 0).Gateway}}' "${project}_default"
}
assert_fixture_running() {
fixture_status=$("$repo_root/scripts/windows/test-host" status) || fail "fixture identity check failed"
printf '%s\n' "$fixture_status"
case $fixture_status in *'state=running') ;; *) fail "VM is not running; prepare it first with scripts/windows/test-host prepare --run-id interactive-rdp" ;; esac
}
password_hash_from_terminal() {
password=
restore_tty=false
if [ "$password_stdin" = true ]; then
IFS= read -r password || fail "could not read password from stdin"
else
[ -t 0 ] || fail "stdin is not a terminal; use --web-password-stdin"
printf 'Fixture password for %s: ' "$RDP_ACCESS_WEB_USER" >&2
stty -echo
restore_tty=true
trap 'test "$restore_tty" = true && stty echo || true' EXIT HUP INT TERM
IFS= read -r password || fail "could not read password"
stty echo
restore_tty=false
trap - EXIT HUP INT TERM
printf '\n' >&2
fi
[ -n "$password" ] || fail "password must not be empty"
hash=$(printf '%s' "$password" | docker run --rm -i --entrypoint md5sum alpine:3.20 | awk '{print $1}')
unset password
case $hash in ''|*[!0-9a-f]*) fail "could not generate password hash" ;; esac
[ "${#hash}" -eq 32 ] || fail "could not generate password hash"
printf '%s\n' "$hash"
}
render_mapping() {
umask 077
mkdir -p "$runtime_dir/config" "$runtime_dir/tls"
chmod 700 "$runtime_dir" "$runtime_dir/config" "$runtime_dir/tls"
if [ "$reset_auth" = true ]; then rm -f "$runtime_dir/config/user-mapping.xml"; fi
if [ -s "$runtime_dir/config/user-mapping.xml" ]; then
password_hash=$(sed -n 's/.*password="\([0-9a-f][0-9a-f]*\)".*/\1/p' "$runtime_dir/config/user-mapping.xml" | head -n 1)
case $password_hash in ''|*[!0-9a-f]*) password_hash=$(password_hash_from_terminal) ;; esac
[ "${#password_hash}" -eq 32 ] || password_hash=$(password_hash_from_terminal)
else
password_hash=$(password_hash_from_terminal)
fi
sed \
-e "s/@WEB_USER@/$RDP_ACCESS_WEB_USER/g" \
-e "s/@WEB_PASSWORD_MD5@/$password_hash/g" \
-e "s/@DOCKER_GATEWAY@/$docker_gateway/g" \
-e "s/@TUNNEL_PORT@/$RDP_ACCESS_TUNNEL_PORT/g" \
-e "s/@RDP_USER@/$RDP_ACCESS_RDP_USER/g" \
"$mapping_template" >"$runtime_dir/config/user-mapping.xml"
chmod 600 "$runtime_dir/config/user-mapping.xml"
if [ -f "$cert_name_file" ] && [ "$(cat "$cert_name_file")" != "$RDP_ACCESS_PUBLIC_HOST" ]; then
rm -f "$runtime_dir/tls/cert.pem" "$runtime_dir/tls/key.pem"
fi
printf '%s\n' "$RDP_ACCESS_PUBLIC_HOST" >"$cert_name_file"
chmod 600 "$cert_name_file"
printf 'url=https://%s:%s/guacamole/\n' "$RDP_ACCESS_PUBLIC_HOST" "$RDP_ACCESS_HTTP_PORT" >"$session_file"
printf 'docker_gateway=%s\n' "$docker_gateway" >>"$session_file"
printf 'tunnel_port=%s\n' "$RDP_ACCESS_TUNNEL_PORT" >>"$session_file"
chmod 600 "$session_file"
}
start_tunnel() {
stop_tunnel
ssh -M -S "$socket_path" -fN \
-o BatchMode=yes \
-o ExitOnForwardFailure=yes \
-o ServerAliveInterval=30 \
-o ServerAliveCountMax=3 \
-L "$docker_gateway:$RDP_ACCESS_TUNNEL_PORT:$RDP_ACCESS_VRDE_HOST:$RDP_ACCESS_VRDE_PORT" \
"$RVBOX_TEST_VBOX_HOST"
ssh -S "$socket_path" -O check "$RVBOX_TEST_VBOX_HOST" >/dev/null 2>&1 || fail "private VRDE tunnel did not start"
}
action=${1-}
[ -n "$action" ] || { usage >&2; exit 2; }
shift || true
case $action in --help|-h) usage; exit 0 ;; esac
reset_auth=false
password_stdin=false
remove_images=false
while [ "$#" -gt 0 ]; do
case $1 in
--bind) [ "$#" -ge 2 ] || fail "--bind needs a value"; RDP_ACCESS_BIND=$2; shift 2 ;;
--http-port) [ "$#" -ge 2 ] || fail "--http-port needs a value"; RDP_ACCESS_HTTP_PORT=$2; shift 2 ;;
--tunnel-port) [ "$#" -ge 2 ] || fail "--tunnel-port needs a value"; RDP_ACCESS_TUNNEL_PORT=$2; shift 2 ;;
--public-host) [ "$#" -ge 2 ] || fail "--public-host needs a value"; RDP_ACCESS_PUBLIC_HOST=$2; shift 2 ;;
--web-user) [ "$#" -ge 2 ] || fail "--web-user needs a value"; RDP_ACCESS_WEB_USER=$2; RDP_ACCESS_RDP_USER=$2; shift 2 ;;
--reset-auth) reset_auth=true; shift ;;
--web-password-stdin) password_stdin=true; shift ;;
--images) remove_images=true; shift ;;
--help|-h) usage; exit 0 ;;
*) break ;;
esac
done
safe_bind "$RDP_ACCESS_BIND"
safe_port RDP_ACCESS_HTTP_PORT "$RDP_ACCESS_HTTP_PORT"
safe_port RDP_ACCESS_TUNNEL_PORT "$RDP_ACCESS_TUNNEL_PORT"
[ "$RDP_ACCESS_HTTP_PORT" != "$RDP_ACCESS_TUNNEL_PORT" ] || fail "HTTPS and tunnel ports must differ"
safe_name RDP_ACCESS_PUBLIC_HOST "$RDP_ACCESS_PUBLIC_HOST"
safe_name RDP_ACCESS_WEB_USER "$RDP_ACCESS_WEB_USER"
safe_name RDP_ACCESS_RDP_USER "$RDP_ACCESS_RDP_USER"
safe_name RVBOX_TEST_VBOX_HOST "$RVBOX_TEST_VBOX_HOST"
case $RDP_ACCESS_VRDE_HOST in 127.0.0.1|localhost) ;; *) fail "RDP_ACCESS_VRDE_HOST must be 127.0.0.1 or localhost" ;; esac
safe_port RDP_ACCESS_VRDE_PORT "$RDP_ACCESS_VRDE_PORT"
case $RDP_ACCESS_PUBLIC_HOST in
*[!0-9.]* ) cert_san="DNS:$RDP_ACCESS_PUBLIC_HOST" ;;
* ) cert_san="IP:$RDP_ACCESS_PUBLIC_HOST" ;;
esac
[ "$remove_images" = false ] || [ "$action" = clean ] || fail "--images is valid only with clean"
[ "$reset_auth" = false ] || [ "$action" = up ] || fail "--reset-auth is valid only with up"
[ "$password_stdin" = false ] || [ "$action" = up ] || fail "--web-password-stdin is valid only with up"
case $action in
up)
[ "$#" -eq 0 ] || { usage >&2; fail "unknown up option $1"; }
if [ "$RDP_ACCESS_BIND" = 0.0.0.0 ] && [ "$RDP_ACCESS_PUBLIC_HOST" = localhost ]; then
fail "a public bind requires --public-host with the browser-visible hostname or IP"
fi
docker version >/dev/null
docker compose version >/dev/null
assert_fixture_running
if compose ps -q | grep -q .; then
fail "gateway already exists; use status or down first"
fi
mkdir -p "$runtime_dir"
compose up -d guacd
docker_gateway=$(gateway_for_network) || { compose down --remove-orphans; fail "could not determine private Docker gateway"; }
render_mapping
if ! start_tunnel; then
compose down --remove-orphans
fail "could not create private SSH tunnel"
fi
if ! compose run --rm certgen; then
stop_tunnel
compose down --remove-orphans
fail "could not generate self-signed certificate"
fi
if ! compose up -d guacamole gateway; then
stop_tunnel
compose down --remove-orphans
fail "could not start Guacamole gateway"
fi
printf 'Guacamole is ready at https://%s:%s/guacamole/\n' "$RDP_ACCESS_PUBLIC_HOST" "$RDP_ACCESS_HTTP_PORT"
printf 'Accept the self-signed certificate warning, then sign in as %s with the fixture password.\n' "$RDP_ACCESS_WEB_USER"
;;
status)
[ "$#" -eq 0 ] || { usage >&2; fail "status accepts no options"; }
"$repo_root/scripts/windows/test-host" status || true
if [ -f "$session_file" ]; then sed -n '1p' "$session_file"; fi
compose ps
if [ -S "$socket_path" ] && ssh -S "$socket_path" -O check "$RVBOX_TEST_VBOX_HOST" >/dev/null 2>&1; then
printf 'private_tunnel=active\n'
else
printf 'private_tunnel=inactive\n'
fi
;;
url)
[ "$#" -eq 0 ] || { usage >&2; fail "url accepts no options"; }
[ -f "$session_file" ] || fail "no saved gateway session; run up first"
sed -n '1s/^url=//p' "$session_file"
;;
logs)
compose logs "$@"
;;
down)
[ "$#" -eq 0 ] || { usage >&2; fail "down accepts no options"; }
stop_tunnel
compose down --remove-orphans || true
printf 'Temporary gateway and private tunnel stopped; generated certificate and password hash retained in %s.\n' "$runtime_dir"
;;
clean)
[ "$#" -eq 0 ] || { usage >&2; fail "clean accepts only --images"; }
stop_tunnel
compose down --remove-orphans || true
case $runtime_dir in "$helper_dir"/.runtime) rm -rf "$runtime_dir" ;; *) fail "unsafe runtime path" ;; esac
if [ "$remove_images" = true ]; then
docker image rm guacamole/guacamole:1.6.0 guacamole/guacd:1.6.0 nginx:1.27-alpine >/dev/null 2>&1 || true
fi
printf 'Temporary gateway state removed.\n'
;;
*) usage >&2; fail "unknown action $action" ;;
esac
+21
View File
@@ -0,0 +1,21 @@
<user-mapping>
<authorize username="@WEB_USER@" password="@WEB_PASSWORD_MD5@" encoding="md5">
<connection name="RVBox Windows test VM">
<protocol>rdp</protocol>
<param name="hostname">@DOCKER_GATEWAY@</param>
<param name="port">@TUNNEL_PORT@</param>
<param name="security">rdp</param>
<param name="username">@RDP_USER@</param>
<param name="password">${GUAC_PASSWORD}</param>
<param name="ignore-cert">true</param>
<param name="disable-audio">true</param>
<param name="enable-audio-input">false</param>
<param name="enable-drive">false</param>
<param name="enable-printing">false</param>
<param name="enable-wallpaper">false</param>
<param name="enable-theming">false</param>
<param name="enable-font-smoothing">false</param>
<param name="disable-gfx">true</param>
</connection>
</authorize>
</user-mapping>