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)
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)
def mount_docker_daemon_path(newroot_norm, app): """Mount tmpfs for docker """ if not _has_docker(app): return # /etc/docker as temp fs as dockerd create /etc/docker/key.json try: fs_linux.mount_tmpfs(newroot_norm, '/etc/docker') 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, )
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)
def _mount_docker_tmpfs(newroot_norm): """Mount tmpfs for docker """ # /etc/docker as temp fs as dockerd create /etc/docker/key.json fs_linux.mount_tmpfs(newroot_norm, '/etc/docker')
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, )
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)