예제 #1
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)
예제 #2
0
파일: native.py 프로젝트: ceache/treadmill
def make_fsroot(root_dir, emptydirs, stickydirs, mounts):
    """Initializes directory structure for the container in a new root.
    """
    _LOGGER.info('Creating fs root in: %s', root_dir)
    for directory in sorted(emptydirs):
        fs.mkdir_safe(root_dir + directory)

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

    # Make shared directories/files readonly to container
    reserved = {'/run',
                '/sys/fs',
                '/var/spool/tickets',
                '/var/spool/keytabs',
                '/var/spool/tokens'}
    for mount, args in mounts.items():
        # These are reserved, mounted on memory in make_osroot.
        if mount in reserved:
            continue

        if not args:
            fs_linux.mount_bind(
                root_dir, mount,
                recursive=True, read_only=True
            )
        else:
            fs_linux.mount_bind(root_dir, mount, **args)
예제 #3
0
def make_osroot(root_dir, app, data):
    """Creates mandatory system directories in the container chroot."""
    _LOGGER.info('Creating os root in: %s', root_dir)
    # Mount .../tickets .../keytabs on tempfs, so that they will be cleaned
    # up when the container exits.
    #
    for tmpfsdir in [
            '/var/spool/tickets', '/var/spool/keytabs', '/var/spool/tokens'
    ]:
        fs_linux.mount_tmpfs(root_dir, tmpfsdir)

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

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

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

    # Per FHS3 /var/run should be a symlink to /run which should be tmpfs
    fs.symlink_safe(os.path.join(root_dir, '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(root_dir, '/run')

    # /etc/docker is a file neceesary for docker daemon
    _docker.prepare_docker_daemon_path(root_dir, app, data)
예제 #4
0
def make_dev(newroot_norm):
    """Make /dev.
    """
    fs_linux.mount_tmpfs(newroot_norm,
                         '/dev',
                         nodev=False,
                         noexec=False,
                         nosuid=True,
                         relatime=False,
                         mode='0755')

    devices = [
        ('/dev/null', 0o666, 1, 3),
        ('/dev/zero', 0o666, 1, 5),
        ('/dev/full', 0o666, 1, 7),
        ('/dev/tty', 0o666, 5, 0),
        ('/dev/random', 0o444, 1, 8),
        ('/dev/urandom', 0o444, 1, 9),
    ]
    prev_umask = os.umask(0000)
    for device, permissions, major, minor in devices:
        os.mknod(newroot_norm + device, permissions | stat.S_IFCHR,
                 os.makedev(major, minor))
    os.umask(prev_umask)
    st = os.stat('/dev/tty')
    os.chown(newroot_norm + '/dev/tty', st.st_uid, st.st_gid)

    symlinks = [
        ('/dev/fd', '/proc/self/fd'),
        ('/dev/stdin', '/proc/self/fd/0'),
        ('/dev/stdout', '/proc/self/fd/1'),
        ('/dev/stderr', '/proc/self/fd/2'),
        ('/dev/core', '/proc/kcore'),
    ]
    for link, target in symlinks:
        fs.symlink_safe(newroot_norm + link, target)

    for directory in ['/dev/shm', '/dev/pts', '/dev/mqueue']:
        fs.mkdir_safe(newroot_norm + directory)
    fs_linux.mount_tmpfs(newroot_norm,
                         '/dev/shm',
                         nodev=True,
                         noexec=False,
                         nosuid=True,
                         relatime=False)
    fs_linux.mount_devpts(newroot_norm,
                          '/dev/pts',
                          gid=st.st_gid,
                          mode='0620',
                          ptmxmode='0666')
    fs.symlink_safe(newroot_norm + '/dev/ptmx', 'pts/ptmx')
    fs_linux.mount_mqueue(newroot_norm, '/dev/mqueue')

    # Passthrough container log to host system logger.
    fs_linux.mount_bind(newroot_norm, '/dev/log', read_only=False)
예제 #5
0
def remount_cgroup(container_root, cgroup, root_cgroup):
    """remount cgroups path to put service cgroup as container root cgroup
    """
    _LOGGER.info('Remounting cgroup paths')
    remount = _get_remount_cgroup(container_root, cgroup, root_cgroup)
    for (target_path, app_subsystem_path) in remount:
        # unmount & mount from /sys/fs/cgroups/<subsystem>/<group> to root dir
        fs_linux.umount_filesystem(app_subsystem_path)
        # dockerd needs to write cgroup path
        fs_linux.mount_bind('/', app_subsystem_path, target_path,
                            read_only=False)
예제 #6
0
def _bind_overlay_docker(container_dir, root_dir):
    """Mount etc/hosts for docker container
    """
    # FIXME: This path is mounted as RW because ro volume in treadmill
    #        container can not be mounted in docker 'Error response from
    #        daemon: chown /etc/hosts: read-only file system.'
    overlay_dir = os.path.join(container_dir, 'overlay')

    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, _CONTAINER_DOCKER_ETC_DIR,
                                     'hosts'),
                        source=os.path.join(overlay_dir, 'etc/hosts'),
                        recursive=False,
                        read_only=False)
예제 #7
0
파일: native.py 프로젝트: ceache/treadmill
def create_environ_dir(container_dir, root_dir, app):
    """Creates environ dir for s6-envdir."""
    env_dir = os.path.join(container_dir, _CONTAINER_ENV_DIR)
    env = {
        'TREADMILL_APP': app.app,
        'TREADMILL_CELL': app.cell,
        'TREADMILL_CPU': app.cpu,
        'TREADMILL_DISK': app.disk,
        'TREADMILL_HOST_IP': app.network.external_ip,
        'TREADMILL_IDENTITY': app.identity,
        'TREADMILL_IDENTITY_COUNT': app.identity_count,
        'TREADMILL_IDENTITY_GROUP': app.identity_group,
        'TREADMILL_INSTANCEID': app.task,
        'TREADMILL_MEMORY': app.memory,
        'TREADMILL_PROID': app.proid,
        'TREADMILL_ENV': app.environment,
    }

    for endpoint in app.endpoints:
        envname = 'TREADMILL_ENDPOINT_{0}'.format(endpoint.name.upper())
        env[envname] = endpoint.real_port

    env['TREADMILL_EPHEMERAL_TCP_PORTS'] = ' '.join(
        [str(port) for port in app.ephemeral_ports.tcp]
    )
    env['TREADMILL_EPHEMERAL_UDP_PORTS'] = ' '.join(
        [str(port) for port in app.ephemeral_ports.udp]
    )

    env['TREADMILL_CONTAINER_IP'] = app.network.vip
    env['TREADMILL_GATEWAY_IP'] = app.network.gateway
    if app.shared_ip:
        env['TREADMILL_SERVICE_IP'] = app.network.external_ip

    supervisor.create_environ_dir(env_dir, env)

    # Bind the environ directory in the container volume
    fs.mkdir_safe(os.path.join(root_dir, _CONTAINER_ENV_DIR))
    fs_linux.mount_bind(
        root_dir, os.path.join(os.sep, _CONTAINER_ENV_DIR),
        source=os.path.join(container_dir, _CONTAINER_ENV_DIR),
        recursive=False, read_only=True
    )

    # envs variables that will be applied in docker container
    _docker.create_docker_environ_dir(container_dir, root_dir, app)
예제 #8
0
def share_cgroup_info(root_dir, cgrp):
    """Shares subset of cgroup tree with the container."""
    # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory
    # of the container.

    # FIXME: This should be removed and proper cgroups should be
    #        exposed (readonly). This is so that tools that
    #        (correctly) read /proc/self/cgroups can access cgroup
    #        data.
    shared_subsystems = ['memory']
    for subsystem in shared_subsystems:
        fs.mkdir_safe(os.path.join(root_dir, 'cgroup', subsystem))
        fs_linux.mount_bind(root_dir,
                            os.path.join(os.sep, 'cgroup', subsystem),
                            source=cgroups.makepath(subsystem, cgrp),
                            recursive=True,
                            read_only=False)
예제 #9
0
def create_docker_environ_dir(container_dir, root_dir, app):
    """Creates environ dir for docker"""
    env_dir = os.path.join(container_dir, _CONTAINER_DOCKER_ENV_DIR)
    env = {}

    if app.ephemeral_ports.tcp or app.ephemeral_ports.udp:
        env['LD_PRELOAD'] = os.path.join(_manifest.TREADMILL_BIND_PATH, '$LIB',
                                         'treadmill_bind_preload.so')

    supervisor.create_environ_dir(env_dir, env)

    # Bind the environ directory in the container volume
    fs.mkdir_safe(os.path.join(root_dir, _CONTAINER_DOCKER_ENV_DIR))
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, _CONTAINER_DOCKER_ENV_DIR),
                        source=os.path.join(container_dir,
                                            _CONTAINER_DOCKER_ENV_DIR),
                        recursive=False,
                        read_only=True)
예제 #10
0
def _bind_overlay(container_dir, root_dir):
    """Create the overlay in the container."""
    # Overlay overrides container configs
    #   - /etc/resolv.conf, so that container always uses dnscache.
    #   - pam.d sshd stack with special sshd pam that unshares network.
    #   - /etc/ld.so.preload to enforce necessary system hooks
    #
    overlay_dir = os.path.join(container_dir, 'overlay')
    for overlay_file in [
            'etc/hosts', 'etc/krb5.keytab', 'etc/ld.so.preload',
            'etc/pam.d/sshd', 'etc/resolv.conf'
    ]:
        fs_linux.mount_bind(root_dir,
                            os.path.join(os.sep, overlay_file),
                            source=os.path.join(overlay_dir, overlay_file),
                            recursive=False,
                            read_only=True)

    # Mount host-aliases as read-write
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, 'run', 'host-aliases'),
                        source=os.path.join(overlay_dir, 'run',
                                            'host-aliases'),
                        recursive=False,
                        read_only=False)

    # Also override resolv.conf in the current mount namespace so that
    # system services have access to our resolver.
    fs_linux.mount_bind('/',
                        '/etc/resolv.conf',
                        source=os.path.join(overlay_dir, 'etc/resolv.conf'),
                        recursive=False,
                        read_only=True)
예제 #11
0
def _bind_overlay(container_dir, root_dir):
    """Create the overlay in the container.

    :param ``str`` container_dir:
        Base directory of container data/config.
    :param ``str`` root_dir:
        New root directory of the container.
    """
    # Overlay overrides container configs
    #   - /etc/resolv.conf, so that container always uses dnscache.
    #   - pam.d sshd stack with special sshd pam that unshares network.
    #   - /etc/ld.so.preload to enforce necessary system hooks
    #
    overlay_dir = os.path.join(container_dir, 'overlay')
    etc_overlay_dir = os.path.join(overlay_dir, 'etc')

    for (basedir, _dirs, files) in os.walk(etc_overlay_dir):
        # We bind mount read-only all etc overlay files.
        for file_ in files:
            overlay_file = os.path.join(basedir, file_)
            target_file = os.path.relpath(overlay_file, overlay_dir)
            fs_linux.mount_bind(root_dir,
                                os.path.join(os.sep, target_file),
                                source=overlay_file,
                                recursive=False,
                                read_only=True)

    # Mount host-aliases as read-write
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, 'run', 'host-aliases'),
                        source=os.path.join(overlay_dir, 'run',
                                            'host-aliases'),
                        recursive=False,
                        read_only=False)

    # Also override resolv.conf in the current mount namespace so that
    # system services have access to our resolver.
    fs_linux.mount_bind('/',
                        '/etc/resolv.conf',
                        source=os.path.join(overlay_dir, 'etc/resolv.conf'),
                        recursive=False,
                        read_only=True)
예제 #12
0
def create_supervision_tree(tm_env, container_dir, root_dir, app,
                            cgroups_path):
    """Creates s6 supervision tree."""
    uniq_name = appcfg.app_unique_name(app)
    ctl_uds = os.path.join(os.sep, 'run', 'tm_ctl')
    tombstone_ctl_uds = os.path.join(ctl_uds, 'tombstone')

    sys_dir = os.path.join(container_dir, 'sys')

    try:
        old_system_services = [
            svc_name for svc_name in os.listdir(sys_dir)
            if (not svc_name.startswith('.')
                and os.path.isdir(os.path.join(sys_dir, svc_name)))
        ]
    except FileNotFoundError:
        old_system_services = []

    new_system_services = [svc_def.name for svc_def in app.system_services]

    for svc_name in set(old_system_services) - set(new_system_services):
        _LOGGER.info('Removing old system service: %s', svc_name)
        fs.rmtree_safe(os.path.join(sys_dir, svc_name))

    sys_scandir = supervisor.create_scan_dir(
        sys_dir,
        finish_timeout=6000,
        wait_cgroups=cgroups_path,
    )
    for svc_def in app.system_services:
        if svc_def.restart is not None:
            monitor_policy = {
                'limit': svc_def.restart.limit,
                'interval': svc_def.restart.interval,
                'tombstone': {
                    'uds': False,
                    'path': tm_env.services_tombstone_dir,
                    'id': '{},{}'.format(uniq_name, svc_def.name)
                }
            }
        else:
            monitor_policy = None

        supervisor.create_service(
            sys_scandir,
            name=svc_def.name,
            app_run_script=svc_def.command,
            userid='root',
            environ_dir=os.path.join(container_dir, _CONTAINER_ENV_DIR),
            environ={envvar.name: envvar.value
                     for envvar in svc_def.environ},
            environment=app.environment,
            downed=svc_def.downed,
            trace=None,
            monitor_policy=monitor_policy)
    sys_scandir.write()

    services_dir = os.path.join(container_dir, 'services')
    services_scandir = supervisor.create_scan_dir(services_dir,
                                                  finish_timeout=5000)

    for svc_def in app.services:

        if svc_def.restart is not None:
            monitor_policy = {
                'limit': svc_def.restart.limit,
                'interval': svc_def.restart.interval,
                'tombstone': {
                    'uds': True,
                    'path': tombstone_ctl_uds,
                    'id': '{},{}'.format(uniq_name, svc_def.name)
                }
            }
        else:
            monitor_policy = None

        if svc_def.trace is not None:
            trace = {
                'instanceid': app.name,
                'uniqueid': app.uniqueid,
                'service': svc_def.name,
                'path': os.path.join(ctl_uds, 'appevents')
            }
        else:
            trace = None

        logger_template = getattr(svc_def, 'logger', 's6.app-logger.run')
        _LOGGER.info('Using logger: %s', logger_template)

        supervisor.create_service(
            services_scandir,
            name=svc_def.name,
            app_run_script=svc_def.command,
            userid=svc_def.proid,
            environ_dir='/' + _CONTAINER_ENV_DIR,
            environ={envvar.name: envvar.value
                     for envvar in svc_def.environ},
            environment=app.environment,
            downed=svc_def.downed,
            trace=trace if svc_def.trace else None,
            log_run_script=logger_template,
            monitor_policy=monitor_policy)
    services_scandir.write()

    # Bind the service directory in the container volume
    fs.mkdir_safe(os.path.join(root_dir, 'services'))
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, 'services'),
                        source=os.path.join(container_dir, 'services'),
                        recursive=False,
                        read_only=False)

    # Bind the ctrl directory in the container volume which has all the
    # unix domain sockets to communicate outside the container to treadmill
    fs.mkdir_safe(os.path.join(root_dir, 'run', 'tm_ctl'))
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, 'run', 'tm_ctl'),
                        source=tm_env.ctl_dir,
                        recursive=False,
                        read_only=False)
예제 #13
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)
예제 #14
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,
            )
예제 #15
0
파일: native.py 프로젝트: GrammaB/treadmill
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)
예제 #16
0
def create_supervision_tree(container_dir, root_dir, app, cgroups_path):
    """Creates s6 supervision tree."""
    sys_dir = os.path.join(container_dir, 'sys')
    sys_scandir = supervisor.create_scan_dir(
        sys_dir,
        finish_timeout=6000,
        monitor_service='monitor',
        wait_cgroups=cgroups_path,
    )
    for svc_def in app.system_services:
        if svc_def.restart is not None:
            monitor_policy = {
                'limit': svc_def.restart.limit,
                'interval': svc_def.restart.interval,
            }
        else:
            monitor_policy = None

        supervisor.create_service(
            sys_scandir,
            name=svc_def.name,
            app_run_script=svc_def.command,
            userid='root',
            environ_dir=os.path.join(container_dir, _CONTAINER_ENV_DIR),
            environ={envvar.name: envvar.value
                     for envvar in svc_def.environ},
            environment=app.environment,
            downed=svc_def.downed,
            trace=None,
            monitor_policy=monitor_policy)
    sys_scandir.write()

    services_dir = os.path.join(container_dir, 'services')
    services_scandir = supervisor.create_scan_dir(services_dir,
                                                  finish_timeout=5000)

    trace = {'instanceid': app.name, 'uniqueid': app.uniqueid}
    for svc_def in app.services:
        if svc_def.restart is not None:
            monitor_policy = {
                'limit': svc_def.restart.limit,
                'interval': svc_def.restart.interval,
            }
        else:
            monitor_policy = None

        supervisor.create_service(
            services_scandir,
            name=svc_def.name,
            app_run_script=svc_def.command,
            userid=svc_def.proid,
            environ_dir='/' + _CONTAINER_ENV_DIR,
            environ={envvar.name: envvar.value
                     for envvar in svc_def.environ},
            environment=app.environment,
            downed=False,
            trace=trace if svc_def.trace else None,
            log_run_script='s6.app-logger.run',
            monitor_policy=monitor_policy)
    services_scandir.write()

    # Bind the service directory in the container volume
    fs.mkdir_safe(os.path.join(root_dir, 'services'))
    fs_linux.mount_bind(root_dir,
                        os.path.join(os.sep, 'services'),
                        source=os.path.join(container_dir, 'services'),
                        recursive=False,
                        read_only=False)