Reapply "Move the home directory outside of work/"

This reverts commit 279bbfb346.

Change-Id: If734440ca26c9deca9070dc480661eab79390c63
This commit is contained in:
James E. Blair
2026-05-19 15:33:31 -07:00
parent bfcbb9f652
commit af244e1a66
6 changed files with 49 additions and 20 deletions
+5
View File
@@ -32,6 +32,11 @@ that is readable and writable by the job. The hierarchy is:
Where the Ansible log for the job is written; your job
may place other logs here as well.
The home directory of the user running the job is outside this
hierarchy. A future version of Zuul will restrict which files in the
home directory may be carried over between playbooks. To use files in
later playbooks, put them under ``work/`` rather than ``$HOME``.
Git Repositories
----------------
+1 -1
View File
@@ -3,7 +3,7 @@
- hosts: all
tasks:
- name: Test skopeo inside the executor bwrap
command: docker run --rm -i --privileged quay.io/zuul-ci/zuul-executor:latest zuul-bwrap /tmp skopeo copy docker://quay.io/zuul-ci/zuul:latest oci:test
command: docker run --rm -i --privileged quay.io/zuul-ci/zuul-executor:latest zuul-bwrap /tmp /tmp skopeo copy docker://quay.io/zuul-ci/zuul:latest oci:test
register: skopeo_output
- name: Verify that skopeo ran correctly
assert:
@@ -0,0 +1,9 @@
---
upgrade:
- |
The home directory of the user that is used to run playbooks on
the executor has been moved outside of the build's ``work/``
directory. A future version of Zuul will restict which files in
the home directory may be carried over between playbooks. Files
that should persist between playbooks should generally be placed
in the build`s ``work/`` directory instead.
+6
View File
@@ -38,10 +38,12 @@ class TestBubblewrap(testtools.TestCase):
bwrap.check()
context = bwrap.getExecutionContext()
work_dir = tempfile.mkdtemp()
home_dir = tempfile.mkdtemp()
ssh_agent = SshAgent()
self.addCleanup(ssh_agent.stop)
ssh_agent.start()
po = context.getPopen(work_dir=work_dir,
home_dir=home_dir,
ssh_auth_sock=ssh_agent.env['SSH_AUTH_SOCK'])
self.assertTrue(po.fds[0] > 2)
self.assertTrue(po.fds[1] > 2)
@@ -68,10 +70,12 @@ class TestBubblewrap(testtools.TestCase):
bwrap.check()
context = bwrap.getExecutionContext()
work_dir = tempfile.mkdtemp()
home_dir = tempfile.mkdtemp()
ssh_agent = SshAgent()
self.addCleanup(ssh_agent.stop)
ssh_agent.start()
po = context.getPopen(work_dir=work_dir,
home_dir=home_dir,
ssh_auth_sock=ssh_agent.env['SSH_AUTH_SOCK'])
self.assertTrue(po.fds[0] > 2)
self.assertTrue(po.fds[1] > 2)
@@ -93,11 +97,13 @@ class TestBubblewrap(testtools.TestCase):
bwrap.check()
context = bwrap.getExecutionContext()
work_dir = tempfile.mkdtemp()
home_dir = tempfile.mkdtemp()
ansible_dir = tempfile.mkdtemp()
ssh_agent = SshAgent()
self.addCleanup(ssh_agent.stop)
ssh_agent.start()
po = context.getPopen(work_dir=work_dir,
home_dir=home_dir,
ansible_dir=ansible_dir,
ssh_auth_sock=ssh_agent.env['SSH_AUTH_SOCK'])
leak_time = 60
+8 -4
View File
@@ -251,8 +251,8 @@ class BubblewrapExecutionContext(BaseExecutionContext):
# Need users and groups
uid = os.getuid()
passwd = list(pwd.getpwuid(uid))
# Replace our user's actual home directory with the work dir.
passwd = passwd[:5] + [kwargs['work_dir']] + passwd[6:]
# Replace our user's actual home directory with the build home dir.
passwd = passwd[:5] + [kwargs['home_dir']] + passwd[6:]
passwd_bytes = b':'.join(
['{}'.format(x).encode('utf8') for x in passwd])
(passwd_r, passwd_w) = os.pipe()
@@ -331,7 +331,8 @@ class BubblewrapDriver(Driver, WrapperInterface):
# Validate basic bwrap functionality before we attempt to run
# workloads under bwrap.
context = self.getExecutionContext()
popen = context.getPopen(work_dir='/tmp')
popen = context.getPopen(work_dir='/tmp',
home_dir='/tmp')
p = popen(['id'],
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
p.communicate()
@@ -340,7 +341,7 @@ class BubblewrapDriver(Driver, WrapperInterface):
" ".join(shlex.quote(c)
for c in popen.command + ['id']))
raise Exception('bwrap execution validation failed. You can '
'use `zuul-bwrap /tmp id` to investigate '
'use `zuul-bwrap /tmp /tmp id` to investigate '
'manually.')
def setCgroupManager(self, cgroup_manager):
@@ -424,6 +425,7 @@ class BubblewrapDriver(Driver, WrapperInterface):
'--ro-bind', '/etc/localtime', '/etc/localtime',
'--bind', '{work_dir}', '{work_dir}',
'--tmpfs', '{work_dir}/tmp',
'--bind', '{home_dir}', '{home_dir}',
'--proc', '/proc',
'--dev', '/dev',
'--chdir', '{work_dir}',
@@ -476,6 +478,7 @@ def main(args=None):
parser.add_argument('--rw-paths', nargs='+')
parser.add_argument('--secret', nargs='+')
parser.add_argument('work_dir')
parser.add_argument('home_dir')
parser.add_argument('run_args', nargs='+')
cli_args = parser.parse_args()
@@ -494,6 +497,7 @@ def main(args=None):
secrets)
popen = context.getPopen(work_dir=cli_args.work_dir,
home_dir=cli_args.home_dir,
ssh_auth_sock=ssh_auth_sock)
x = popen(cli_args.run_args)
x.wait()
+20 -15
View File
@@ -647,11 +647,12 @@ class JobDir(object):
# project_0
# <git.example.com>
# <project>
# home (mounted in bwrap read-write)
# .ssh
# known_hosts
# kube (mounted in bwrap read-only)
# config
# work (mounted in bwrap read-write)
# .ssh
# known_hosts
# src
# <git.example.com>
# <project>
@@ -666,6 +667,13 @@ class JobDir(object):
tmpdir = tempfile.gettempdir()
self.root = os.path.realpath(os.path.join(tmpdir, build_uuid))
os.mkdir(self.root, 0o700)
self.home = os.path.join(self.root, 'home')
os.makedirs(self.home)
ssh_dir = os.path.join(self.home, '.ssh')
os.mkdir(ssh_dir, 0o700)
# A home directory with nothing in it for utility calls.
self.utility_home = os.path.join(self.root, 'utility-home')
os.makedirs(self.utility_home)
self.work_root = os.path.join(self.root, 'work')
os.makedirs(self.work_root)
self.src_root = os.path.join(self.work_root, 'src')
@@ -689,8 +697,6 @@ class JobDir(object):
os.makedirs(self.trusted_root)
self.untrusted_root = os.path.join(self.root, 'untrusted')
os.makedirs(self.untrusted_root)
ssh_dir = os.path.join(self.work_root, '.ssh')
os.mkdir(ssh_dir, 0o700)
self.kube_dir = os.path.join(self.root, "kube")
os.makedirs(self.kube_dir)
self.kubeconfig = os.path.join(self.kube_dir, "config")
@@ -745,11 +751,11 @@ class JobDir(object):
with _safe_open(self.result_data_file, 'w'):
pass
self.known_hosts = os.path.join(ssh_dir, 'known_hosts')
# This file is not created or used by Zuul, but we forbid its
# use in jobs.
# The ssh config file is not created or used by Zuul, but we
# forbid its use in jobs.
self.path_blocklist = [
os.path.join(self.work_root, '.ssh', 'config'),
os.path.join(self.work_root, '.kube', 'config'),
os.path.join(self.home, '.ssh', 'config'),
os.path.join(self.home, '.kube', 'config'),
]
self.inventory = os.path.join(self.ansible_root, 'inventory.yaml')
self.logging_json = os.path.join(self.ansible_root, 'logging.json')
@@ -1993,7 +1999,7 @@ class AnsibleJob(object):
# the work directory.
env_copy = {}
env_copy['TMP'] = self.jobdir.local_tmp
env_copy['HOME'] = self.jobdir.work_root
env_copy['HOME'] = self.jobdir.utility_home
ro_paths = []
rw_paths = []
secrets = {}
@@ -2001,6 +2007,7 @@ class AnsibleJob(object):
context = wrapper.getExecutionContext(ro_paths, rw_paths, secrets)
return env_copy, context.getPopen(
work_dir=self.jobdir.work_root,
home_dir=self.jobdir.utility_home,
share_net=False,
)
@@ -3395,9 +3402,8 @@ class AnsibleJob(object):
allow_pre_fail, wrapped=True, cleanup=False,
phase=None, index=None):
# Cleanup any forbidden files in the home directory.
# TODO: move the home directory to a subdir of work/ and add a
# job-based allowlist of files to explicitly copy between
# playbook runs.
# TODO: add a job-based allowlist of files to explicitly copy
# between playbook runs.
for path in self.jobdir.path_blocklist:
try:
@@ -3471,12 +3477,11 @@ class AnsibleJob(object):
popen = context.getPopen(
work_dir=self.jobdir.work_root,
home_dir=self.jobdir.home,
ssh_auth_sock=env_copy.get('SSH_AUTH_SOCK'))
env_copy['ANSIBLE_CONFIG'] = config_file
# NOTE(pabelanger): Default HOME variable to jobdir.work_root, as it is
# possible we don't bind mount current zuul user home directory.
env_copy['HOME'] = self.jobdir.work_root
env_copy['HOME'] = self.jobdir.home
with self.proc_lock:
self.proc_cleanup = playbook.cleanup