Let vigil run without sudo.
Now using user namespaces which allow unprivileged users to create the sandbox.
This commit is contained in:
parent
5a9b29bb84
commit
60fe921881
8 changed files with 100 additions and 94 deletions
68
sandbox_fs.py
Normal file → Executable file
68
sandbox_fs.py
Normal file → Executable file
|
|
@ -1,10 +1,12 @@
|
|||
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (C) 2016 Andrew Hamilton. All rights reserved.
|
||||
# Licensed under the Artistic License 2.0.
|
||||
|
||||
import contextlib
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
|
||||
|
|
@ -13,33 +15,25 @@ class OverlayfsMount():
|
|||
def __init__(self, lower_dir, mount_point):
|
||||
self.lower_dir = lower_dir
|
||||
self.mount_point = mount_point
|
||||
self.upper_dir = tempfile.mkdtemp()
|
||||
self.work_dir = tempfile.mkdtemp()
|
||||
self.upper_dir = tempfile.TemporaryDirectory()
|
||||
self.work_dir = tempfile.TemporaryDirectory()
|
||||
option_string = ("lowerdir=%s,upperdir=%s,workdir=%s" %
|
||||
(self.lower_dir, self.upper_dir, self.work_dir))
|
||||
subprocess.check_call(["sudo", "mount", "-t", "overlay", "-o",
|
||||
(self.lower_dir, self.upper_dir.name,
|
||||
self.work_dir.name))
|
||||
subprocess.check_call(["mount", "-t", "overlay", "-o",
|
||||
option_string, "overlay", self.mount_point],
|
||||
stderr=subprocess.PIPE)
|
||||
for command in ["chmod", "chown"]:
|
||||
subprocess.check_call(["sudo", command, "--reference", lower_dir,
|
||||
mount_point])
|
||||
|
||||
def __repr__(self):
|
||||
return "<OverlayfsMount:%r over %r>" % (self.mount_point,
|
||||
self.lower_dir)
|
||||
|
||||
def umount(self):
|
||||
subprocess.check_call(["sudo", "umount", "--lazy", self.mount_point])
|
||||
subprocess.check_call(["sudo", "rm", "-rf", self.upper_dir,
|
||||
self.work_dir])
|
||||
subprocess.check_call(["umount", "--lazy", self.mount_point])
|
||||
|
||||
|
||||
def _in_chroot(mount_point, command):
|
||||
return ["sudo", "chroot", "--userspec=%s" % os.environ["USER"],
|
||||
mount_point] + command
|
||||
|
||||
|
||||
_IN_DIRECTORY_SCRIPT = os.path.join(os.path.dirname(__file__), "in-directory")
|
||||
_SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
|
||||
_IN_DIRECTORY_SCRIPT = os.path.join(_SCRIPT_DIR, "in-directory")
|
||||
|
||||
|
||||
def _in_directory(directory_path, command):
|
||||
|
|
@ -61,24 +55,54 @@ def _find_mounts():
|
|||
|
||||
class SandboxFs:
|
||||
|
||||
def __init__(self, mount_point):
|
||||
def __init__(self, mount_point, holes=None):
|
||||
self.mount_point = mount_point
|
||||
self.holes = [] if holes is None else holes
|
||||
for hole in self.holes:
|
||||
if not hole.startswith("/"):
|
||||
raise ValueError("Holes must be absolute paths: %r" % hole)
|
||||
self.overlay_mounts = []
|
||||
|
||||
def __repr__(self):
|
||||
return "<SandboxFs:%r mounts:%r>" % (self.mount_point,
|
||||
len(self.overlay_mounts))
|
||||
return ("<SandboxFs:%r mounts:%r>" %
|
||||
(self.mount_point, len(self.overlay_mounts)))
|
||||
|
||||
def mount(self):
|
||||
self.overlay_mounts = [OverlayfsMount(mount_point,
|
||||
self.mount_point + mount_point)
|
||||
for mount_point in sorted(_find_mounts())]
|
||||
for hole in self.holes:
|
||||
subprocess.check_call(["mount", "--bind", hole,
|
||||
self.mount_point + hole])
|
||||
|
||||
def umount(self):
|
||||
for hole in reversed(self.holes):
|
||||
subprocess.check_call(["umount", self.mount_point + hole])
|
||||
for mount in reversed(self.overlay_mounts):
|
||||
mount.umount()
|
||||
self.overlay_mounts = []
|
||||
|
||||
def command(self, command, env=None):
|
||||
return _in_chroot(self.mount_point,
|
||||
_in_directory(os.getcwd(), command))
|
||||
return (["chroot", self.mount_point] +
|
||||
_in_directory(os.getcwd(), command))
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def sandbox_(holes=None):
|
||||
temp_dir = tempfile.TemporaryDirectory()
|
||||
sandbox = SandboxFs(temp_dir.name, holes)
|
||||
sandbox.mount()
|
||||
try:
|
||||
yield sandbox
|
||||
finally:
|
||||
sandbox.umount()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
divider_index = sys.argv.index("--")
|
||||
holes, command = sys.argv[1:divider_index], sys.argv[divider_index+1:]
|
||||
except ValueError:
|
||||
holes, command = None, sys.argv[1:]
|
||||
with sandbox_(holes) as sandbox:
|
||||
subprocess.check_call(sandbox.command(command))
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue