Skip to content

Commit 39fedf1

Browse files
jailer mount propagation test
this test creates two mounts for a rootfs and guest kernel in directories that are mounted in a location that the jailer requires to boot successfully Signed-off-by: Anthony Corletti <[email protected]>
1 parent 3ca2fab commit 39fedf1

File tree

2 files changed

+89
-0
lines changed

2 files changed

+89
-0
lines changed

tests/framework/microvm.py

+24
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import select
1919
import shutil
2020
import signal
21+
import subprocess
2122
import time
2223
import uuid
2324
from collections import namedtuple
@@ -1115,13 +1116,36 @@ def build_from_snapshot(self, snapshot: Snapshot):
11151116
vm.restore_from_snapshot(snapshot, resume=True)
11161117
return vm
11171118

1119+
def unmount(self, path: str) -> None:
1120+
"""Unmounts a path with `umount` in a subprocess"""
1121+
try:
1122+
subprocess.run(["umount", path], check=True)
1123+
except subprocess.CalledProcessError:
1124+
print(f"Failed to unmount {path}")
1125+
1126+
def get_mounts_at_path(self, path: str) -> list:
1127+
"""Get all mounts for a given path. Returns a list of mount points."""
1128+
try:
1129+
with open("/proc/mounts", "r", encoding="utf-8") as f:
1130+
return [
1131+
line.split()[1]
1132+
for line in f
1133+
if line.split()[1].startswith(os.path.abspath(path))
1134+
]
1135+
except FileNotFoundError:
1136+
return False # /proc/mounts may not exist on some systems
1137+
11181138
def kill(self):
11191139
"""Clean up all built VMs"""
11201140
for vm in self.vms:
11211141
vm.kill()
11221142
vm.jailer.cleanup()
11231143
chroot_base_with_id = vm.jailer.chroot_base_with_id()
11241144
if len(vm.jailer.jailer_id) > 0 and chroot_base_with_id.exists():
1145+
mounts = self.get_mounts_at_path(chroot_base_with_id)
1146+
if mounts:
1147+
for mounted_path in mounts:
1148+
self.unmount(mounted_path)
11251149
shutil.rmtree(chroot_base_with_id)
11261150
vm.netns.cleanup()
11271151

tests/integration_tests/security/test_jail.py

+65
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,68 @@ def test_cgroupsv2_written_only_once(uvm_plain, cgroups_info):
664664
assert len(write_lines) == 1
665665
assert len(mkdir_lines) != len(cgroups), "mkdir equal to number of cgroups"
666666
assert len(mkdir_lines) == 1
667+
668+
669+
def test_jail_mount(uvm_plain):
670+
"""
671+
Test that the jailer mounts are propagated to the root mount namespace.
672+
"""
673+
# setup the microvm
674+
test_microvm = uvm_plain
675+
676+
chroot_base = test_microvm.jailer.chroot_base
677+
# make a directory to hold the original content
678+
original_content_dir = chroot_base / "original_content"
679+
original_content_dir.mkdir(parents=True, exist_ok=True)
680+
681+
# make a directory to hold the jailed content
682+
jailed_content_dir = Path(test_microvm.jailer.chroot_path())
683+
jailed_content_dir.mkdir(parents=True, exist_ok=True)
684+
685+
# assert that the directory was created
686+
assert original_content_dir.exists()
687+
assert jailed_content_dir.exists()
688+
689+
# create the files that will be mounted
690+
test_data = original_content_dir / "test_data"
691+
test_data.touch()
692+
assert test_data.exists()
693+
test_data.write_text("test_data")
694+
assert test_data.read_text() == "test_data"
695+
696+
jailed_test_data = jailed_content_dir / "test_data"
697+
jailed_test_data.touch()
698+
assert jailed_test_data.exists()
699+
assert jailed_test_data.read_text() == ""
700+
701+
# mount the data
702+
subprocess.run(["mount", "--bind", test_data, jailed_test_data], check=True)
703+
704+
# spawn the microvm
705+
test_microvm.spawn()
706+
test_microvm.basic_config()
707+
708+
# set params for the microvm
709+
test_microvm.jailer.gid = 0
710+
test_microvm.jailer.uid = 0
711+
test_microvm.jailer.daemonize = True
712+
test_microvm.extra_args = {"seccomp-level": 0}
713+
test_microvm.add_net_iface()
714+
test_microvm.start()
715+
716+
# mock jailer
717+
for cmd in [
718+
"unshare --mount --propagation unchanged",
719+
"mount --make-rslave /",
720+
f"mount --rbind {jailed_content_dir} {jailed_content_dir}",
721+
]:
722+
subprocess.run(cmd.split(), check=True, capture_output=True)
723+
724+
# check that the file output is there
725+
output = subprocess.run(
726+
f"cat {jailed_content_dir}/test_data",
727+
shell=True,
728+
check=True,
729+
capture_output=True,
730+
)
731+
assert output.stdout.decode() == "test_data"

0 commit comments

Comments
 (0)