From d0f2380915bbdcb43eb989c88619b6049c891cbe Mon Sep 17 00:00:00 2001 From: Mark C Allen Date: Wed, 26 Aug 2026 18:09:45 +0000 Subject: [PATCH 1/2] Redirect HTTP requests on HTTPS port --- INSTALL.md | 2 +- PRD.md | 2 ++ README.md | 2 +- config/chromium-launch.sh | 2 +- scripts/e2e-guacamole.sh | 15 ++++++++++++++- scripts/guacamole-config.sh | 1 + 6 files changed, 20 insertions(+), 4 deletions(-) diff --git a/INSTALL.md b/INSTALL.md index cb263c7..81e7300 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -208,7 +208,7 @@ A protocol-specific `400` or `404` can still prove connectivity. `Connection ref ## Chromium Sandbox -Hatch keeps Chromium's sandbox enabled when the container runtime allows it. If the container is started with `--security-opt no-new-privileges:true`, Chromium cannot use its setuid sandbox, so Hatch automatically adds `--no-sandbox` for that session. +If the container is started with `--security-opt no-new-privileges:true`, Chromium cannot use its setuid sandbox, so Hatch automatically adds `--no-sandbox` for that session. Hatch also adds Chromium's `--test-type` flag in that mode to suppress Chromium's unsupported command-line flag warning. If an unusually restrictive host still prevents Chromium from starting, diagnose the host first. As a last resort, set: diff --git a/PRD.md b/PRD.md index 46b0757..a20a328 100644 --- a/PRD.md +++ b/PRD.md @@ -9,6 +9,7 @@ Hatch must provide browser-based desktop access through HTTPS from the container ### Acceptance Criteria - The default container listens on HTTPS port `443`. +- Plain HTTP requests sent to the published HTTPS port redirect to the equivalent `https://` URL. - The HTTPS server proxies `/guacamole/` to Guacamole on `127.0.0.1:8080`. - Guacamole connects through `guacd` on `127.0.0.1:4822`. - `guacd` connects to xrdp on `127.0.0.1:3389`. @@ -17,5 +18,6 @@ Hatch must provide browser-based desktop access through HTTPS from the container - The generated Guacamole credentials are printed to container logs and usable when the container is started detached. - Docker users can map any host port to container port `443`, for example `-p 8443:443`. - Host-network OAuth callback mode remains documented for cases where Chromium must reach a callback listener on host loopback. +- Default Docker and Docker Compose starts do not show Chromium's unsupported `--no-sandbox` warning. - The README presents the HTTPS Guacamole flow as the primary quickstart and keeps Docker Compose as a lower-priority option. - An E2E smoke test validates the HTTPS Guacamole login path and confirms the browser desktop starts. diff --git a/README.md b/README.md index d197287..c9ba75c 100644 --- a/README.md +++ b/README.md @@ -166,7 +166,7 @@ Do not publish direct RDP access. Hatch exposes HTTPS for browser access, and xr The default certificate is self-signed. Use a reverse proxy, load balancer, or mounted certificate files if you need a publicly trusted certificate. Certbot HTTP-01 validation requires public port 80, while DNS-01 can issue certificates without opening port 80. -When the container is started with `--security-opt no-new-privileges:true`, Hatch automatically adds Chromium's `--no-sandbox` flag because the setuid sandbox cannot run under that kernel setting. +When the container is started with `--security-opt no-new-privileges:true`, Hatch automatically adds Chromium's `--no-sandbox` compatibility flag because the setuid sandbox cannot run under that kernel setting. Hatch also adds Chromium's `--test-type` flag in that mode to suppress Chromium's unsupported command-line flag warning. Browser persistence is off by default. If persistent browser sessions are required, mount `/home/oauth/.config/chromium` as a Docker volume and protect it as sensitive authentication material. diff --git a/config/chromium-launch.sh b/config/chromium-launch.sh index 4d42961..2d30310 100644 --- a/config/chromium-launch.sh +++ b/config/chromium-launch.sh @@ -2,7 +2,7 @@ set -eu FLAGS="--disable-dev-shm-usage --no-first-run --no-default-browser-check --disable-session-crashed-bubble" if grep -q '^NoNewPrivs:[[:space:]]*1$' /proc/self/status 2>/dev/null; then - FLAGS="$FLAGS --no-sandbox" + FLAGS="$FLAGS --no-sandbox --test-type" fi if [ "$#" -eq 0 ]; then set -- about:blank; fi # shellcheck disable=SC2086 diff --git a/scripts/e2e-guacamole.sh b/scripts/e2e-guacamole.sh index 85e6c8c..37aff10 100755 --- a/scripts/e2e-guacamole.sh +++ b/scripts/e2e-guacamole.sh @@ -73,6 +73,14 @@ if ! curl -kfsS "https://127.0.0.1:${HTTPS_PORT}/guacamole/" >/dev/null; then exit 1 fi +HTTP_REDIRECT_LOCATION="$(curl -fsSI "http://127.0.0.1:${HTTPS_PORT}/" | sed -n 's/^[Ll]ocation: //p' | tr -d '\r' | tail -n 1)" +if [ "$HTTP_REDIRECT_LOCATION" != "https://127.0.0.1:${HTTPS_PORT}/" ]; then + docker logs "${PREFIX}-hatch" >&2 || true + echo "ERROR: HTTP on the HTTPS port did not redirect to https://127.0.0.1:${HTTPS_PORT}/." >&2 + echo "Observed Location: ${HTTP_REDIRECT_LOCATION:-}" >&2 + exit 1 +fi + GUAC_USER="$(docker logs "${PREFIX}-hatch" 2>&1 | sed -n 's/^Guacamole user: //p' | tail -n 1)" GUAC_PASSWORD="$(docker logs "${PREFIX}-hatch" 2>&1 | sed -n 's/^Guacamole password: //p' | tail -n 1)" if [ -z "$GUAC_USER" ] || [ -z "$GUAC_PASSWORD" ]; then @@ -121,7 +129,12 @@ GUAC_PASSWORD="$GUAC_PASSWORD" \ } for _ in $(seq 1 45); do - if docker exec "${PREFIX}-hatch" sh -lc "pgrep -af 'chromium.*${URL}' >/dev/null"; then + if docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' >/dev/null"; then + if ! docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' | grep -F -- '--test-type' >/dev/null"; then + echo "ERROR: Chromium did not start with --test-type to suppress unsupported flag warnings." >&2 + docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' || true" >&2 || true + exit 1 + fi echo "Guacamole E2E succeeded: HTTPS login reached Hatch RDP and Chromium opened $URL" exit 0 fi diff --git a/scripts/guacamole-config.sh b/scripts/guacamole-config.sh index e2e26b9..ebd165f 100755 --- a/scripts/guacamole-config.sh +++ b/scripts/guacamole-config.sh @@ -40,6 +40,7 @@ server { ssl_certificate_key $TLS_KEY; ssl_protocols TLSv1.2 TLSv1.3; absolute_redirect off; + error_page 497 =301 https://\$http_host\$request_uri; access_log /dev/stdout; error_log /dev/stderr warn; From 89d80148e56f1e636295b744588a1764354d5871 Mon Sep 17 00:00:00 2001 From: Mark C Allen Date: Wed, 26 Aug 2026 18:15:38 +0000 Subject: [PATCH 2/2] Harden Guacamole smoke test checks --- scripts/e2e-guacamole.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/e2e-guacamole.sh b/scripts/e2e-guacamole.sh index 37aff10..dd854b4 100755 --- a/scripts/e2e-guacamole.sh +++ b/scripts/e2e-guacamole.sh @@ -73,7 +73,7 @@ if ! curl -kfsS "https://127.0.0.1:${HTTPS_PORT}/guacamole/" >/dev/null; then exit 1 fi -HTTP_REDIRECT_LOCATION="$(curl -fsSI "http://127.0.0.1:${HTTPS_PORT}/" | sed -n 's/^[Ll]ocation: //p' | tr -d '\r' | tail -n 1)" +HTTP_REDIRECT_LOCATION="$(curl -sSI "http://127.0.0.1:${HTTPS_PORT}/" 2>/dev/null | sed -n 's/^[Ll]ocation: //p' | tr -d '\r' | tail -n 1 || true)" if [ "$HTTP_REDIRECT_LOCATION" != "https://127.0.0.1:${HTTPS_PORT}/" ]; then docker logs "${PREFIX}-hatch" >&2 || true echo "ERROR: HTTP on the HTTPS port did not redirect to https://127.0.0.1:${HTTPS_PORT}/." >&2 @@ -129,10 +129,10 @@ GUAC_PASSWORD="$GUAC_PASSWORD" \ } for _ in $(seq 1 45); do - if docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' >/dev/null"; then - if ! docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' | grep -F -- '--test-type' >/dev/null"; then + if docker exec -e EXPECTED_URL="$URL" "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium' | grep -F -- \"\$EXPECTED_URL\" >/dev/null"; then + if ! docker exec -e EXPECTED_URL="$URL" "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium' | grep -F -- \"\$EXPECTED_URL\" | grep -F -- '--test-type' >/dev/null"; then echo "ERROR: Chromium did not start with --test-type to suppress unsupported flag warnings." >&2 - docker exec "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium.*${URL}' || true" >&2 || true + docker exec -e EXPECTED_URL="$URL" "${PREFIX}-hatch" sh -lc "pgrep -af '[c]hromium' | grep -F -- \"\$EXPECTED_URL\" || true" >&2 || true exit 1 fi echo "Guacamole E2E succeeded: HTTPS login reached Hatch RDP and Chromium opened $URL"