Backport of the docker group removal (PR #8056, merged to quattro as b5ded31e)
onto the v4-0-1 release branch.
The docker group is root-equivalent: anything in it can docker run -v /:/host
and rewrite the host as root with no password. On a single-user box that is not
an escalation -- the owner is already a wheel user -- but it hands any code
running as the user, a rogue plugin or a poisoned dependency, a silent,
headless, passwordless path to root that sudo's password prompt would otherwise
gate.
Stop granting the group by default. The daemon still runs, the Docker TUI and
the Windows VM reach it through a polkit prompt, and the plain docker CLI runs
under sudo. Sudoless Docker becomes a warned opt-in under Setup > Security, and
no automatic path re-grants it: install and first-boot provisioning never record
or apply the group, and the Quattro upgrade no longer adds it. A migration takes
existing installs out of the group, reusing omarchy-remove-security-sudoless-
docker so the change and its notice have one source of truth.
The Windows VM keeps needing the root daemon for a privileged container, so it
runs without the group without becoming a new way in: the compose moves to a
root-owned directory written only by an elevated, input-validated writer, volume
paths are rebuilt from $HOME on migration rather than trusted from the
user-writable legacy file, the privileged sub-action is checked against an
allowlist before dispatch, pkexec elevates a verified root-owned command path,
mount sources are refused when they are or resolve through a symlink, and the
guest password moves to a private 0600 per-user file instead of a
world-readable compose. Existing installs auto-migrate the VM without a
redownload.
Two files had diverged from quattro and were resolved by hand:
bin/omarchy-windows-vm -- v4-0-1 still carries the "Starting Windows VM" toast
that #7585 dropped on quattro, and the new start path has no user-side status
check to hang it on: after this change the user cannot inspect the container
without privilege, which is the whole point. Took quattro's version. #7585's
reason holds here too -- the shell shows its own "Launching Windows…" OSD until
the RDP window appears (shell/services/AppLibrary.qml) -- and the failure
notification stays. The file is now byte-identical to quattro.
manual/28-windows-vm.md -- took the new paragraph on the root-owned compose,
without the neighbouring OEM-key paragraph, which documents omarchy windows key,
a command quattro has and this branch does not.
test/shell passes here: 186 files, including the three this adds.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DEMYa9UWtroz93DhMTtcV
62 lines
2.8 KiB
Bash
62 lines
2.8 KiB
Bash
#!/bin/bash
|
|
#
|
|
# The install scripts that grant group memberships must record them in the provisioning
|
|
# groups file (for first-boot user creation and factory reset) and only call
|
|
# usermod when the install user actually exists.
|
|
#
|
|
# Docker is deliberately excluded: the docker group is root-equivalent, so it is
|
|
# no longer granted at install time (opt in with omarchy-setup-security-sudoless-docker).
|
|
|
|
set -euo pipefail
|
|
|
|
source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"
|
|
|
|
TMPDIR=$(mktemp -d)
|
|
trap 'rm -rf "$TMPDIR"' EXIT
|
|
|
|
export OMARCHY_PROVISIONING_DIR="$TMPDIR/provisioning"
|
|
|
|
# Stub getent/usermod: the fake system knows only the user "existing".
|
|
mkdir -p "$TMPDIR/bin"
|
|
cat >"$TMPDIR/bin/getent" <<'STUB'
|
|
#!/bin/bash
|
|
[[ $1 == passwd && $2 == existing ]] && { echo "existing:x:1000:1000::/home/existing:/bin/bash"; exit 0; }
|
|
exit 2
|
|
STUB
|
|
cat >"$TMPDIR/bin/usermod" <<STUB
|
|
#!/bin/bash
|
|
echo "\$@" >>"$TMPDIR/usermod.calls"
|
|
STUB
|
|
chmod +x "$TMPDIR/bin/getent" "$TMPDIR/bin/usermod"
|
|
export PATH="$TMPDIR/bin:$PATH"
|
|
|
|
# No install user (deferred-provisioning install): input recorded, usermod not called.
|
|
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/config/docker.sh"
|
|
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/hardware/input-group.sh"
|
|
|
|
[[ -f $OMARCHY_PROVISIONING_DIR/groups ]] || fail "groups file written without an install user"
|
|
grep -qxF input "$OMARCHY_PROVISIONING_DIR/groups" || fail "input group recorded"
|
|
[[ ! -f $TMPDIR/usermod.calls ]] || fail "usermod not called without an install user"
|
|
pass "deferred provisioning records groups without calling usermod"
|
|
|
|
# The docker group is root-equivalent and must never be granted automatically.
|
|
! grep -qxF docker "$OMARCHY_PROVISIONING_DIR/groups" || fail "docker group must not be recorded"
|
|
pass "docker group is not recorded at install"
|
|
|
|
# Missing user (defensive): no usermod either.
|
|
OMARCHY_INSTALL_USER=ghost bash -eE "$ROOT/install/hardware/input-group.sh"
|
|
[[ ! -f $TMPDIR/usermod.calls ]] || fail "usermod not called for a missing user"
|
|
pass "missing install user defers group grants"
|
|
|
|
# Re-running never duplicates entries.
|
|
OMARCHY_INSTALL_USER="" bash -eE "$ROOT/install/hardware/input-group.sh"
|
|
[[ $(grep -cxF input "$OMARCHY_PROVISIONING_DIR/groups") == 1 ]] || fail "input group recorded once"
|
|
pass "group recording is idempotent"
|
|
|
|
# Existing user: usermod applies the recorded groups, and docker is never among them.
|
|
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/config/docker.sh"
|
|
OMARCHY_INSTALL_USER=existing bash -eE "$ROOT/install/hardware/input-group.sh"
|
|
grep -qx -- "-aG input existing" "$TMPDIR/usermod.calls" || fail "usermod grants input to the install user"
|
|
! grep -q -- "docker" "$TMPDIR/usermod.calls" || fail "usermod must not grant docker to the install user"
|
|
pass "existing install user gets input but never docker"
|