Beispiel #1
0
def mount_bind(newroot, target, source=None, recursive=True, read_only=True):
    """Bind mounts `source` to `newroot/target` so that `source` is accessed
    when reaching `newroot/target`.

    If a directory, the source will be mounted using --rbind.
    """
    # Ensure root directory exists
    if not os.path.exists(newroot):
        raise exc.ContainerSetupError('Path %r does not exist' % newroot)

    if source is None:
        source = target

    target = fs.norm_safe(target)
    source = fs.norm_safe(source)

    # Make sure target directory exists.
    if not os.path.exists(source):
        raise exc.ContainerSetupError('Source path %r does not exist' % source)

    mnt_flags = [mount.MS_BIND]

    # Use --rbind for directories and --bind for files.
    if recursive and os.path.isdir(source):
        mnt_flags.append(mount.MS_REC)

    # Strip leading /, ensure that mount is relative path.
    while target.startswith('/'):
        target = target[1:]

    # Create mount directory, make sure it does not exists.
    target_fp = os.path.join(newroot, target)
    if os.path.isdir(source):
        fs.mkdir_safe(target_fp)
    else:
        fs.mkfile_safe(target_fp)

    res = mount.mount(source=source,
                      target=target_fp,
                      fs_type=None,
                      mnt_flags=mnt_flags)

    if res == 0 and read_only:
        res = mount.mount(source=None,
                          target=target_fp,
                          fs_type=None,
                          mnt_flags=(mount.MS_BIND, mount.MS_RDONLY,
                                     mount.MS_REMOUNT))

    return res
Beispiel #2
0
    def unpack(self, container_dir, root_dir, app, app_cgroups, data):

        root_dir = fs.norm_safe(root_dir)

        emptydirs, stickydirs, mounts = configure()
        for plugin in image_fs.plugins(app):
            _LOGGER.info('Processing plugin: %r', plugin)
            extra = plugin(self.tm_env).configure(container_dir, app)
            if extra is not None:
                emptydirs.update(extra[0])
                stickydirs.update(extra[1])
                mounts.update(extra[2])

        make_fsroot(root_dir, emptydirs, stickydirs, mounts)
        make_osroot(root_dir, app, data)

        # FIXME: Lots of things are still reading this file.
        #        Copy updated state manifest as app.json in the
        #        container_dir so it is visible in chrooted env.
        shutil.copy(os.path.join(container_dir, runtime.STATE_JSON),
                    os.path.join(root_dir, appcfg.APP_JSON))

        cgrp = os.path.join(app_cgroups['memory'], 'services')

        create_environ_dir(container_dir, root_dir, app)

        create_supervision_tree(
            self.tm_env, container_dir, root_dir, app,
            cgroups_path=cgroups.makepath(
                'freezer', cgrp
            ),
        )
        create_overlay(self.tm_env, container_dir, root_dir, app)
Beispiel #3
0
    def configure(self, container_dir, app):
        root_dir = os.path.join(container_dir, 'root')
        newroot_norm = fs.norm_safe(root_dir)
        mounts = [
        ]

        emptydirs = [
            '/u',
            '/var/account',
            '/var/empty',
            '/var/lock',
            '/var/log',
            '/var/run',
        ]

        stickydirs = [
            '/opt',
        ]

        for mount in mounts:
            if os.path.exists(mount):
                fs.mount_bind(newroot_norm, mount)

        for directory in emptydirs:
            fs.mkdir_safe(newroot_norm + directory)

        for directory in stickydirs:
            os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)
Beispiel #4
0
def configure(_approot, newroot, _app):
    """Configure layout in chroot."""
    newroot_norm = fs.norm_safe(newroot)
    mounts = [
    ]

    emptydirs = [
        '/u',
        '/var/account',
        '/var/empty',
        '/var/lock',
        '/var/log',
        '/var/run',
    ]

    stickydirs = [
        '/opt',
    ]

    for mount in mounts:
        if os.path.exists(mount):
            fs.mount_bind(newroot_norm, mount)

    for directory in emptydirs:
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0777 | stat.S_ISVTX)
Beispiel #5
0
    def configure(self, container_dir, app):
        root_dir = os.path.join(container_dir, 'root')
        newroot_norm = fs.norm_safe(root_dir)

        emptydirs = [
            '/opt/s6',
            '/opt/treadmill',
            '/opt/treadmill-bind',
        ]

        stickydirs = []

        mounts = [
            '/opt/s6',
            '/opt/treadmill',
            '/opt/treadmill-bind',
        ]

        for directory in emptydirs:
            fs.mkdir_safe(newroot_norm + directory)

        for directory in stickydirs:
            os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

        for mount in mounts:
            if os.path.exists(mount):
                fs_linux.mount_bind(newroot_norm,
                                    mount,
                                    recursive=True,
                                    read_only=True)
Beispiel #6
0
def make_root(root_dir):
    """create container root by calling pivot_root
    """
    newroot_norm = fs.norm_safe(root_dir)
    old_pivot = os.path.join(newroot_norm, PIVOT_PATH)
    fs.mkdir_safe(old_pivot)

    proot.pivot_root(newroot_norm, old_pivot)
    # return list of mount to be umounted from down to top
    to_umount = move_mounts(os.path.sep + PIVOT_PATH)

    # umount entries from down to top
    for mount_entry in to_umount:
        _umount_with_detach(mount_entry.target)
Beispiel #7
0
def _create_root_dir(container_dir, localdisk):
    """Prepares chrooted environment."""
    # Create root directory structure (chroot base).
    # container_dir/<subdir>
    root_dir = os.path.join(container_dir, 'root')

    already_initialized = fs_linux.blk_fs_test(localdisk['block_dev'])
    if not already_initialized:
        # Format the block device
        fs_linux.blk_fs_create(localdisk['block_dev'])

    _LOGGER.info('Creating container root directory: %s', root_dir)
    # Creates directory that will serve as new root.
    fs.mkdir_safe(fs.norm_safe(root_dir))
    # Unshare the mount namespace
    unshare.unshare(unshare.CLONE_NEWNS)
    # Mount the container root volume
    fs_linux.mount_filesystem(localdisk['block_dev'], root_dir)

    return root_dir
Beispiel #8
0
def _create_root_dir(tm_env, container_dir, root_dir, app):
    """Prepares chrooted environment."""
    # Generate a unique name for the app
    unique_name = appcfg.app_unique_name(app)

    # First wait for the block device to be ready
    localdisk_client = tm_env.svc_localdisk.make_client(
        os.path.join(container_dir, 'localdisk'))
    localdisk = localdisk_client.wait(unique_name)

    already_initialized = fs.test_filesystem(localdisk['block_dev'])
    if not already_initialized:
        # Format the block device
        fs.create_filesystem(localdisk['block_dev'])

    _LOGGER.info('Creating container root directory: %s', root_dir)
    # Creates directory that will serve as new root.
    fs.mkdir_safe(fs.norm_safe(root_dir))
    # Unshare the mount namespace
    unshare.unshare(unshare.CLONE_NEWNS)
    # Mount the container root volume
    fs.mount_filesystem(localdisk['block_dev'], root_dir)
Beispiel #9
0
def make_fsroot(root, proid):
    """Initializes directory structure for the container in a new root.

     - Bind directories in parent / (with exceptions - see below.)
     - Skip /tmp, create /tmp in the new root with correct permissions.
     - Selectively create / bind /var.
       - /var/tmp (new)
       - /var/logs (new)
       - /var/spool - create empty with dirs.
     - Bind everything in /var, skipping /spool/tickets
     """
    newroot_norm = fs.norm_safe(root)
    mounts = [
        '/bin',
        '/common',
        '/dev',
        '/etc',
        '/home',
        '/lib',
        '/lib64',
        '/mnt',
        '/proc',
        '/sbin',
        '/srv',
        '/sys',
        '/usr',
        '/var/tmp/treadmill/env',
        '/var/tmp/treadmill/spool',
    ] + glob.glob('/opt/*')

    emptydirs = [
        '/tmp',
        '/opt',
        '/var/empty',
        '/var/run',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        '/var/tmp',
        '/var/tmp/cores',
    ]

    stickydirs = [
        '/tmp',
        '/opt',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        '/var/tmp',
        '/var/tmp/cores/',
    ]

    for directory in emptydirs:
        _LOGGER.debug('Creating empty dir: %s', directory)
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

    for mount in mounts:
        if os.path.exists(mount):
            fs.mount_bind(newroot_norm, mount)

    # Mount .../tickets .../keytabs on tempfs, so that they will be cleaned
    # up when the container exits.
    #
    # TODO: Do we need to have a single mount for all tmpfs dirs?
    for tmpfsdir in [
            '/var/spool/tickets', '/var/spool/keytabs', '/var/spool/tokens'
    ]:
        fs.mount_tmpfs(newroot_norm, tmpfsdir, '4M')
Beispiel #10
0
def make_fsroot(root_dir, app, data):
    """Initializes directory structure for the container in a new root.

    The container uses pretty much a blank a FHS 3 layout.

     - Bind directories in parent / (with exceptions - see below.)
     - Skip /tmp, create /tmp in the new root with correct permissions.
     - Selectively create / bind /var.
       - /var/tmp (new)
       - /var/log (new)
       - /var/spool - create empty with dirs.
     - Bind everything in /var, skipping /spool/tickets

    :param ``str`` root_dit:
        Container root directory.
    :param app:
        Container app manifest.
    :param ``dict`` data:
        Local configuration data.
    """
    newroot_norm = fs.norm_safe(root_dir)

    emptydirs = [
        '/bin',
        '/dev',
        '/etc',
        '/home',
        '/lib',
        '/lib64',
        '/opt',
        '/proc',
        '/root',
        '/run',
        '/sbin',
        '/sys',
        '/tmp',
        '/usr',
        '/var/cache',
        '/var/empty',
        '/var/empty/sshd',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/spool',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        # for SSS
        '/var/lib/sss',
    ]

    stickydirs = [
        '/opt',
        '/run',
        '/tmp',
        '/var/cache',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
    ]

    # these folders are shared with underlying host and other containers,
    mounts = [
        '/bin',
        '/etc',  # TODO: Add /etc/opt
        '/lib',
        '/lib64',
        '/root',
        '/sbin',
        '/usr',
        # for SSS
        '/var/lib/sss',
        # TODO: Remove below once PAM UDS is implemented
        os.path.expandvars('${TREADMILL_APPROOT}/env'),
        os.path.expandvars('${TREADMILL_APPROOT}/spool'),
    ]

    for directory in emptydirs:
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

    # /var/empty must be owned by root and not group or world-writable.
    os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711)

    # Mount a new sysfs for the container, bring in the /sys/fs subtree from
    # the host.
    fs_linux.mount_sysfs(newroot_norm)
    fs_linux.mount_bind(newroot_norm,
                        os.path.join(os.sep, 'sys', 'fs'),
                        recursive=True,
                        read_only=False)

    make_dev(newroot_norm)
    # Passthrough node devices per the node config data
    extra_devices = data.get('runtime', {}).get('passthrough_devices', [])
    make_extra_dev(newroot_norm, extra_devices, app.proid)

    # Per FHS3 /var/run should be a symlink to /run which should be tmpfs
    fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run')
    # We create an unbounded tmpfs mount so that runtime data can be written to
    # it, counting against the memory limit of the container.
    fs_linux.mount_tmpfs(newroot_norm, '/run')

    # Make shared directories/files readonly to container
    for mount in mounts:
        if os.path.exists(mount):
            fs_linux.mount_bind(newroot_norm,
                                mount,
                                recursive=True,
                                read_only=True)

    # /etc/docker is a file neceesary for docker daemon
    _docker.mount_docker_daemon_path(newroot_norm, app)
Beispiel #11
0
def make_fsroot(root_dir, app):
    """Initializes directory structure for the container in a new root.

    The container uses pretty much a blank a FHS 3 layout.

     - Bind directories in parent / (with exceptions - see below.)
     - Skip /tmp, create /tmp in the new root with correct permissions.
     - Selectively create / bind /var.
       - /var/tmp (new)
       - /var/log (new)
       - /var/spool - create empty with dirs.
     - Bind everything in /var, skipping /spool/tickets

     tm_env is used to deliver abort events
     """
    newroot_norm = fs.norm_safe(root_dir)

    emptydirs = [
        '/bin',
        '/dev',
        '/etc',
        '/home',
        '/lib',
        '/lib64',
        '/opt',
        '/proc',
        '/root',
        '/run',
        '/sbin',
        '/sys',
        '/tmp',
        '/usr',
        '/var/cache',
        '/var/empty',
        '/var/empty/sshd',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/spool',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        # for SSS
        '/var/lib/sss',
    ]

    stickydirs = [
        '/opt',
        '/run',
        '/tmp',
        '/var/cache',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
    ]

    # these folders are shared with underlying host and other containers,
    mounts = [
        '/bin',
        '/etc',  # TODO: Add /etc/opt
        '/lib',
        '/lib64',
        '/root',
        '/sbin',
        '/usr',
        # for SSS
        '/var/lib/sss',
        # TODO: Remove below once PAM UDS is implemented
        os.path.expandvars('${TREADMILL_APPROOT}/env'),
        os.path.expandvars('${TREADMILL_APPROOT}/spool'),
    ]

    for directory in emptydirs:
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

    # /var/empty must be owned by root and not group or world-writable.
    os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711)

    fs_linux.mount_bind(newroot_norm,
                        os.path.join(os.sep, 'sys'),
                        source='/sys',
                        recursive=True,
                        read_only=False)

    make_dev(newroot_norm)

    # Per FHS3 /var/run should be a symlink to /run which should be tmpfs
    fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run')
    # We create an unbounded tmpfs mount so that runtime data can be written to
    # it, counting against the memory limit of the container.
    fs_linux.mount_tmpfs(newroot_norm, '/run')

    # Make shared directories/files readonly to container
    for mount in mounts:
        if os.path.exists(mount):
            fs_linux.mount_bind(newroot_norm,
                                mount,
                                recursive=True,
                                read_only=True)

    if hasattr(app, 'docker') and app.docker:
        # If unable to mount docker directory, we throw Aborted events
        try:
            _mount_docker_tmpfs(newroot_norm)
        except FileNotFoundError as err:
            _LOGGER.error('Failed to mount docker tmpfs: %s', err)
            # this exception is caught by sproc run to generate abort event
            raise exc.ContainerSetupError(
                msg=str(err),
                reason=app_abort.AbortedReason.UNSUPPORTED,
            )
Beispiel #12
0
def make_fsroot(root_dir):
    """Initializes directory structure for the container in a new root.

    The container uses pretty much a blank a FHS 3 layout.

     - Bind directories in parent / (with exceptions - see below.)
     - Skip /tmp, create /tmp in the new root with correct permissions.
     - Selectively create / bind /var.
       - /var/tmp (new)
       - /var/log (new)
       - /var/spool - create empty with dirs.
     - Bind everything in /var, skipping /spool/tickets
     """
    newroot_norm = fs.norm_safe(root_dir)

    emptydirs = [
        '/bin',
        '/dev',
        '/etc',
        '/home',
        '/lib',
        '/lib64',
        '/opt',
        '/proc',
        '/root',
        '/run',
        '/sbin',
        '/sys',
        '/tmp',
        '/usr',
        '/var/cache',
        '/var/empty',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/spool',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
        # for SSS
        '/var/lib/sss',
    ]

    stickydirs = [
        '/opt',
        '/run',
        '/tmp',
        '/var/cache',
        '/var/lib',
        '/var/lock',
        '/var/log',
        '/var/opt',
        '/var/tmp',
        '/var/spool/keytabs',
        '/var/spool/tickets',
        '/var/spool/tokens',
    ]

    # these folders are shared with underlying host and other containers,
    mounts = [
        '/bin',
        '/etc',  # TODO: Add /etc/opt
        '/lib',
        '/lib64',
        '/root',
        '/sbin',
        '/usr',
        # for SSS
        '/var/lib/sss',
        # TODO: Remove below once PAM UDS is implemented
        '/var/tmp/treadmill/env',
        '/var/tmp/treadmill/spool',
    ]

    # Add everything under /opt
    mounts += glob.glob('/opt/*')

    for directory in emptydirs:
        fs.mkdir_safe(newroot_norm + directory)

    for directory in stickydirs:
        os.chmod(newroot_norm + directory, 0o777 | stat.S_ISVTX)

    # /var/empty must be owned by root and not group or world-writable.
    os.chmod(os.path.join(newroot_norm, 'var/empty'), 0o711)

    fs_linux.mount_bind(newroot_norm,
                        os.path.join(os.sep, 'sys'),
                        source='/sys',
                        recursive=True,
                        read_only=False)
    # TODO: For security, /dev/ should be minimal and separated to each
    #       container.
    fs_linux.mount_bind(newroot_norm,
                        os.path.join(os.sep, 'dev'),
                        source='/dev',
                        recursive=True,
                        read_only=False)
    # Per FHS3 /var/run should be a symlink to /run which should be tmpfs
    fs.symlink_safe(os.path.join(newroot_norm, 'var', 'run'), '/run')
    # We create an unbounded tmpfs mount so that runtime data can be written to
    # it, counting against the memory limit of the container.
    fs_linux.mount_tmpfs(newroot_norm, '/run')

    # Make shared directories/files readonly to container
    for mount in mounts:
        if os.path.exists(mount):
            fs_linux.mount_bind(newroot_norm,
                                mount,
                                recursive=True,
                                read_only=True)