Example #1
0
    def test_mount_bind_dir(self):
        """Tests fs.mount_bind directory binding behavior"""
        # test binding directory in /
        container_dir = os.path.join(self.root, 'container')
        os.makedirs(container_dir)

        # test binding directory in /
        foo_dir = os.path.join(self.root, 'foo')
        os.makedirs(foo_dir)
        fs.mount_bind(container_dir, foo_dir)
        treadmill.subproc.check_call.assert_called_with([
            'mount',
            '-n',
            '--rbind',
            foo_dir,
            os.path.join(container_dir, foo_dir[1:]),
        ])
        self.assertTrue(os.path.isdir(os.path.join(container_dir,
                                                   foo_dir[1:])))
        treadmill.subproc.check_call.reset_mock()

        # test binding directory with subdirs
        bar_dir = os.path.join(self.root, 'bar')
        os.makedirs(os.path.join(bar_dir, 'baz'))
        fs.mount_bind(container_dir, bar_dir)
        treadmill.subproc.check_call.assert_called_with([
            'mount', '-n', '--rbind', bar_dir,
            os.path.join(container_dir, bar_dir[1:])
        ])
        self.assertTrue(os.path.isdir(os.path.join(container_dir,
                                                   bar_dir[1:])))
        treadmill.subproc.check_call.reset_mock()
Example #2
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)
Example #3
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)
Example #4
0
    def test_mount_bind_dir(self):
        """Tests fs.mount_bind directory binding behavior"""
        # test binding directory in /
        fs.mount_bind(self.root, '/bin')
        treadmill.subproc.check_call.assert_called_with(
            [
                'mount',
                '-n',
                '--rbind',
                '/bin',
                os.path.join(self.root, 'bin'),
            ]
        )
        treadmill.subproc.check_call.reset_mock()
        self.assertTrue(os.path.isdir('%s/bin' % (self.root)))

        # test binding directory with subdirs
        fs.mount_bind(self.root, '/var/spool/tickets')
        treadmill.subproc.check_call.assert_called_with(
            [
                'mount',
                '-n',
                '--rbind',
                '/var/spool/tickets',
                os.path.join(self.root, 'var/spool/tickets'),
            ]
        )
        treadmill.subproc.check_call.reset_mock()
        self.assertTrue(os.path.isdir('%s/var/spool/tickets' % (self.root)))
Example #5
0
    def test_mount_bind_file(self):
        """Verifies correct mount options for files vs dirs."""
        fs.mount_bind(self.root, '/bin/ls')
        treadmill.subproc.check_call.assert_called_with(
            [
                'mount',
                '-n',
                '--bind',
                '/bin/ls',
                os.path.join(self.root, 'bin/ls'),
            ]
        )
        treadmill.subproc.check_call.reset_mock()
        self.assertTrue(os.path.isfile('%s/bin/ls' % (self.root)))

        fs.mount_bind(self.root, '/lib/libc.so.6')
        treadmill.subproc.check_call.assert_called_with(
            [
                'mount',
                '-n',
                '--bind',
                '/lib/libc.so.6',
                os.path.join(self.root, 'lib/libc.so.6'),
            ]
        )
        treadmill.subproc.check_call.reset_mock()
        self.assertTrue(os.path.isfile('%s/lib/libc.so.6' % (self.root)))
Example #6
0
 def _bind(src, tgt):
     """Helper function to bind source to target in the same root"""
     # FIXME(boysson): This name mount_bind() have counter-intuitive
     #                 arguments ordering.
     src_path = os.path.join(root_dir, src)
     if os.path.exists(src_path):
         fs.mount_bind(root_dir,
                       tgt,
                       target=src_path,
                       bind_opt='--bind')
Example #7
0
def share_cgroup_info(app, root_dir):
    """Shares subset of cgroup tree with the container."""
    # Bind /cgroup/memory inside chrooted environment to /cgroup/.../memory
    # of the container.
    unique_name = appcfg.app_unique_name(app)
    cgrp = os.path.join('treadmill', 'apps', unique_name)

    # 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.mount_bind(root_dir, os.path.join('/cgroup', subsystem),
                      cgroups.makepath(subsystem, cgrp))
Example #8
0
    def test_mount_bind_file(self):
        """Verifies correct mount options for files vs dirs."""
        container_dir = os.path.join(self.root, 'container')
        os.makedirs(container_dir)

        # test binding a file
        foo_file = os.path.join(self.root, 'foo')
        with open(os.path.join(self.root, 'foo'), 'w'):
            pass
        fs.mount_bind(container_dir, foo_file)
        treadmill.subproc.check_call.assert_called_with([
            'mount', '-n', '--bind', foo_file,
            os.path.join(container_dir, foo_file[1:])
        ])
        self.assertTrue(
            os.path.isfile(os.path.join(container_dir, foo_file[1:])))
        treadmill.subproc.check_call.reset_mock()
Example #9
0
def _bind_etc_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/host-aliases', 'etc/ld.so.preload',
            'etc/pam.d/sshd', 'etc/resolv.conf'
    ]:
        fs.mount_bind(root_dir,
                      os.path.join('/', overlay_file),
                      target=os.path.join(overlay_dir, overlay_file),
                      bind_opt='--bind')

    # Also override resolv.conf in the current mount namespace so that
    # system services have access to out resolver.
    fs.mount_bind('/',
                  '/etc/resolv.conf',
                  target=os.path.join(overlay_dir, 'etc/resolv.conf'),
                  bind_opt='--bind')
Example #10
0
def create_supervision_tree(container_dir, app):
    """Creates s6 supervision tree."""
    # Disable R0915: Too many statements
    # pylint: disable=R0915
    root_dir = os.path.join(container_dir, 'root')

    # Services and sys directories will be restored when container restarts
    # with data retention on existing volume.
    #
    # Sys directories will be removed. Services directory will stay, which
    # present a danger of accumulating restart counters in finished files.
    #
    # TODO:
    #
    # It is rather arbitrary how restart counts should work when data is
    # restored, but most likely services are "restart always" policy, so it
    # will not affect them.
    services_dir = os.path.join(container_dir, 'services')
    sys_dir = os.path.join(container_dir, 'sys')
    if os.path.exists(sys_dir):
        _LOGGER.info('Deleting existing sys dir: %s', sys_dir)
        shutil.rmtree(sys_dir)

    app_json = os.path.join(root_dir, 'app.json')

    # Create /services directory for the supervisor
    svcdir = os.path.join(root_dir, 'services')
    fs.mkdir_safe(svcdir)

    fs.mkdir_safe(services_dir)
    fs.mount_bind(root_dir, '/services', services_dir)

    root_pw = pwd.getpwnam('root')
    proid_pw = pwd.getpwnam(app.proid)

    # Create .s6-svscan directories for svscan finish
    sys_svscandir = os.path.join(sys_dir, '.s6-svscan')
    fs.mkdir_safe(sys_svscandir)

    svc_svscandir = os.path.join(services_dir, '.s6-svscan')
    fs.mkdir_safe(svc_svscandir)

    # svscan finish scripts to wait on all services
    utils.create_script(os.path.join(sys_svscandir, 'finish'),
                        'svscan.finish',
                        timeout=6000)

    utils.create_script(os.path.join(svc_svscandir, 'finish'),
                        'svscan.finish',
                        timeout=5000)

    for svc in app.services:
        if getattr(svc, 'root', False):
            svc_user = '******'
            svc_home = root_pw.pw_dir
            svc_shell = root_pw.pw_shell
        else:
            svc_user = app.proid
            svc_home = proid_pw.pw_dir
            svc_shell = proid_pw.pw_shell

        supervisor.create_service(
            services_dir,
            svc_user,
            svc_home,
            svc_shell,
            svc.name,
            svc.command,
            env=app.environment,
            down=True,
            envdirs=['/environ/app', '/environ/sys'],
            as_root=True,
        )
        _create_logrun(os.path.join(services_dir, svc.name))

    for svc in app.system_services:
        supervisor.create_service(
            services_dir,
            'root',
            root_pw.pw_dir,
            root_pw.pw_shell,
            svc.name,
            svc.command,
            env=app.environment,
            down=False,
            envdirs=['/environ/sys'],
            as_root=True,
        )
        _create_logrun(os.path.join(services_dir, svc.name))

    # Vring services
    for cell in app.vring.cells:
        fs.mkdir_safe(os.path.join(sys_dir, 'vring.%s' % cell))
        cmd = '%s sproc --zookeeper - --cell %s vring %s' % (
            treadmill.TREADMILL_BIN, cell, app_json)
        utils.create_script(os.path.join(sys_dir, 'vring.%s' % cell, 'run'),
                            'supervisor.run_sys',
                            cmd=cmd)
        _create_logrun(os.path.join(sys_dir, 'vring.%s' % cell))

    # Create endpoint presence service
    presence_monitor_cmd = '%s sproc presence monitor %s %s' % (
        treadmill.TREADMILL_BIN, app_json, container_dir)
    presence_register_cmd = '%s sproc presence register %s %s' % (
        treadmill.TREADMILL_BIN, app_json, container_dir)
    shadow_etc = os.path.join(container_dir, 'overlay', 'etc')
    host_aliases_cmd = '%s sproc host-aliases --aliases-dir %s %s %s' % (
        treadmill.TREADMILL_BIN,
        os.path.join(shadow_etc, 'host-aliases'),
        os.path.join(shadow_etc, 'hosts.original'),
        os.path.join(shadow_etc, 'hosts'),
    )

    _create_sysrun(sys_dir, 'monitor', presence_monitor_cmd)
    _create_sysrun(sys_dir, 'register', presence_register_cmd)
    _create_sysrun(sys_dir, 'hostaliases', host_aliases_cmd)

    cmd = None
    args = None

    if hasattr(app, 'command'):
        cmd = app.command

    if hasattr(app, 'args'):
        args = app.args

    if not cmd:
        cmd = subproc.resolve('s6_svscan')
        if not args:
            args = ['/services']

    _create_sysrun(sys_dir,
                   'start_container',
                   '%s %s %s -m -p -i %s %s' %
                   (subproc.resolve('chroot'), root_dir,
                    subproc.resolve('pid1'), cmd, ' '.join(args)),
                   down=True)
Example #11
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')