diff --git a/README.md b/README.md index 6f87243..45a49de 100644 --- a/README.md +++ b/README.md @@ -135,13 +135,20 @@ the console already set to force install, and Chrome keeps it updated. Run on its own at any time: ```powershell -powershell -NoProfile -ExecutionPolicy Bypass -File scripts\enroll-chrome-management.ps1 -Token +powershell -NoProfile -ExecutionPolicy Bypass -File scripts\enroll-chrome-management.ps1 ``` ```bash -sudo scripts/enroll-chrome-management.sh +sudo scripts/enroll-chrome-management.sh ``` +The enrolment token is built into both scripts — a token is reusable across any +number of machines — so there is nothing to type. Pass one explicitly to use a +different org. Chrome itself is installed first if the machine hasn't got it, +from Google's own enterprise MSI on Windows and the official .deb on Linux; +Chromium is not accepted as a substitute, because it has no cloud-management +support and can never be enrolled. + **Enrolment is not optional.** No Chromium browser will force-install an off-store extension on an unmanaged machine, whatever the registry says — it reads the policy and blanks it. Enrolling is what makes Chrome managed without diff --git a/scripts/enroll-chrome-management.ps1 b/scripts/enroll-chrome-management.ps1 index 9ad5f69..7eebf6c 100644 --- a/scripts/enroll-chrome-management.ps1 +++ b/scripts/enroll-chrome-management.ps1 @@ -21,8 +21,14 @@ #> [CmdletBinding()] param( - [Parameter(Mandatory = $true, ParameterSetName = 'Enrol')] - [string]$Token, + # Defaulted so setup is hands-off. An enrolment token is reusable and enrols + # any number of browsers, which is what makes a baked-in default work at all. + # It is a modest secret: anyone holding it can enrol *their* browser into the + # org, which gets them our policies and a row in the console — it grants no + # access to the Google account or to anything else. Rotate it in Devices > + # Chrome > Managed browsers and change it here. + [Parameter(ParameterSetName = 'Enrol')] + [string]$Token = 'ce4fa6cd-e940-45f6-9086-85075d6cff34', # Refuse to start Chrome at all if enrolment fails. Sound on a fleet, risky # on a trading PC: a revoked token would leave you with no browser. @@ -34,7 +40,10 @@ param( # Fail instead of asking for elevation. Also the loop-breaker: the elevated # copy is launched with this set, so it can never spawn another. - [switch]$NoElevate + [switch]$NoElevate, + + # Fail rather than installing Chrome when it is missing. + [switch]$SkipChromeInstall ) $ErrorActionPreference = 'Stop' @@ -52,6 +61,7 @@ if (-not ([Security.Principal.WindowsPrincipal]$identity).IsInRole( # arguments are already parsed and can be passed on intact. Write-Host 'Asking for administrator rights — the enrolment token is machine-level.' $psArgs = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', "`"$PSCommandPath`"", '-NoElevate') + if ($SkipChromeInstall) { $psArgs += '-SkipChromeInstall' } if ($Remove) { $psArgs += '-Remove' } else { $psArgs += @('-Token', $Token) @@ -63,6 +73,38 @@ if (-not ([Security.Principal.WindowsPrincipal]$identity).IsInRole( exit $elevated.ExitCode } +# ── Chrome itself ──────────────────────────────────────────────────────────── +# Enrolling a browser that is not installed would write a token nothing ever +# reads. Chromium is deliberately not accepted as a substitute: it has no +# cloud-management support, so it can never be enrolled and would stay subject +# to the off-store restriction forever. +function Find-Chrome { + foreach ($exe in @("$env:ProgramFiles\Google\Chrome\Application\chrome.exe", + "${env:ProgramFiles(x86)}\Google\Chrome\Application\chrome.exe", + "$env:LOCALAPPDATA\Google\Chrome\Application\chrome.exe")) { + if ($exe -and (Test-Path $exe)) { return $exe } + } + return $null +} + +if (-not $Remove -and -not (Find-Chrome)) { + if ($SkipChromeInstall) { throw 'Google Chrome is not installed, and -SkipChromeInstall was given.' } + + # Google's own enterprise MSI, which installs machine-wide and silently. We + # are already elevated by this point, so this needs no second prompt. + $msi = Join-Path $env:TEMP 'googlechromestandaloneenterprise64.msi' + Write-Host 'Google Chrome is not installed — downloading it from Google (about 120 MB).' + Invoke-WebRequest -Uri 'https://dl.google.com/dl/chrome/install/googlechromestandaloneenterprise64.msi' ` + -OutFile $msi -UseBasicParsing + + Write-Host 'Installing Chrome...' + $msiexec = Start-Process msiexec.exe -Wait -PassThru -ArgumentList @('/i', "`"$msi`"", '/qn', '/norestart') + Remove-Item $msi -ErrorAction SilentlyContinue + if ($msiexec.ExitCode -ne 0) { throw "Chrome install failed (msiexec exit $($msiexec.ExitCode))." } + if (-not (Find-Chrome)) { throw 'Chrome still not found after installing.' } + Write-Host 'Chrome installed.' +} + $key = 'HKLM:\SOFTWARE\Policies\Google\Chrome' if (-not (Test-Path $key)) { New-Item -Path $key -Force | Out-Null } diff --git a/scripts/enroll-chrome-management.sh b/scripts/enroll-chrome-management.sh index 53ef7f4..d4cd7d3 100755 --- a/scripts/enroll-chrome-management.sh +++ b/scripts/enroll-chrome-management.sh @@ -5,13 +5,21 @@ # from a file under /etc at startup, so this needs root, and it applies to every # user on the machine. # -# sudo scripts/enroll-chrome-management.sh +# sudo scripts/enroll-chrome-management.sh # the built-in token +# sudo scripts/enroll-chrome-management.sh # a different one # sudo scripts/enroll-chrome-management.sh --remove # # Chrome only — Chromium builds have no cloud-management support, so a Chromium # install cannot be enrolled and stays subject to the off-store restriction. set -euo pipefail +# Defaulted so setup is hands-off. A token is reusable and enrols any number of +# browsers, which is what makes a baked-in default work. It is a modest secret: +# anyone holding it can enrol *their* browser into the org — our policies and a +# row in the console — and nothing more. Rotate it in Devices > Chrome > Managed +# browsers and change it here. +DEFAULT_TOKEN=ce4fa6cd-e940-45f6-9086-85075d6cff34 + DIR=/etc/opt/chrome/policies/enrollment TOKEN_FILE="$DIR/CloudManagementEnrollmentToken" @@ -19,7 +27,6 @@ info() { printf ' %s\n' "$1"; } fail() { printf '\n\033[1;31mERROR: %s\033[0m\n' "$1" >&2; exit 1; } [ "$(id -u)" -eq 0 ] || fail "run this with sudo — the token lives under /etc" -[ $# -ge 1 ] || fail "usage: $0 | --remove" if [ "$1" = "--remove" ]; then rm -f "$TOKEN_FILE" "$DIR/CloudManagementEnrollmentOptions" @@ -30,12 +37,33 @@ if [ "$1" = "--remove" ]; then exit 0 fi -[ -d /etc/opt/chrome ] || fail "no /etc/opt/chrome — this is for Google Chrome, not Chromium" +TOKEN="${1:-$DEFAULT_TOKEN}" + +# Enrolling a browser that is not installed would write a token nothing reads. +# Chromium is not a substitute: it has no cloud-management support, so it can +# never be enrolled, and Google publishes Chrome for x86_64 only — on a Pi there +# is no version of this that works, and saying so beats installing something +# that cannot do the job. +if ! command -v google-chrome >/dev/null 2>&1 && [ ! -x /opt/google/chrome/chrome ]; then + ARCH=$(uname -m) + [ "$ARCH" = "x86_64" ] || fail "Google Chrome is not published for $ARCH. Chromium cannot be enrolled — this machine cannot run the extension." + command -v apt-get >/dev/null || fail "Chrome is missing and this is not an apt system — install Google Chrome, then re-run" + + info "Google Chrome is not installed — fetching it from Google (about 120 MB)" + DEB=$(mktemp --suffix=.deb) + curl -fsSL -o "$DEB" https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb \ + || fail "could not download Chrome" + # apt rather than dpkg: it pulls the dependencies a bare dpkg -i would leave + # half-configured. + DEBIAN_FRONTEND=noninteractive apt-get install -y "$DEB" >/dev/null || fail "Chrome install failed" + rm -f "$DEB" + info "Chrome installed" +fi mkdir -p "$DIR" # The file must contain the token and nothing else: no quotes, no newline noise, # no filename extension. Chrome reads it verbatim. -printf '%s' "$1" > "$TOKEN_FILE" +printf '%s' "$TOKEN" > "$TOKEN_FILE" chmod 644 "$TOKEN_FILE" info "wrote $TOKEN_FILE" diff --git a/setup-linux.sh b/setup-linux.sh index 1fe7b1d..186c0ab 100755 --- a/setup-linux.sh +++ b/setup-linux.sh @@ -150,16 +150,15 @@ echo info "[7/7] Chrome extension" info " Enrols this machine's Chrome, after which the extension installs" info " itself from the Admin console and stays updated on its own." -info " The token is in the Admin console under Devices > Chrome >" -info " Managed browsers. Blank to skip." -read -r -p " Enrollment token: " ENROLTOKEN +info " Installs Chrome first if it is not here yet. Needs sudo." +read -r -p " Set this up now? [Y/n] " DOEXT EXTPOLICY=0 -if [ -n "${ENROLTOKEN:-}" ]; then - if sudo bash scripts/enroll-chrome-management.sh "$ENROLTOKEN"; then EXTPOLICY=1; else - warn "enrolment failed - re-run: sudo scripts/enroll-chrome-management.sh " +if [ "${DOEXT,,}" != "n" ]; then + if sudo bash scripts/enroll-chrome-management.sh; then EXTPOLICY=1; else + warn "enrolment failed - re-run: sudo scripts/enroll-chrome-management.sh" fi else - info " Skipped. Run: sudo scripts/enroll-chrome-management.sh " + info " Skipped. Run: sudo scripts/enroll-chrome-management.sh" fi # ── summary ───────────────────────────────────────────────────────────────── @@ -186,7 +185,7 @@ if [ "$EXTPOLICY" -eq 1 ]; then info " and the extension installs itself within a minute or two." else info " - Enrol Chrome so it can install the extension:" - info " sudo scripts/enroll-chrome-management.sh " + info " sudo scripts/enroll-chrome-management.sh" fi info " - Add your firm credentials on the dashboard's Settings page" if [ -n "${WAYLAND_DISPLAY:-}" ] && [ -z "${DISPLAY:-}" ]; then diff --git a/setup-windows.bat b/setup-windows.bat index d93a9ff..ffc9426 100644 --- a/setup-windows.bat +++ b/setup-windows.bat @@ -335,14 +335,14 @@ echo. echo [7/7] Chrome extension echo Enrols this machine's Chrome, after which the extension installs echo itself from the Admin console and stays updated on its own. -echo The token is in the Admin console under -echo Devices ^> Chrome ^> Managed browsers. Blank to skip. -set "ENROLTOKEN=" -set /p "ENROLTOKEN= Enrollment token: " -if not defined ENROLTOKEN goto :ext_skipped -REM The token is machine-scoped, so this raises a UAC prompt and only this. The -REM script asks for elevation itself; nothing else in this installer needs it. -powershell -NoProfile -ExecutionPolicy Bypass -File "%TARGET%\scripts\enroll-chrome-management.ps1" -Token "%ENROLTOKEN%" +echo Installs Chrome first if it is not here yet. +set "DOEXT=" +set /p "DOEXT= Set this up now? [Y/n] " +if /i "%DOEXT%"=="n" goto :ext_skipped +REM The enrolment token is built into the script, so there is nothing to type. +REM It is machine-scoped, so this raises a UAC prompt - the only one in this +REM installer, and the script asks for it itself. +powershell -NoProfile -ExecutionPolicy Bypass -File "%TARGET%\scripts\enroll-chrome-management.ps1" if errorlevel 1 ( echo [!] Enrolment failed. Re-run scripts\enroll-chrome-management.ps1 with echo a fresh token from the Admin console to try again. @@ -353,8 +353,8 @@ if errorlevel 1 ( goto :ext_done :ext_skipped -echo Skipped. Run scripts\enroll-chrome-management.ps1 -Token ^ -echo later; until then this machine gets no extension. +echo Skipped. Run scripts\enroll-chrome-management.ps1 later; +echo until then this machine gets no extension. set "EXTPOLICY=0" :ext_done @@ -400,7 +400,7 @@ if "%EXTPOLICY%"=="1" ( echo and the extension installs itself within a minute or two. ) else ( echo - Enrol Chrome so it can install the extension: - echo scripts\enroll-chrome-management.ps1 -Token ^ + echo scripts\enroll-chrome-management.ps1 ) echo - Add your firm credentials on the dashboard's Settings page echo.