예제 #1
0
def run_sandbox(command, cwd=None, env=None,
                filesystem_root='/', filesystem_writable_paths='all',
                mounts='undefined', extra_mounts=None,
                network='undefined',
                stdout=sandboxlib.CAPTURE, stderr=sandboxlib.CAPTURE):
    if type(command) == str:
        command = [command]

    linux_user_chroot_command = [linux_user_chroot_program()]

    extra_mounts = sandboxlib.validate_extra_mounts(extra_mounts)

    linux_user_chroot_command += process_network_config(network)

    if cwd is not None:
        linux_user_chroot_command.extend(['--chdir', cwd])

    linux_user_chroot_command += process_writable_paths(
        filesystem_root, filesystem_writable_paths)

    create_mount_points_if_missing(filesystem_root, extra_mounts)

    mount_context = process_mount_config(
        mounts=mounts, extra_mounts=extra_mounts or [])
    with mount_context as linux_user_chroot_mount_args:
        linux_user_chroot_command.extend(linux_user_chroot_mount_args)

        argv = linux_user_chroot_command + [filesystem_root] + command
        exit, out, err = sandboxlib._run_command(argv, stdout, stderr, env=env)
    return exit, out, err
예제 #2
0
def mount(source, path, mount_type, mount_options):
    # We depend on the host system's 'mount' program here, which is a
    # little sad. It's possible to call the libc's mount() function
    # directly from Python using the 'ctypes' library, and perhaps we
    # should do that instead.  The 'mount' requires that a source is
    # given even for the special filesystems (e.g. proc, tmpfs), so we
    # use the mount type as the source if the latter is not explicitly
    # given.
    def is_none(value):
        return value in (None, 'none', '')

    argv = ['mount']
    if not is_none(mount_type):
        argv.extend(('-t', mount_type))
    if not is_none(mount_options):
        argv.extend(('-o', mount_options))
    #If this is left empty, mount looks in fstab which will fail
    if not is_none(source):
        argv.append(source)
    else:
        argv.append("none")
    argv.append(path)

    exit, out, err = sandboxlib._run_command(
        argv, stdout=sandboxlib.CAPTURE, stderr=sandboxlib.CAPTURE)

    if exit != 0:
        raise RuntimeError(
            "%s failed: %s" % (
                argv, err.decode('utf-8')))
예제 #3
0
def run_sandbox(command, cwd=None, env=None,
                filesystem_root='/', filesystem_writable_paths='all',
                mounts='undefined', extra_mounts=None,
                network='undefined',
                stdout=sandboxlib.CAPTURE, stderr=sandboxlib.CAPTURE):
    if type(command) == str:
        command = [command]

    linux_user_chroot_command = ['linux-user-chroot']

    extra_mounts = sandboxlib.validate_extra_mounts(extra_mounts)

    unshare_command = process_mount_config(
        root=filesystem_root, mounts=mounts, extra_mounts=extra_mounts or [])

    linux_user_chroot_command += process_network_config(network)

    if cwd is not None:
        linux_user_chroot_command.extend(['--chdir', cwd])

    linux_user_chroot_command += process_writable_paths(
        filesystem_root, filesystem_writable_paths)

    linux_user_chroot_command.append(filesystem_root)

    create_mount_points_if_missing(filesystem_root, extra_mounts)

    argv = (unshare_command + linux_user_chroot_command + command)
    exit, out, err = sandboxlib._run_command(argv, stdout, stderr, env=env)
    return exit, out, err
예제 #4
0
def unmount(path):
    argv = ['umount', path]
    exit, out, err = sandboxlib._run_command(argv, stdout=None, stderr=None)

    if exit != 0:
        warnings.warn("%s failed: %s" % (
            argv, err.decode('utf-8')))
예제 #5
0
def run_sandbox(command,
                cwd=None,
                env=None,
                filesystem_root='/',
                filesystem_writable_paths='all',
                mounts='undefined',
                extra_mounts=None,
                network='undefined',
                stdout=sandboxlib.CAPTURE,
                stderr=sandboxlib.CAPTURE):
    if type(command) == str:
        command = [command]

    linux_user_chroot_command = [linux_user_chroot_program()]

    extra_mounts = sandboxlib.validate_extra_mounts(extra_mounts)

    linux_user_chroot_command += process_network_config(network)

    if cwd is not None:
        linux_user_chroot_command.extend(['--chdir', cwd])

    linux_user_chroot_command += process_writable_paths(
        filesystem_root, filesystem_writable_paths)

    create_mount_points_if_missing(filesystem_root, extra_mounts)

    mount_context = process_mount_config(mounts=mounts,
                                         extra_mounts=extra_mounts or [])
    with mount_context as linux_user_chroot_mount_args:
        linux_user_chroot_command.extend(linux_user_chroot_mount_args)

        argv = linux_user_chroot_command + [filesystem_root] + command
        exit, out, err = sandboxlib._run_command(argv, stdout, stderr, env=env)
    return exit, out, err
예제 #6
0
def unmount(path):
    argv = ['umount', path]
    exit, out, err = sandboxlib._run_command(argv,
                                             stdout=sandboxlib.CAPTURE,
                                             stderr=sandboxlib.CAPTURE)

    if exit != 0:
        warnings.warn("%s failed: %s" % (argv, err.decode('utf-8')))
예제 #7
0
def mount(source, path, mount_type, mount_options):
    # We depend on the host system's 'mount' program here, which is a
    # little sad. It's possible to call the libc's mount() function
    # directly from Python using the 'ctypes' library, and perhaps we
    # should do that instead.
    argv = [
        'mount', '-t', mount_type, '-o', mount_options, source, path]
    exit, out, err = sandboxlib._run_command(argv, stdout=None, stderr=None)

    if exit != 0:
        raise RuntimeError(
            "%s failed: %s" % (
                argv, err.decode('utf-8')))
예제 #8
0
def run_command_in_chroot(pipe, stdout, stderr, extra_mounts, chroot_path,
                          command, cwd, env):
    # This function should be run in a multiprocessing.Process() subprocess,
    # because it calls os.chroot(). There's no 'unchroot()' function! After
    # chrooting, it calls sandboxlib._run_command(), which uses the
    # 'subprocess' module to exec 'command'. This means there are actually
    # two subprocesses, which is not ideal, but it seems to be the simplest
    # implementation.
    #
    # An alternative approach would be to use the 'preexec_fn' feature of
    # subprocess.Popen() to call os.chroot(rootfs_path) and os.chdir(cwd).
    # The Python 3 '_posixsubprocess' module hints in several places that
    # deadlocks can occur when using preexec_fn, and it is very difficult to
    # propagate exceptions from that function, so it seems best to avoid it.

    try:
        # You have most likely got to be the 'root' user in order for this to
        # work.

        try:
            os.chroot(chroot_path)
        except OSError as e:
            raise RuntimeError("Unable to chroot: %s" % e)

        # This is important in case 'cwd' is a relative path.
        os.chdir('/')

        if cwd is not None:
            try:
                os.chdir(cwd)
            except OSError as e:
                raise RuntimeError(
                    "Unable to set current working directory: %s" % e)

        exit, out, err = sandboxlib._run_command(command,
                                                 stdout,
                                                 stderr,
                                                 env=env)
        pipe.send([exit, out, err])
        result = 0
    except Exception as e:
        pipe.send(e)
        result = 1
    os._exit(result)
예제 #9
0
def run_command_in_chroot(pipe, stdout, stderr, extra_mounts, chroot_path,
                          command, cwd, env):
    # This function should be run in a multiprocessing.Process() subprocess,
    # because it calls os.chroot(). There's no 'unchroot()' function! After
    # chrooting, it calls sandboxlib._run_command(), which uses the
    # 'subprocess' module to exec 'command'. This means there are actually
    # two subprocesses, which is not ideal, but it seems to be the simplest
    # implementation.
    #
    # An alternative approach would be to use the 'preexec_fn' feature of
    # subprocess.Popen() to call os.chroot(rootfs_path) and os.chdir(cwd).
    # The Python 3 '_posixsubprocess' module hints in several places that
    # deadlocks can occur when using preexec_fn, and it is very difficult to
    # propagate exceptions from that function, so it seems best to avoid it.

    try:
        # You have most likely got to be the 'root' user in order for this to
        # work.

        try:
            os.chroot(chroot_path)
        except OSError as e:
            raise RuntimeError("Unable to chroot: %s" % e)

        # This is important in case 'cwd' is a relative path.
        os.chdir('/')

        if cwd is not None:
            try:
                os.chdir(cwd)
            except OSError as e:
                raise RuntimeError(
                    "Unable to set current working directory: %s" % e)

        exit, out, err = sandboxlib._run_command(
            command, stdout, stderr, env=env)
        pipe.send([exit, out, err])
        result = 0
    except Exception as e:
        tb = traceback.format_exc()
        pipe.send((e, tb))
        result = 1
    os._exit(result)
예제 #10
0
def mount(source, path, mount_type, mount_options):
    # We depend on the host system's 'mount' program here, which is a
    # little sad. It's possible to call the libc's mount() function
    # directly from Python using the 'ctypes' library, and perhaps we
    # should do that instead.
    def is_none(value):
        return value in (None, 'none', '')

    argv = ['mount']
    if not is_none(mount_type):
        argv.extend(('-t', mount_type))
    if not is_none(mount_options):
        argv.extend(('-o', mount_options))
    if not is_none(source):
        argv.append(source)
    argv.append(path)

    exit, out, err = sandboxlib._run_command(argv,
                                             stdout=sandboxlib.CAPTURE,
                                             stderr=sandboxlib.CAPTURE)

    if exit != 0:
        raise RuntimeError("%s failed: %s" % (argv, err.decode('utf-8')))
예제 #11
0
def mount(source, path, mount_type, mount_options):
    # We depend on the host system's 'mount' program here, which is a
    # little sad. It's possible to call the libc's mount() function
    # directly from Python using the 'ctypes' library, and perhaps we
    # should do that instead.
    def is_none(value):
        return value in (None, 'none', '')

    argv = ['mount']
    if not is_none(mount_type):
        argv.extend(('-t', mount_type))
    if not is_none(mount_options):
        argv.extend(('-o', mount_options))
    if not is_none(source):
        argv.append(source)
    argv.append(path)

    exit, out, err = sandboxlib._run_command(
        argv, stdout=sandboxlib.CAPTURE, stderr=sandboxlib.CAPTURE)

    if exit != 0:
        raise RuntimeError(
            "%s failed: %s" % (
                argv, err.decode('utf-8')))
예제 #12
0
def run_sandbox(command, cwd=None, env=None,
                filesystem_root='/', filesystem_writable_paths='all',
                mounts='undefined', extra_mounts=None,
                network='undefined',
                stderr=sandboxlib.CAPTURE, stdout=sandboxlib.CAPTURE):
    """Run 'command' in a sandboxed environment.

    Parameters:
      - command: the command to run. Pass a list of parameters rather than
            using spaces to separate them, e.g. ['echo', '"Hello world"'].
      - cwd: the working directory of 'command', relative to 'rootfs_path'.
            Defaults to '/' if "rootfs_path" is specified, and the current
            directory of the calling process otherwise.
      - env: environment variables to set
      - filesystem_root: the path to the root of the sandbox. Defaults to '/',
            which doesn't isolate the command from the host filesystem at all.
      - filesystem_writable_paths: defaults to 'all', which allows the command
            to write to anywhere under 'filesystem_root' that the user of the
            calling process could write to. Backends may accept a list of paths
            instead of 'all', and will prevent writes to any files not under a
            path in that whitelist. If 'none' or an empty list is passed, the
            whole file-system will be read-only. The paths should be relative
            to filesystem_root. This will processed /after/ extra_mounts are
            mounted.
      - mounts: configures mount sharing. Defaults to 'undefined', where no
            no attempt is made to isolate mounts. Backends may support
            'isolated' as well.
      - extra_mounts: a list of locations to mount inside 'rootfs_path',
            specified as a list of tuples of (source_path, target_path, type,
            options). The 'type' and 'options' should match what would be
            specified in /etc/fstab, but a backends may support only a limited
            subset of values. The 'target_path' is relative to filesystem_root
            and will be created before mounting if it doesn't exist.
      - network: configures network sharing. Defaults to 'undefined', where
            no attempt is made to either prevent or provide networking
            inside the sandbox. Backends may support 'isolated' and/or other
            values as well.
      - stdout: whether to capture stdout, or redirect stdout to a file handle.
            If set to sandboxlib.CAPTURE, the function will return the stdout
            data, if not, it will return None for that. If stdout=None, the
            data will be discarded -- it will NOT inherit the parent process's
            stdout, unlike with subprocess.Popen(). Set 'stdout=sys.stdout' if
            you want that.
      - stderr: same as stdout

    Returns:
      a tuple of (exit code, stdout output, stderr output).

    """

    log.debug("cmd: {}, cwd: {}, env: {}, filesystem_root: {}, "
              "filesystem_writable_paths: {}, mounts: {}, extra_mounts: {}, "
              "network: {}, stderr: {}, stdout: {}".format(
                command, cwd, env, filesystem_root, filesystem_writable_paths,
                mounts, extra_mounts, network, stderr, stdout))
    
    if type(command) == str:
        command = [command]

    # Bwrap full path
    bwrap_command = [bubblewrap_program()]
    log.debug("/path/to/bwrap : {}".format(bwrap_command))

    # Add in the root filesystem stuff first
    # rootfs is mounted as RW initially so that further mounts can be placed on top
    # If a RO root is required, after all other mounts are complete, root is
    # remounted as RO
    bwrap_command += ["--bind", filesystem_root, "/"]

    bwrap_command += process_network_config(network)
 
    if cwd is not None:
        log.debug("Setting cwd to '{}'".format(cwd))
        bwrap_command.extend(['--chdir', cwd])
 
    # do pre checks on mounts
    extra_mounts = sandboxlib.validate_extra_mounts(extra_mounts)
    create_mount_points_if_missing(filesystem_root, extra_mounts)

    # Handles the ro and rw mounts
    bwrap_command += process_mounts(filesystem_root, extra_mounts,
                                    filesystem_writable_paths)

    # Set UID and GUI
    bwrap_command.extend(['--unshare-user', '--uid', '0', '--gid', '0'])

    argv = bwrap_command + command
    log.info("bubblewrap.run_command({}, stdout:{}, stderr:{}, env:{})"
             .format(" ".join(argv), stdout, stderr, env))

    exit, out, err = sandboxlib._run_command(argv, stdout, stderr, env=env)

    return exit, out, err