From 1140ba4258f4dc3625c5f0ad0a3cefbc13630500 Mon Sep 17 00:00:00 2001 From: Paulo Date: Mon, 17 Aug 2026 18:49:23 +0200 Subject: [PATCH 1/2] The login browser is real Chrome The login browser ran Playwright's bundled Chromium (shipped as Chrome for Testing). Its client hints report the `Chromium` brand while the user-agent string claims `Chrome`, and a login gate reads that split. Install real Google Chrome and launch it through Playwright's `chrome` channel, so the browser presents Chrome's fingerprint. Google ships no arm64 Linux build, so Chrome is amd64-only; session-launch falls back to the bundled chromium where Chrome is absent. Honor TZ on the container, so the browser's timezone can be aligned with the login egress. --- deploy/browser/Dockerfile | 18 +++++++++++++++++- deploy/browser/session-launch | 6 ++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/deploy/browser/Dockerfile b/deploy/browser/Dockerfile index a693691b..e9120687 100644 --- a/deploy/browser/Dockerfile +++ b/deploy/browser/Dockerfile @@ -31,7 +31,8 @@ RUN apt-get update \ > /etc/apt/sources.list.d/nodesource.list \ && apt-get update \ && apt-get install -y --no-install-recommends \ - fonts-liberation fonts-noto-cjk fonts-noto-color-emoji nodejs x11vnc xvfb \ + fonts-liberation fonts-noto-cjk fonts-noto-color-emoji \ + nodejs tzdata x11vnc xvfb \ && rm -rf /var/lib/apt/lists/* \ && install -d -m 0755 /opt/druks-browser \ && cd /opt/druks-browser \ @@ -52,6 +53,21 @@ RUN apt-get update \ && install -m 0755 /tmp/pinchtab /usr/local/bin/pinchtab \ && rm -f /tmp/pinchtab /tmp/pinchtab-checksums.txt +# Real Google Chrome, so the login browser presents Chrome's fingerprint rather +# than the bundled chromium's — a login gate reads the difference. Google ships +# no arm64 Linux build, so this is amd64-only; session-launch falls back to the +# bundled chromium where Chrome is absent. +# hadolint ignore=DL3008 +RUN if [ "${TARGETARCH}" = "amd64" ]; then \ + curl -fsSL https://dl.google.com/linux/linux_signing_key.pub \ + | gpg --dearmor -o /etc/apt/keyrings/google-chrome.gpg \ + && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google-chrome.gpg] https://dl.google.com/linux/chrome/deb/ stable main" \ + > /etc/apt/sources.list.d/google-chrome.list \ + && apt-get update \ + && apt-get install -y --no-install-recommends google-chrome-stable \ + && rm -rf /var/lib/apt/lists/*; \ + fi + RUN useradd --create-home --shell /bin/bash druks COPY session-launch session-export /usr/local/bin/ diff --git a/deploy/browser/session-launch b/deploy/browser/session-launch index ef4155dc..56e20d18 100755 --- a/deploy/browser/session-launch +++ b/deploy/browser/session-launch @@ -13,6 +13,11 @@ const META_PATH = path.join(SESSION_ROOT, "state.meta.json"); const PROFILE_ARCHIVE_PATH = path.join(SESSION_ROOT, "state.tar.gz"); const STORAGE_STATE_PATH = path.join(SESSION_ROOT, "state.json"); const PROFILE_PATH = path.join(SESSION_ROOT, "profile"); +// Real Google Chrome when the image carries it (amd64), else the bundled +// chromium (arm64). A login gate reads the Chromium-vs-Chrome brand split, so +// the login browser must be real Chrome wherever it can be. +const CHROME_PATH = "/opt/google/chrome/chrome"; +const channel = fs.existsSync(CHROME_PATH) ? "chrome" : undefined; const RUNTIME_PATH = path.join(SESSION_ROOT, ".runtime"); const PID_PATH = path.join(RUNTIME_PATH, "launcher.pid"); const READY_PATH = path.join(RUNTIME_PATH, "ready.json"); @@ -87,6 +92,7 @@ async function main() { let isReady = false; try { context = await chromium.launchPersistentContext(PROFILE_PATH, { + channel, args: [ "--remote-debugging-address=127.0.0.1", "--remote-debugging-port=9222", From 7ee2ed693069182c2552e4151191ef083dd7bc2d Mon Sep 17 00:00:00 2001 From: Paulo Date: Mon, 17 Aug 2026 19:10:41 +0200 Subject: [PATCH 2/2] Deploy-image changes get a build check on the pull request The sandbox and browser images publish only on push to main, and the two pull-request workflows watch backend and frontend paths, so a change under deploy/ reached main with nothing having built it. Add a pull-request workflow that builds both images (amd64, no push) when their contexts change, so a broken Dockerfile fails the PR rather than the deploy. --- .github/workflows/on-pull-request-images.yml | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/on-pull-request-images.yml diff --git a/.github/workflows/on-pull-request-images.yml b/.github/workflows/on-pull-request-images.yml new file mode 100644 index 00000000..51d56a3a --- /dev/null +++ b/.github/workflows/on-pull-request-images.yml @@ -0,0 +1,49 @@ +name: PR Images + +# The sandbox and browser images publish only on push to main, so a change to +# their build contexts reached main with no pre-merge check. This builds them on +# the pull request — amd64 only and without pushing, so a broken Dockerfile +# fails the PR instead of the deploy. arm64 stays covered by the publish build. +on: + pull_request: + branches: [main] + paths: + - "deploy/sandbox/**" + - "deploy/browser/**" + - ".github/workflows/on-pull-request-images.yml" + +concurrency: + group: on-pull-request-images-${{ github.event.pull_request.number }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + sandbox: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: deploy/sandbox + platforms: linux/amd64 + push: false + cache-from: type=gha,scope=sandbox + + browser: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0 + - uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6 + with: + context: deploy/browser + platforms: linux/amd64 + push: false + cache-from: type=gha,scope=browser