146 lines
6.0 KiB
Bash
Executable File
146 lines
6.0 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Image-Backed /proc and /storage Parity Tests
|
|
#
|
|
# Regression coverage for two asymmetries between bare-VM and --image machines:
|
|
#
|
|
# 1. --image froze a chunk of /proc read-only (runc's readonlyPaths), so
|
|
# docker-in-VM could not write /proc/sys/net/ipv4/ip_forward.
|
|
# 2. --image did not expose the per-VM /storage disk inside the container, so
|
|
# the docker-in-VM bind-mount pattern
|
|
# (`mount --bind /storage/docker /var/lib/docker`) broke.
|
|
#
|
|
# Both are fixed by treating a privileged (default) image machine like a bare
|
|
# VM: /proc is unrestricted and /storage is bind-mounted into the container.
|
|
# An unprivileged image machine keeps the full runc hardening.
|
|
#
|
|
# Part of the smolvm test suite. Run with:
|
|
# ./tests/test_machine_image_proc_storage.sh
|
|
#
|
|
|
|
source "$(dirname "$0")/common.sh"
|
|
init_smolvm
|
|
|
|
log_info "Pre-flight cleanup: killing orphan processes..."
|
|
kill_orphan_smolvm_processes
|
|
|
|
trap cleanup_machine EXIT
|
|
|
|
echo ""
|
|
echo "=========================================="
|
|
echo " Image-Backed /proc + /storage Parity"
|
|
echo "=========================================="
|
|
echo ""
|
|
|
|
# A privileged --image VM must have a writable /proc/sys, like a bare VM, so an
|
|
# init system / dockerd can set sysctls. Before the fix crun applied runc's
|
|
# readonlyPaths and this write failed with EROFS.
|
|
test_image_proc_sys_is_writable() {
|
|
local out
|
|
out=$(run_with_timeout 120 $SMOLVM machine run --net --image alpine -- \
|
|
sh -c 'echo 1 > /proc/sys/net/ipv4/ip_forward && cat /proc/sys/net/ipv4/ip_forward' 2>&1)
|
|
local rc=$?
|
|
[[ $rc -eq 124 ]] && { echo "FAIL: timed out booting image VM"; return 1; }
|
|
echo "$out" | grep -qx '1' || {
|
|
echo "FAIL: /proc/sys/net/ipv4/ip_forward not writable under --image"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Corollary: /proc/sys must not be a read-only bind in privileged mode. runc's
|
|
# readonlyPaths shows up as a `ro` mount line on the path; its absence means the
|
|
# path inherits the writable top-level /proc mount.
|
|
test_image_proc_sys_not_readonly_bind() {
|
|
local out
|
|
out=$(run_with_timeout 90 $SMOLVM machine run --net --image alpine -- \
|
|
sh -c 'mount | grep " /proc/sys " || true' 2>&1)
|
|
local rc=$?
|
|
[[ $rc -eq 124 ]] && { echo "FAIL: timed out booting image VM"; return 1; }
|
|
if echo "$out" | grep -q '\bro\b'; then
|
|
echo "FAIL: /proc/sys still mounted read-only under --image"
|
|
echo " output: $out"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# A privileged --image VM must see the per-VM /storage ext4 disk inside the
|
|
# container, so /storage/* resolves to the disk exactly as in a bare VM.
|
|
test_image_storage_disk_visible() {
|
|
local out
|
|
out=$(run_with_timeout 90 $SMOLVM machine run --net --image alpine -- \
|
|
sh -c 'mount | grep " /storage " || true' 2>&1)
|
|
local rc=$?
|
|
[[ $rc -eq 124 ]] && { echo "FAIL: timed out booting image VM"; return 1; }
|
|
echo "$out" | grep -q ' /storage ' || {
|
|
echo "FAIL: /storage not mounted inside --image container"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
echo "$out" | grep -q 'ext4' || {
|
|
echo "FAIL: /storage is not the ext4 disk under --image"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# The docker-in-VM bind-mount pattern works under --image. Binding
|
|
# /var/lib/docker onto /storage/docker must land on the ext4 disk (so dockerd's
|
|
# overlay2 nests correctly), proven by a file written through the bind appearing
|
|
# on the /storage side.
|
|
test_image_docker_bind_resolves_to_storage() {
|
|
local out
|
|
out=$(run_with_timeout 90 $SMOLVM machine run --net --image alpine -- sh -c '
|
|
set -e
|
|
mkdir -p /storage/docker /var/lib/docker
|
|
mount --bind /storage/docker /var/lib/docker
|
|
: > /var/lib/docker/probe
|
|
test -f /storage/docker/probe
|
|
# Confirm /var/lib/docker now resolves onto the ext4 storage disk.
|
|
mount | grep " /var/lib/docker " | grep -q ext4
|
|
echo BIND_OK' 2>&1)
|
|
local rc=$?
|
|
[[ $rc -eq 124 ]] && { echo "FAIL: timed out booting image VM"; return 1; }
|
|
echo "$out" | grep -q 'BIND_OK' || {
|
|
echo "FAIL: bind-mount of /var/lib/docker onto /storage did not resolve to ext4"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
# Guard the security boundary: an UNPRIVILEGED image container must NOT get the
|
|
# relaxed /proc or the /storage disk — the hardening is retained for untrusted
|
|
# code. `machine run --unprivileged` opts into the restricted profile.
|
|
test_unprivileged_image_keeps_hardening() {
|
|
# /proc/sys read-only (write must fail) and /storage absent.
|
|
local out
|
|
out=$(run_with_timeout 90 $SMOLVM machine run --net --unprivileged --image alpine -- sh -c '
|
|
if echo 1 > /proc/sys/net/ipv4/ip_forward 2>/dev/null; then echo PROC_WRITABLE; else echo PROC_RO; fi
|
|
if mount | grep -q " /storage "; then echo STORAGE_VISIBLE; else echo STORAGE_HIDDEN; fi' 2>&1)
|
|
local rc=$?
|
|
[[ $rc -eq 124 ]] && { echo "FAIL: timed out booting unprivileged image VM"; return 1; }
|
|
# If --unprivileged is unsupported by this build, skip rather than fail.
|
|
if echo "$out" | grep -qiE 'error|unexpected argument|unknown'; then
|
|
echo "SKIP: --unprivileged not supported by this build: $out"
|
|
return 0
|
|
fi
|
|
echo "$out" | grep -q 'PROC_RO' || {
|
|
echo "FAIL: unprivileged container got a writable /proc/sys (hardening lost)"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
echo "$out" | grep -q 'STORAGE_HIDDEN' || {
|
|
echo "FAIL: unprivileged container saw /storage (disk leak)"
|
|
echo " output: $out"
|
|
return 1
|
|
}
|
|
}
|
|
|
|
run_test "Image /proc/sys is writable (ip_forward settable)" test_image_proc_sys_is_writable || true
|
|
run_test "Image /proc/sys is not a read-only bind" test_image_proc_sys_not_readonly_bind || true
|
|
run_test "Image container sees the /storage ext4 disk" test_image_storage_disk_visible || true
|
|
run_test "Docker bind-mount onto /storage resolves to ext4" test_image_docker_bind_resolves_to_storage || true
|
|
run_test "Unprivileged image keeps /proc + /storage hardening" test_unprivileged_image_keeps_hardening || true
|
|
|
|
print_summary "Image /proc + /storage Parity Tests"
|