Skip to content

Commit 89c9346

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 89c9346

File tree

2 files changed

+135
-0
lines changed

2 files changed

+135
-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

+111
Original file line numberDiff line numberDiff line change
@@ -664,3 +664,114 @@ 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, guest_kernel, rootfs_rw):
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 / str(test_microvm.id)[:8]
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 = chroot_base / "firecracker" / "testbindmount" / "root"
683+
jailed_content_dir.mkdir(parents=True, exist_ok=True)
684+
jailed_content_dir_ssh = jailed_content_dir / ".ssh"
685+
jailed_content_dir_ssh.mkdir(parents=True, exist_ok=True)
686+
687+
# assert that the directory was created
688+
assert original_content_dir.exists()
689+
assert jailed_content_dir.exists()
690+
assert jailed_content_dir_ssh.exists()
691+
692+
# add the ssh key to the jailed content dir so we can ssh into the microvm
693+
pub_key_contents = Path("/srv/img/x86_64/id_rsa.pub").read_text()
694+
ssh_key = jailed_content_dir_ssh / "authorized_keys"
695+
ssh_key.write_text(pub_key_contents)
696+
697+
# create the files that will be mounted
698+
test_data = original_content_dir / "test_data"
699+
test_data.touch()
700+
assert test_data.exists()
701+
test_data.write_text("test_data")
702+
assert test_data.read_text() == "test_data"
703+
704+
os.system(f"cp {guest_kernel} {original_content_dir}")
705+
os.system(f"cp {rootfs_rw} {original_content_dir}")
706+
assert (original_content_dir / guest_kernel.name).exists()
707+
assert (original_content_dir / rootfs_rw.name).exists()
708+
709+
jailed_test_data = jailed_content_dir / "test_data"
710+
jailed_test_data.touch()
711+
assert jailed_test_data.exists()
712+
assert jailed_test_data.read_text() == ""
713+
jailed_kernel = jailed_content_dir / guest_kernel.name
714+
jailed_rootfs = jailed_content_dir / rootfs_rw.name
715+
jailed_kernel.touch()
716+
jailed_rootfs.touch()
717+
assert (jailed_content_dir / guest_kernel.name).exists()
718+
assert (jailed_content_dir / rootfs_rw.name).exists()
719+
720+
pid_file = jailed_content_dir / "firecracker.pid"
721+
pid_file.touch()
722+
723+
# mount the data
724+
subprocess.run(
725+
["mount", "--bind", test_data, jailed_test_data],
726+
check=True,
727+
)
728+
subprocess.run(
729+
[
730+
"mount",
731+
"--bind",
732+
original_content_dir / guest_kernel.name,
733+
jailed_content_dir / guest_kernel.name,
734+
],
735+
check=True,
736+
)
737+
subprocess.run(
738+
[
739+
"mount",
740+
"--bind",
741+
original_content_dir / rootfs_rw.name,
742+
jailed_content_dir / rootfs_rw.name,
743+
],
744+
check=True,
745+
)
746+
747+
# spawn the microvm
748+
test_microvm.spawn()
749+
test_microvm.basic_config()
750+
# set params for the microvm
751+
test_microvm.kernel_file = str(jailed_content_dir / guest_kernel.name)
752+
test_microvm.rootfs_file = str(jailed_content_dir / rootfs_rw.name)
753+
test_microvm.jailer.chroot_base = chroot_base
754+
test_microvm.jailer.jailer_id = "testbindmount"
755+
test_microvm.jailer.gid = 0
756+
test_microvm.jailer.uid = 0
757+
test_microvm.jailer.daemonize = True
758+
test_microvm.extra_args = {"seccomp-level": 0}
759+
test_microvm.add_net_iface()
760+
test_microvm.start()
761+
762+
# # and assert the content is there in the microvm
763+
# _, stdout, stderr = test_microvm.ssh.run("cat /root/test_data")
764+
# assert stdout == "test_data"
765+
# assert stderr == ""
766+
767+
for cmd in [
768+
"unshare --mount --propagation unchanged",
769+
"mount --make-rslave /",
770+
f"mount --rbind {jailed_content_dir} {jailed_content_dir}",
771+
f"ls -al {jailed_content_dir}",
772+
]:
773+
_, stdout, stderr = test_microvm.ssh.run(cmd)
774+
print("--------------------")
775+
print("stdout", stdout)
776+
print("stderr", stderr)
777+
print("--------------------")

0 commit comments

Comments
 (0)