Merge pull request #12323 from atoslins/fix-windows-vm-setgid

Clear setgid when hardening Windows VM directories
This commit is contained in:
David Heinemeier Hansson authored and GitHub committed 2026-10-04 07:47:20 -04:00
commit 35a0d59053
3 files changed
+34 -2

No files matched your search

+5 -2
View File
@@ -580,7 +580,9 @@ prepare_caller_mounts() {
# Privacy is an explicit preflight step for both already-pinned sources, not
# a side effect halfway through the two-mount transaction. Old umask-022
# installs are hardened together before either Docker-facing anchor changes.
chmod 0700 -- "/proc/$BASHPID/fd/$storage_fd" "/proc/$BASHPID/fd/$shared_fd" || {
# The mode is symbolic because a numeric chmod keeps setuid/setgid on a
# directory, and dockur marks an initially empty /shared setgid (2777).
chmod u=rwx,go=,a-s -- "/proc/$BASHPID/fd/$storage_fd" "/proc/$BASHPID/fd/$shared_fd" || {
exec {storage_fd}<&-
exec {shared_fd}<&-
return 1
@@ -590,6 +592,7 @@ prepare_caller_mounts() {
if [[ $storage_mode != 700 || $shared_mode != 700 ]]; then
exec {storage_fd}<&-
exec {shared_fd}<&-
echo "omarchy-windows-vm: could not make the VM data directories private: $LEGACY_STORAGE is mode ${storage_mode:-unknown}, $LEGACY_SHARED is mode ${shared_mode:-unknown} (expected 700)" >&2
return 1
fi
@@ -1015,7 +1018,7 @@ prepare_user_mount_sources() {
echo "omarchy-windows-vm: storage and shared must be different directories" >&2
return 1
}
chmod 0700 -- "$storage" "$shared"
chmod u=rwx,go=,a-s -- "$storage" "$shared"
}
storage_space_path() {
+2
View File
@@ -112,6 +112,8 @@ pass "pkexec target is only the canonical packaged regular file, never a PATH sy
reset_case
external_shared="$TMPDIR/external-shared"
mkdir -m 0755 -p "$HOME/.windows" "$external_shared" "$HOME/.config/windows"
# dockur leaves an initially empty share setgid, which a numeric chmod keeps.
chmod 2777 "$external_shared"
ln -s "$external_shared" "$HOME/Windows"
touch "$HOME/.windows/existing-disk" "$external_shared/existing-shared-file"
LEGACY_COMPOSE_FILE="$HOME/.config/windows/docker-compose.yml"
@@ -143,6 +143,33 @@ chown root:root "$USERS_DIR"
mounts_ready || fail "restored production boundaries were rejected"
pass "root rejects wrong-owned and group-writable production mount boundaries without mutation"
# dockur marks an empty /shared setgid (chmod 2777) on first boot, and a numeric
# chmod keeps setuid/setgid on directories. Hardening has to clear the special
# bits behind live anchors and again when the binds are recreated after reboot.
chmod 2777 /home/shared-target
chmod 6755 /home/storage-target
with_vm_lock prepare_caller_mounts || fail "root rejected setgid sources behind live anchors"
mounts_ready || fail "final guard rejected re-hardened setgid sources"
umount "$EXPECTED_SHARED"
umount "$EXPECTED_STORAGE"
chmod 2777 /home/shared-target
chmod 6755 /home/storage-target
with_vm_lock prepare_caller_mounts || fail "root could not rebind setgid sources"
[[ $(command stat -Lc '%u:%a' "$EXPECTED_STORAGE") == 1000:700 &&
$(command stat -Lc '%u:%a' "$EXPECTED_SHARED") == 1000:700 ]] || fail "setgid sources were not hardened to 0700"
mounts_ready || fail "final guard rejected rebound setgid sources"
pass "hardening clears the setuid/setgid bits a numeric chmod keeps on directories"
# When hardening still leaves a source non-private, the rejection names the
# directory and its mode instead of failing silently.
chmod() { command chmod "$@" && command chmod g+s /home/shared-target; }
hardening_error=$(with_vm_lock prepare_caller_mounts 2>&1) && fail "root accepted a source that stayed setgid after hardening"
unset -f chmod
[[ $hardening_error == *"/home/alice/Windows is mode 2700"* ]] || fail "setgid rejection did not name the directory and mode: $hardening_error"
chmod g-s /home/shared-target
mounts_ready || fail "verified anchors were disturbed by the rejected hardening"
pass "a source that cannot be made private is rejected with its path and mode"
expected_space=$(command df -P -- /home/storage-target | awk 'NR==2 {print int($4/1024/1024)}')
actual_space=$(available_storage_gb)
[[ $actual_space == "$expected_space" ]] || fail "disk-space helper did not measure the storage target filesystem"