executor: run trusted playbook in a bubblewrap
This change renames untrusted_wrapper to execution_wrapper and uses bubblewrap for both trusted and untrusted playbooks by default. This change adds new options to the zuul.conf executor section to let operators define what directories to mount ro or rw for both context: * trusted_ro_dirs/trusted_rw_dirs, and * untrusted_ro_dirs/untrusted_rw_dirs Change-Id: I9a8a74a338a8a837913db5e2effeef1bd949a49c Story: 2001070 Task: 4687
This commit is contained in:
parent
2438860823
commit
44aef15d6e
|
@ -26,6 +26,8 @@ zuul_url=http://zuul.example.com/p
|
|||
|
||||
[executor]
|
||||
default_username=zuul
|
||||
trusted_ro_dirs=/opt/zuul-scripts:/var/cache
|
||||
trusted_rw_dirs=/opt/zuul-logs
|
||||
|
||||
[webapp]
|
||||
listen_address=0.0.0.0
|
||||
|
|
|
@ -2024,6 +2024,8 @@ class ZuulTestCase(BaseTestCase):
|
|||
project = reponame.replace('_', '/')
|
||||
self.copyDirToRepo(project,
|
||||
os.path.join(git_path, reponame))
|
||||
# Make test_root persist after ansible run for .flag test
|
||||
self.config.set('executor', 'trusted_rw_dirs', self.test_root)
|
||||
self.setupAllProjectKeys()
|
||||
|
||||
def setupSimpleLayout(self):
|
||||
|
|
|
@ -31,17 +31,14 @@ class TestBubblewrap(testtools.TestCase):
|
|||
def test_bubblewrap_wraps(self):
|
||||
bwrap = bubblewrap.BubblewrapDriver()
|
||||
work_dir = tempfile.mkdtemp()
|
||||
ansible_dir = tempfile.mkdtemp()
|
||||
ssh_agent = SshAgent()
|
||||
self.addCleanup(ssh_agent.stop)
|
||||
ssh_agent.start()
|
||||
po = bwrap.getPopen(work_dir=work_dir,
|
||||
ansible_dir=ansible_dir,
|
||||
ssh_auth_sock=ssh_agent.env['SSH_AUTH_SOCK'])
|
||||
self.assertTrue(po.passwd_r > 2)
|
||||
self.assertTrue(po.group_r > 2)
|
||||
self.assertTrue(work_dir in po.command)
|
||||
self.assertTrue(ansible_dir in po.command)
|
||||
# Now run /usr/bin/id to verify passwd/group entries made it in
|
||||
true_proc = po(['/usr/bin/id'], stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
|
|
|
@ -278,3 +278,13 @@ class WrapperInterface(object):
|
|||
:rtype: Callable
|
||||
"""
|
||||
pass
|
||||
|
||||
@abc.abstractmethod
|
||||
def setMountsMap(self, state_dir, ro_dirs=[], rw_dirs=[]):
|
||||
"""Add additional mount point to the execution environment.
|
||||
|
||||
:arg str state_dir: the state directory to be read write
|
||||
:arg list ro_dirs: read only directories paths
|
||||
:arg list rw_dirs: read write directories paths
|
||||
"""
|
||||
pass
|
||||
|
|
|
@ -84,7 +84,6 @@ class BubblewrapDriver(Driver, WrapperInterface):
|
|||
'--ro-bind', '/bin', '/bin',
|
||||
'--ro-bind', '/sbin', '/sbin',
|
||||
'--ro-bind', '/etc/resolv.conf', '/etc/resolv.conf',
|
||||
'--ro-bind', '{ansible_dir}', '{ansible_dir}',
|
||||
'--ro-bind', '{ssh_auth_sock}', '{ssh_auth_sock}',
|
||||
'--dir', '{work_dir}',
|
||||
'--bind', '{work_dir}', '{work_dir}',
|
||||
|
@ -99,6 +98,7 @@ class BubblewrapDriver(Driver, WrapperInterface):
|
|||
'--file', '{uid_fd}', '/etc/passwd',
|
||||
'--file', '{gid_fd}', '/etc/group',
|
||||
]
|
||||
mounts_map = {'rw': [], 'ro': []}
|
||||
|
||||
def reconfigure(self, tenant):
|
||||
pass
|
||||
|
@ -106,6 +106,9 @@ class BubblewrapDriver(Driver, WrapperInterface):
|
|||
def stop(self):
|
||||
pass
|
||||
|
||||
def setMountsMap(self, state_dir, ro_dirs=[], rw_dirs=[]):
|
||||
self.mounts_map = {'ro': ro_dirs, 'rw': [state_dir] + rw_dirs}
|
||||
|
||||
def getPopen(self, **kwargs):
|
||||
# Set zuul_dir if it was not passed in
|
||||
if 'zuul_dir' in kwargs:
|
||||
|
@ -119,6 +122,11 @@ class BubblewrapDriver(Driver, WrapperInterface):
|
|||
if not zuul_dir.startswith('/usr'):
|
||||
bwrap_command.extend(['--ro-bind', zuul_dir, zuul_dir])
|
||||
|
||||
for mount_type in ('ro', 'rw'):
|
||||
bind_arg = '--ro-bind' if mount_type == 'ro' else '--bind'
|
||||
for bind in self.mounts_map[mount_type]:
|
||||
bwrap_command.extend([bind_arg, bind, bind])
|
||||
|
||||
# Need users and groups
|
||||
uid = os.getuid()
|
||||
passwd = pwd.getpwuid(uid)
|
||||
|
@ -159,14 +167,12 @@ def main(args=None):
|
|||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('work_dir')
|
||||
parser.add_argument('ansible_dir')
|
||||
parser.add_argument('run_args', nargs='+')
|
||||
cli_args = parser.parse_args()
|
||||
|
||||
ssh_auth_sock = os.environ.get('SSH_AUTH_SOCK')
|
||||
|
||||
popen = driver.getPopen(work_dir=cli_args.work_dir,
|
||||
ansible_dir=cli_args.ansible_dir,
|
||||
ssh_auth_sock=ssh_auth_sock)
|
||||
x = popen(cli_args.run_args)
|
||||
x.wait()
|
||||
|
|
|
@ -26,3 +26,6 @@ class NullwrapDriver(Driver, WrapperInterface):
|
|||
|
||||
def getPopen(self, **kwargs):
|
||||
return subprocess.Popen
|
||||
|
||||
def setMountsMap(self, **kwargs):
|
||||
pass
|
||||
|
|
|
@ -382,9 +382,9 @@ class ExecutorServer(object):
|
|||
'default_username', 'zuul')
|
||||
self.merge_email = get_default(self.config, 'merger', 'git_user_email')
|
||||
self.merge_name = get_default(self.config, 'merger', 'git_user_name')
|
||||
untrusted_wrapper_name = get_default(self.config, 'executor',
|
||||
'untrusted_wrapper', 'bubblewrap')
|
||||
self.untrusted_wrapper = connections.drivers[untrusted_wrapper_name]
|
||||
execution_wrapper_name = get_default(self.config, 'executor',
|
||||
'execution_wrapper', 'bubblewrap')
|
||||
self.execution_wrapper = connections.drivers[execution_wrapper_name]
|
||||
|
||||
self.connections = connections
|
||||
# This merger and its git repos are used to maintain
|
||||
|
@ -1238,14 +1238,24 @@ class AnsibleJob(object):
|
|||
|
||||
if trusted:
|
||||
config_file = self.jobdir.trusted_config
|
||||
popen = subprocess.Popen
|
||||
opt_prefix = 'trusted'
|
||||
else:
|
||||
config_file = self.jobdir.untrusted_config
|
||||
driver = self.executor_server.untrusted_wrapper
|
||||
popen = driver.getPopen(
|
||||
work_dir=self.jobdir.root,
|
||||
ansible_dir=self.executor_server.ansible_dir,
|
||||
ssh_auth_sock=env_copy.get('SSH_AUTH_SOCK'))
|
||||
opt_prefix = 'untrusted'
|
||||
ro_dirs = get_default(self.executor_server.config, 'executor',
|
||||
'%s_ro_dirs' % opt_prefix)
|
||||
rw_dirs = get_default(self.executor_server.config, 'executor',
|
||||
'%s_rw_dirs' % opt_prefix)
|
||||
state_dir = get_default(self.executor_server.config, 'zuul',
|
||||
'state_dir', '/var/lib/zuul', expand_user=True)
|
||||
ro_dirs = ro_dirs.split(":") if ro_dirs else []
|
||||
rw_dirs = rw_dirs.split(":") if rw_dirs else []
|
||||
self.executor_server.execution_wrapper.setMountsMap(state_dir, ro_dirs,
|
||||
rw_dirs)
|
||||
|
||||
popen = self.executor_server.execution_wrapper.getPopen(
|
||||
work_dir=self.jobdir.root,
|
||||
ssh_auth_sock=env_copy.get('SSH_AUTH_SOCK'))
|
||||
|
||||
env_copy['ANSIBLE_CONFIG'] = config_file
|
||||
|
||||
|
|
Loading…
Reference in New Issue