mirror of
https://github.com/ansible/awx.git
synced 2026-01-10 15:32:07 -03:30
remove unnecessary references to bwrap, bubblewrap, and proot
This commit is contained in:
parent
73cc9e7b35
commit
5d210a1063
16
awx/conf/migrations/0009_rename_proot_settings.py
Normal file
16
awx/conf/migrations/0009_rename_proot_settings.py
Normal file
@ -0,0 +1,16 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from django.db import migrations
|
||||
from awx.conf.migrations import _rename_setting
|
||||
|
||||
|
||||
def rename_proot_settings(apps, schema_editor):
|
||||
_rename_setting.rename_setting(apps, schema_editor, old_key='AWX_PROOT_BASE_PATH', new_key='AWX_ISOLATION_BASE_PATH')
|
||||
_rename_setting.rename_setting(apps, schema_editor, old_key='AWX_PROOT_SHOW_PATHS', new_key='AWX_ISOLATION_SHOW_PATHS')
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [('conf', '0008_subscriptions')]
|
||||
|
||||
operations = [migrations.RunPython(rename_proot_settings)]
|
||||
@ -233,16 +233,7 @@ register(
|
||||
)
|
||||
|
||||
register(
|
||||
'AWX_PROOT_ENABLED',
|
||||
field_class=fields.BooleanField,
|
||||
label=_('Enable job isolation'),
|
||||
help_text=_('Isolates an Ansible job from protected parts of the system to prevent exposing sensitive information.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
|
||||
register(
|
||||
'AWX_PROOT_BASE_PATH',
|
||||
'AWX_ISOLATION_BASE_PATH',
|
||||
field_class=fields.CharField,
|
||||
label=_('Job execution path'),
|
||||
help_text=_(
|
||||
@ -255,17 +246,7 @@ register(
|
||||
)
|
||||
|
||||
register(
|
||||
'AWX_PROOT_HIDE_PATHS',
|
||||
field_class=fields.StringListField,
|
||||
required=False,
|
||||
label=_('Paths to hide from isolated jobs'),
|
||||
help_text=_('Additional paths to hide from isolated processes. Enter one path per line.'),
|
||||
category=_('Jobs'),
|
||||
category_slug='jobs',
|
||||
)
|
||||
|
||||
register(
|
||||
'AWX_PROOT_SHOW_PATHS',
|
||||
'AWX_ISOLATION_SHOW_PATHS',
|
||||
field_class=fields.StringListField,
|
||||
required=False,
|
||||
label=_('Paths to expose to isolated jobs'),
|
||||
|
||||
@ -52,7 +52,6 @@ ENV_BLOCKLIST = frozenset(
|
||||
'VIRTUAL_ENV',
|
||||
'PATH',
|
||||
'PYTHONPATH',
|
||||
'PROOT_TMP_DIR',
|
||||
'JOB_ID',
|
||||
'INVENTORY_ID',
|
||||
'INVENTORY_SOURCE_ID',
|
||||
|
||||
@ -135,7 +135,7 @@ class IsolatedManager(object):
|
||||
|
||||
extravars = {
|
||||
'src': self.private_data_dir,
|
||||
'dest': settings.AWX_PROOT_BASE_PATH,
|
||||
'dest': settings.AWX_ISOLATION_BASE_PATH,
|
||||
'ident': self.ident,
|
||||
'job_id': self.instance.id,
|
||||
}
|
||||
@ -304,7 +304,7 @@ class IsolatedManager(object):
|
||||
if not len(instance_qs):
|
||||
return
|
||||
try:
|
||||
private_data_dir = tempfile.mkdtemp(prefix='awx_iso_heartbeat_', dir=settings.AWX_PROOT_BASE_PATH)
|
||||
private_data_dir = tempfile.mkdtemp(prefix='awx_iso_heartbeat_', dir=settings.AWX_ISOLATION_BASE_PATH)
|
||||
self.runner_params = self.build_runner_params([instance.hostname for instance in instance_qs])
|
||||
self.runner_params['private_data_dir'] = private_data_dir
|
||||
self.runner_params['forks'] = len(instance_qs)
|
||||
|
||||
@ -69,8 +69,6 @@ class AnsibleInventoryLoader(object):
|
||||
def __init__(self, source, venv_path=None, verbosity=0):
|
||||
self.source = source
|
||||
self.verbosity = verbosity
|
||||
# TODO: remove once proot has been removed
|
||||
self.tmp_private_dir = None
|
||||
if venv_path:
|
||||
self.venv_path = venv_path
|
||||
else:
|
||||
|
||||
@ -25,7 +25,7 @@ class Command(BaseCommand):
|
||||
raise CommandError("--hostname is a required argument")
|
||||
|
||||
try:
|
||||
path = tempfile.mkdtemp(prefix='awx_isolated_ssh', dir=settings.AWX_PROOT_BASE_PATH)
|
||||
path = tempfile.mkdtemp(prefix='awx_isolated_ssh', dir=settings.AWX_ISOLATION_BASE_PATH)
|
||||
ssh_key = None
|
||||
if all([getattr(settings, 'AWX_ISOLATED_KEY_GENERATION', False) is True, getattr(settings, 'AWX_ISOLATED_PRIVATE_KEY', None)]):
|
||||
ssh_key = settings.AWX_ISOLATED_PRIVATE_KEY
|
||||
|
||||
@ -841,7 +841,6 @@ class BaseTask(object):
|
||||
model = None
|
||||
event_model = None
|
||||
abstract = True
|
||||
proot_show_paths = []
|
||||
|
||||
def __init__(self):
|
||||
self.cleanup_paths = []
|
||||
@ -908,9 +907,9 @@ class BaseTask(object):
|
||||
if pull:
|
||||
params['container_options'].append(f'--pull={pull}')
|
||||
|
||||
if settings.AWX_PROOT_SHOW_PATHS:
|
||||
if settings.AWX_ISOLATION_SHOW_PATHS:
|
||||
params['container_volume_mounts'] = []
|
||||
for this_path in settings.AWX_PROOT_SHOW_PATHS:
|
||||
for this_path in settings.AWX_ISOLATION_SHOW_PATHS:
|
||||
params['container_volume_mounts'].append(f'{this_path}:{this_path}:Z')
|
||||
return params
|
||||
|
||||
@ -924,7 +923,7 @@ class BaseTask(object):
|
||||
"""
|
||||
Create a temporary directory for job-related files.
|
||||
"""
|
||||
pdd_wrapper_path = tempfile.mkdtemp(prefix=f'pdd_wrapper_{instance.pk}_', dir=settings.AWX_PROOT_BASE_PATH)
|
||||
pdd_wrapper_path = tempfile.mkdtemp(prefix=f'pdd_wrapper_{instance.pk}_', dir=settings.AWX_ISOLATION_BASE_PATH)
|
||||
os.chmod(pdd_wrapper_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
if settings.AWX_CLEANUP_PATHS:
|
||||
self.cleanup_paths.append(pdd_wrapper_path)
|
||||
@ -1090,12 +1089,6 @@ class BaseTask(object):
|
||||
"""
|
||||
return False
|
||||
|
||||
def should_use_proot(self, instance):
|
||||
"""
|
||||
Return whether this task should use proot.
|
||||
"""
|
||||
return False
|
||||
|
||||
def build_inventory(self, instance, private_data_dir):
|
||||
script_params = dict(hostvars=True, towervars=True)
|
||||
if hasattr(instance, 'job_slice_number'):
|
||||
@ -1371,8 +1364,8 @@ class BaseTask(object):
|
||||
status = self.instance.status
|
||||
raise RuntimeError('not starting %s task' % self.instance.status)
|
||||
|
||||
if not os.path.exists(settings.AWX_PROOT_BASE_PATH):
|
||||
raise RuntimeError('AWX_PROOT_BASE_PATH=%s does not exist' % settings.AWX_PROOT_BASE_PATH)
|
||||
if not os.path.exists(settings.AWX_ISOLATION_BASE_PATH):
|
||||
raise RuntimeError('AWX_ISOLATION_BASE_PATH=%s does not exist' % settings.AWX_ISOLATION_BASE_PATH)
|
||||
|
||||
# store a record of the venv used at runtime
|
||||
if hasattr(self.instance, 'custom_virtualenv'):
|
||||
@ -1598,8 +1591,7 @@ class RunJob(BaseTask):
|
||||
env['ANSIBLE_CALLBACK_PLUGINS'] = ':'.join(settings.AWX_ANSIBLE_CALLBACK_PLUGINS)
|
||||
env['AWX_HOST'] = settings.TOWER_URL_BASE
|
||||
|
||||
# Create a directory for ControlPath sockets that is unique to each
|
||||
# job and visible inside the proot environment (when enabled).
|
||||
# Create a directory for ControlPath sockets that is unique to each job
|
||||
cp_dir = os.path.join(private_data_dir, 'cp')
|
||||
if not os.path.exists(cp_dir):
|
||||
os.mkdir(cp_dir, 0o700)
|
||||
@ -1768,14 +1760,6 @@ class RunJob(BaseTask):
|
||||
"""
|
||||
return settings.AWX_RESOURCE_PROFILING_ENABLED
|
||||
|
||||
def should_use_proot(self, job):
|
||||
"""
|
||||
Return whether this task should use proot.
|
||||
"""
|
||||
if job.is_container_group_task:
|
||||
return False
|
||||
return getattr(settings, 'AWX_PROOT_ENABLED', False)
|
||||
|
||||
def build_execution_environment_params(self, instance):
|
||||
if settings.IS_K8S:
|
||||
return {}
|
||||
@ -1929,10 +1913,6 @@ class RunProjectUpdate(BaseTask):
|
||||
event_model = ProjectUpdateEvent
|
||||
event_data_key = 'project_update_id'
|
||||
|
||||
@property
|
||||
def proot_show_paths(self):
|
||||
return [settings.PROJECTS_ROOT]
|
||||
|
||||
def __init__(self, *args, job_private_data_dir=None, **kwargs):
|
||||
super(RunProjectUpdate, self).__init__(*args, **kwargs)
|
||||
self.playbook_new_revision = None
|
||||
@ -1990,7 +1970,7 @@ class RunProjectUpdate(BaseTask):
|
||||
env['DISPLAY'] = '' # Prevent stupid password popup when running tests.
|
||||
# give ansible a hint about the intended tmpdir to work around issues
|
||||
# like https://github.com/ansible/ansible/issues/30064
|
||||
env['TMP'] = settings.AWX_PROOT_BASE_PATH
|
||||
env['TMP'] = settings.AWX_ISOLATION_BASE_PATH
|
||||
env['PROJECT_UPDATE_ID'] = str(project_update.pk)
|
||||
if settings.GALAXY_IGNORE_CERTS:
|
||||
env['ANSIBLE_GALAXY_IGNORE'] = True
|
||||
@ -2394,12 +2374,6 @@ class RunProjectUpdate(BaseTask):
|
||||
if status == 'successful' and instance.launch_type != 'sync':
|
||||
self._update_dependent_inventories(instance, dependent_inventory_sources)
|
||||
|
||||
def should_use_proot(self, project_update):
|
||||
"""
|
||||
Return whether this task should use proot.
|
||||
"""
|
||||
return getattr(settings, 'AWX_PROOT_ENABLED', False)
|
||||
|
||||
def build_execution_environment_params(self, instance):
|
||||
if settings.IS_K8S:
|
||||
return {}
|
||||
@ -2790,7 +2764,7 @@ class RunAdHocCommand(BaseTask):
|
||||
env['ANSIBLE_SFTP_BATCH_MODE'] = 'False'
|
||||
|
||||
# Create a directory for ControlPath sockets that is unique to each
|
||||
# ad hoc command and visible inside the proot environment (when enabled).
|
||||
# ad hoc command
|
||||
cp_dir = os.path.join(private_data_dir, 'cp')
|
||||
if not os.path.exists(cp_dir):
|
||||
os.mkdir(cp_dir, 0o700)
|
||||
@ -2894,14 +2868,6 @@ class RunAdHocCommand(BaseTask):
|
||||
d[r'Password:\s*?$'] = 'ssh_password'
|
||||
return d
|
||||
|
||||
def should_use_proot(self, ad_hoc_command):
|
||||
"""
|
||||
Return whether this task should use proot.
|
||||
"""
|
||||
if ad_hoc_command.is_container_group_task:
|
||||
return False
|
||||
return getattr(settings, 'AWX_PROOT_ENABLED', False)
|
||||
|
||||
def final_run_hook(self, adhoc_job, status, private_data_dir, fact_modification_times, isolated_manager_instance=None):
|
||||
super(RunAdHocCommand, self).final_run_hook(adhoc_job, status, private_data_dir, fact_modification_times)
|
||||
if isolated_manager_instance:
|
||||
|
||||
@ -33,16 +33,14 @@ def test_jobs_settings(get, put, patch, delete, admin):
|
||||
response = get(url, user=admin, expect=200)
|
||||
data = dict(response.data.items())
|
||||
put(url, user=admin, data=data, expect=200)
|
||||
patch(url, user=admin, data={'AWX_PROOT_HIDE_PATHS': ['/home']}, expect=200)
|
||||
patch(url, user=admin, data={'AWX_ISOLATION_SHOW_PATHS': ['/home']}, expect=200)
|
||||
response = get(url, user=admin, expect=200)
|
||||
assert response.data['AWX_PROOT_HIDE_PATHS'] == ['/home']
|
||||
data.pop('AWX_PROOT_HIDE_PATHS')
|
||||
data.pop('AWX_PROOT_SHOW_PATHS')
|
||||
assert response.data['AWX_ISOLATION_SHOW_PATHS'] == ['/home']
|
||||
data.pop('AWX_ISOLATION_SHOW_PATHS')
|
||||
data.pop('AWX_ANSIBLE_CALLBACK_PLUGINS')
|
||||
put(url, user=admin, data=data, expect=200)
|
||||
response = get(url, user=admin, expect=200)
|
||||
assert response.data['AWX_PROOT_HIDE_PATHS'] == []
|
||||
assert response.data['AWX_PROOT_SHOW_PATHS'] == []
|
||||
assert response.data['AWX_ISOLATION_SHOW_PATHS'] == []
|
||||
assert response.data['AWX_ANSIBLE_CALLBACK_PLUGINS'] == []
|
||||
|
||||
|
||||
|
||||
@ -725,7 +725,6 @@ class TestIsolatedExecution(TestJobExecution):
|
||||
extra_vars = json.loads(extra_vars)
|
||||
assert extra_vars['dest'] == '/tmp'
|
||||
assert extra_vars['src'] == private_data
|
||||
assert extra_vars['proot_temp_dir'].startswith('/tmp/awx_proot_')
|
||||
|
||||
def test_systemctl_failure(self):
|
||||
# If systemctl fails, read the contents of `artifacts/systemctl_logs`
|
||||
|
||||
@ -69,9 +69,6 @@ __all__ = [
|
||||
'get_system_task_capacity',
|
||||
'get_cpu_capacity',
|
||||
'get_mem_capacity',
|
||||
'wrap_args_with_proot',
|
||||
'build_proot_temp_dir',
|
||||
'check_proot_installed',
|
||||
'model_to_dict',
|
||||
'NullablePromptPseudoField',
|
||||
'model_instance_diff',
|
||||
@ -842,94 +839,6 @@ def set_environ(**environ):
|
||||
os.environ.update(old_environ)
|
||||
|
||||
|
||||
@memoize()
|
||||
def check_proot_installed():
|
||||
"""
|
||||
Check that proot is installed.
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
cmd = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--version']
|
||||
try:
|
||||
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
proc.communicate()
|
||||
return bool(proc.returncode == 0)
|
||||
except (OSError, ValueError) as e:
|
||||
if isinstance(e, ValueError) or getattr(e, 'errno', 1) != 2: # ENOENT, no such file or directory
|
||||
logger.exception('bwrap unavailable for unexpected reason.')
|
||||
return False
|
||||
|
||||
|
||||
def build_proot_temp_dir():
|
||||
"""
|
||||
Create a temporary directory for proot to use.
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
path = tempfile.mkdtemp(prefix='awx_proot_', dir=settings.AWX_PROOT_BASE_PATH)
|
||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
return path
|
||||
|
||||
|
||||
def wrap_args_with_proot(args, cwd, **kwargs):
|
||||
"""
|
||||
Wrap existing command line with proot to restrict access to:
|
||||
- AWX_PROOT_BASE_PATH (generally, /tmp) (except for own /tmp files)
|
||||
For non-isolated nodes:
|
||||
- /etc/tower (to prevent obtaining db info or secret key)
|
||||
- /var/lib/awx (except for current project)
|
||||
- /var/log/tower
|
||||
- /var/log/supervisor
|
||||
"""
|
||||
from django.conf import settings
|
||||
|
||||
cwd = os.path.realpath(cwd)
|
||||
new_args = [getattr(settings, 'AWX_PROOT_CMD', 'bwrap'), '--unshare-pid', '--dev-bind', '/', '/', '--proc', '/proc']
|
||||
hide_paths = [settings.AWX_PROOT_BASE_PATH]
|
||||
if not kwargs.get('isolated'):
|
||||
hide_paths.extend(['/etc/tower', '/var/lib/awx', '/var/log', '/etc/ssh', settings.PROJECTS_ROOT, settings.JOBOUTPUT_ROOT])
|
||||
hide_paths.extend(getattr(settings, 'AWX_PROOT_HIDE_PATHS', None) or [])
|
||||
for path in sorted(set(hide_paths)):
|
||||
if not os.path.exists(path):
|
||||
continue
|
||||
path = os.path.realpath(path)
|
||||
if os.path.isdir(path):
|
||||
new_path = tempfile.mkdtemp(dir=kwargs['proot_temp_dir'])
|
||||
os.chmod(new_path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
|
||||
else:
|
||||
handle, new_path = tempfile.mkstemp(dir=kwargs['proot_temp_dir'])
|
||||
os.close(handle)
|
||||
os.chmod(new_path, stat.S_IRUSR | stat.S_IWUSR)
|
||||
new_args.extend(['--bind', '%s' % (new_path,), '%s' % (path,)])
|
||||
if kwargs.get('isolated'):
|
||||
show_paths = [kwargs['private_data_dir']]
|
||||
elif 'private_data_dir' in kwargs:
|
||||
show_paths = [cwd, kwargs['private_data_dir']]
|
||||
else:
|
||||
show_paths = [cwd]
|
||||
for venv in (settings.ANSIBLE_VENV_PATH, settings.AWX_VENV_PATH, kwargs.get('proot_custom_virtualenv')):
|
||||
if venv:
|
||||
new_args.extend(['--ro-bind', venv, venv])
|
||||
show_paths.extend(getattr(settings, 'AWX_PROOT_SHOW_PATHS', None) or [])
|
||||
show_paths.extend(kwargs.get('proot_show_paths', []))
|
||||
for path in sorted(set(show_paths)):
|
||||
if not os.path.exists(path):
|
||||
continue
|
||||
path = os.path.realpath(path)
|
||||
new_args.extend(['--bind', '%s' % (path,), '%s' % (path,)])
|
||||
if kwargs.get('isolated'):
|
||||
if '/bin/ansible-playbook' in ' '.join(args):
|
||||
# playbook runs should cwd to the SCM checkout dir
|
||||
new_args.extend(['--chdir', os.path.join(kwargs['private_data_dir'], 'project')])
|
||||
else:
|
||||
# ad-hoc runs should cwd to the root of the private data dir
|
||||
new_args.extend(['--chdir', kwargs['private_data_dir']])
|
||||
else:
|
||||
new_args.extend(['--chdir', cwd])
|
||||
new_args.extend(args)
|
||||
return new_args
|
||||
|
||||
|
||||
def get_pk_from_dict(_dict, key):
|
||||
"""
|
||||
Helper for obtaining a pk from user data dict or None if not present.
|
||||
|
||||
@ -569,26 +569,15 @@ AWX_SHOW_PLAYBOOK_LINKS = False
|
||||
# Applies to any galaxy server
|
||||
GALAXY_IGNORE_CERTS = False
|
||||
|
||||
# Enable bubblewrap support for running jobs (playbook runs only).
|
||||
# Additional paths to show for jobs using process isolation.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_ENABLED = True
|
||||
|
||||
# Command/path to bubblewrap.
|
||||
AWX_PROOT_CMD = 'bwrap'
|
||||
|
||||
# Additional paths to hide from jobs using bubblewrap.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_HIDE_PATHS = []
|
||||
|
||||
# Additional paths to show for jobs using bubbelwrap.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_SHOW_PATHS = []
|
||||
AWX_ISOLATION_SHOW_PATHS = []
|
||||
|
||||
# The directory in which Tower will create new temporary directories for job
|
||||
# execution and isolation (such as credential files and custom
|
||||
# inventory scripts).
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_BASE_PATH = "/tmp"
|
||||
AWX_ISOLATION_BASE_PATH = "/tmp"
|
||||
|
||||
# Disable resource profiling by default
|
||||
AWX_RESOURCE_PROFILING_ENABLED = False
|
||||
|
||||
@ -67,10 +67,6 @@ CALLBACK_QUEUE = "callback_tasks"
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_ROLES_ENABLED = True
|
||||
|
||||
# Enable PROOT for tower-qa integration tests.
|
||||
# Note: This setting may be overridden by database settings.
|
||||
AWX_PROOT_ENABLED = True
|
||||
|
||||
AWX_ISOLATED_USERNAME = 'root'
|
||||
AWX_ISOLATED_CHECK_INTERVAL = 1
|
||||
AWX_ISOLATED_PERIODIC_CHECK = 30
|
||||
|
||||
@ -101,7 +101,6 @@
|
||||
"VIRTUAL_ENV": "/var/lib/awx/venv/ansible",
|
||||
"INVENTORY_ID": "1",
|
||||
"MAX_EVENT_RES": "700000",
|
||||
"PROOT_TMP_DIR": "/tmp",
|
||||
"ANSIBLE_LIBRARY": "/awx_devel/awx/plugins/library",
|
||||
"SDB_NOTIFY_HOST": "docker.for.mac.host.internal",
|
||||
"AWX_GROUP_QUEUES": "tower",
|
||||
|
||||
@ -101,7 +101,6 @@
|
||||
"VIRTUAL_ENV": "/var/lib/awx/venv/ansible",
|
||||
"INVENTORY_ID": "1",
|
||||
"MAX_EVENT_RES": "700000",
|
||||
"PROOT_TMP_DIR": "/tmp",
|
||||
"ANSIBLE_LIBRARY": "/awx_devel/awx/plugins/library",
|
||||
"SDB_NOTIFY_HOST": "docker.for.mac.host.internal",
|
||||
"AWX_GROUP_QUEUES": "tower",
|
||||
|
||||
@ -154,7 +154,6 @@
|
||||
"ANSIBLE_INVENTORY_UNPARSED_FAILED": "True",
|
||||
"ANSIBLE_PARAMIKO_RECORD_HOST_KEYS": "False",
|
||||
"ANSIBLE_VENV_PATH": "/var/lib/awx/venv/ansible",
|
||||
"PROOT_TMP_DIR": "/tmp",
|
||||
"AWX_PRIVATE_DATA_DIR": "/tmp/awx_2_a4b1afiw",
|
||||
"ANSIBLE_COLLECTIONS_PATHS": "/tmp/collections",
|
||||
"PYTHONPATH": "/var/lib/awx/venv/ansible/lib/python2.7/site-packages:/awx_devel/awx/lib:",
|
||||
|
||||
@ -50,7 +50,6 @@ describe('<JobsDetail />', () => {
|
||||
});
|
||||
|
||||
test('should render expected details', () => {
|
||||
assertDetail(wrapper, 'Enable job isolation', 'On');
|
||||
assertDetail(wrapper, 'Job execution path', '/tmp');
|
||||
assertDetail(wrapper, 'Isolated status check interval', '1 seconds');
|
||||
assertDetail(wrapper, 'Isolated launch timeout', '600 seconds');
|
||||
@ -81,7 +80,6 @@ describe('<JobsDetail />', () => {
|
||||
'Ansible Modules Allowed for Ad Hoc Jobs',
|
||||
'[\n "command"\n]'
|
||||
);
|
||||
assertVariableDetail(wrapper, 'Paths to hide from isolated jobs', '[]');
|
||||
assertVariableDetail(wrapper, 'Paths to expose to isolated jobs', '[]');
|
||||
assertVariableDetail(wrapper, 'Extra Environment Variables', '{}');
|
||||
assertVariableDetail(wrapper, 'Ansible Callback Plugins', '[]');
|
||||
|
||||
@ -70,8 +70,7 @@ function JobsEdit() {
|
||||
await submitForm({
|
||||
...form,
|
||||
AD_HOC_COMMANDS: formatJson(form.AD_HOC_COMMANDS),
|
||||
AWX_PROOT_SHOW_PATHS: formatJson(form.AWX_PROOT_SHOW_PATHS),
|
||||
AWX_PROOT_HIDE_PATHS: formatJson(form.AWX_PROOT_HIDE_PATHS),
|
||||
AWX_ISOLATION_SHOW_PATHS: formatJson(form.AWX_ISOLATION_SHOW_PATHS),
|
||||
AWX_ANSIBLE_CALLBACK_PLUGINS: formatJson(
|
||||
form.AWX_ANSIBLE_CALLBACK_PLUGINS
|
||||
),
|
||||
@ -116,8 +115,8 @@ function JobsEdit() {
|
||||
<Form autoComplete="off" onSubmit={formik.handleSubmit}>
|
||||
<FormColumnLayout>
|
||||
<InputField
|
||||
name="AWX_PROOT_BASE_PATH"
|
||||
config={jobs.AWX_PROOT_BASE_PATH}
|
||||
name="AWX_ISOLATION_BASE_PATH"
|
||||
config={jobs.AWX_ISOLATION_BASE_PATH}
|
||||
isRequired
|
||||
/>
|
||||
<InputField
|
||||
@ -151,10 +150,6 @@ function JobsEdit() {
|
||||
config={jobs.MAX_FORKS}
|
||||
type="number"
|
||||
/>
|
||||
<BooleanField
|
||||
name="AWX_PROOT_ENABLED"
|
||||
config={jobs.AWX_PROOT_ENABLED}
|
||||
/>
|
||||
<BooleanField
|
||||
name="PROJECT_UPDATE_VVV"
|
||||
config={jobs.PROJECT_UPDATE_VVV}
|
||||
@ -209,12 +204,8 @@ function JobsEdit() {
|
||||
config={jobs.AWX_ANSIBLE_CALLBACK_PLUGINS}
|
||||
/>
|
||||
<ObjectField
|
||||
name="AWX_PROOT_SHOW_PATHS"
|
||||
config={jobs.AWX_PROOT_SHOW_PATHS}
|
||||
/>
|
||||
<ObjectField
|
||||
name="AWX_PROOT_HIDE_PATHS"
|
||||
config={jobs.AWX_PROOT_HIDE_PATHS}
|
||||
name="AWX_ISOLATION_SHOW_PATHS"
|
||||
config={jobs.AWX_ISOLATION_SHOW_PATHS}
|
||||
/>
|
||||
<ObjectField name="AWX_TASK_ENV" config={jobs.AWX_TASK_ENV} />
|
||||
{submitError && <FormSubmitError error={submitError} />}
|
||||
|
||||
@ -27,10 +27,8 @@
|
||||
"AWX_ISOLATED_CONNECTION_TIMEOUT": 10,
|
||||
"AWX_ISOLATED_HOST_KEY_CHECKING": false,
|
||||
"AWX_ISOLATED_LAUNCH_TIMEOUT": 600,
|
||||
"AWX_PROOT_BASE_PATH": "/tmp",
|
||||
"AWX_PROOT_ENABLED": true,
|
||||
"AWX_PROOT_HIDE_PATHS": [],
|
||||
"AWX_PROOT_SHOW_PATHS": [],
|
||||
"AWX_ISOLATION_BASE_PATH": "/tmp",
|
||||
"AWX_ISOLATION_SHOW_PATHS": [],
|
||||
"AWX_RESOURCE_PROFILING_CPU_POLL_INTERVAL": 0.25,
|
||||
"AWX_RESOURCE_PROFILING_ENABLED": false,
|
||||
"AWX_RESOURCE_PROFILING_MEMORY_POLL_INTERVAL": 0.25,
|
||||
@ -45,4 +43,4 @@
|
||||
"MAX_FORKS": 200,
|
||||
"PROJECT_UPDATE_VVV": false,
|
||||
"SCHEDULE_MAX_JOBS": 10
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,15 +166,7 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"AWX_PROOT_ENABLED": {
|
||||
"type": "boolean",
|
||||
"label": "Enable job isolation",
|
||||
"help_text": "Isolates an Ansible job from protected parts of the system to prevent exposing sensitive information.",
|
||||
"category": "Jobs",
|
||||
"category_slug": "jobs",
|
||||
"defined_in_file": false
|
||||
},
|
||||
"AWX_PROOT_BASE_PATH": {
|
||||
"AWX_ISOLATION_BASE_PATH": {
|
||||
"type": "string",
|
||||
"label": "Job execution path",
|
||||
"help_text": "The directory in which Tower will create new temporary directories for job execution and isolation (such as credential files and custom inventory scripts).",
|
||||
@ -182,18 +174,7 @@
|
||||
"category_slug": "jobs",
|
||||
"defined_in_file": false
|
||||
},
|
||||
"AWX_PROOT_HIDE_PATHS": {
|
||||
"type": "list",
|
||||
"label": "Paths to hide from isolated jobs",
|
||||
"help_text": "Additional paths to hide from isolated processes. Enter one path per line.",
|
||||
"category": "Jobs",
|
||||
"category_slug": "jobs",
|
||||
"defined_in_file": false,
|
||||
"child": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"AWX_PROOT_SHOW_PATHS": {
|
||||
"AWX_ISOLATION_SHOW_PATHS": {
|
||||
"type": "list",
|
||||
"label": "Paths to expose to isolated jobs",
|
||||
"help_text": "List of paths that would otherwise be hidden to expose to isolated jobs. Enter one path per line.",
|
||||
@ -3158,16 +3139,7 @@
|
||||
]
|
||||
]
|
||||
},
|
||||
"AWX_PROOT_ENABLED": {
|
||||
"type": "boolean",
|
||||
"required": true,
|
||||
"label": "Enable job isolation",
|
||||
"help_text": "Isolates an Ansible job from protected parts of the system to prevent exposing sensitive information.",
|
||||
"category": "Jobs",
|
||||
"category_slug": "jobs",
|
||||
"default": true
|
||||
},
|
||||
"AWX_PROOT_BASE_PATH": {
|
||||
"AWX_ISOLATION_BASE_PATH": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"label": "Job execution path",
|
||||
@ -3176,21 +3148,7 @@
|
||||
"category_slug": "jobs",
|
||||
"default": "/tmp"
|
||||
},
|
||||
"AWX_PROOT_HIDE_PATHS": {
|
||||
"type": "list",
|
||||
"required": false,
|
||||
"label": "Paths to hide from isolated jobs",
|
||||
"help_text": "Additional paths to hide from isolated processes. Enter one path per line.",
|
||||
"category": "Jobs",
|
||||
"category_slug": "jobs",
|
||||
"default": [],
|
||||
"child": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"read_only": false
|
||||
}
|
||||
},
|
||||
"AWX_PROOT_SHOW_PATHS": {
|
||||
"AWX_ISOLATION_SHOW_PATHS": {
|
||||
"type": "list",
|
||||
"required": false,
|
||||
"label": "Paths to expose to isolated jobs",
|
||||
|
||||
@ -34,10 +34,8 @@
|
||||
"win_user"
|
||||
],
|
||||
"ALLOW_JINJA_IN_EXTRA_VARS":"template",
|
||||
"AWX_PROOT_ENABLED":true,
|
||||
"AWX_PROOT_BASE_PATH":"/tmp",
|
||||
"AWX_PROOT_HIDE_PATHS":[],
|
||||
"AWX_PROOT_SHOW_PATHS":[],
|
||||
"AWX_ISOLATION_BASE_PATH":"/tmp",
|
||||
"AWX_ISOLATION_SHOW_PATHS":[],
|
||||
"AWX_ISOLATED_CHECK_INTERVAL":1,
|
||||
"AWX_ISOLATED_LAUNCH_TIMEOUT":600,
|
||||
"AWX_ISOLATED_CONNECTION_TIMEOUT":10,
|
||||
@ -306,4 +304,4 @@
|
||||
"users":{"fields":["username"],"adj_list":[]},
|
||||
"instances":{"fields":["hostname"],"adj_list":[]}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,10 +4,8 @@
|
||||
"command"
|
||||
],
|
||||
"ALLOW_JINJA_IN_EXTRA_VARS": "template",
|
||||
"AWX_PROOT_ENABLED": true,
|
||||
"AWX_PROOT_BASE_PATH": "/tmp",
|
||||
"AWX_PROOT_HIDE_PATHS": [],
|
||||
"AWX_PROOT_SHOW_PATHS": [],
|
||||
"AWX_ISOLATION_BASE_PATH": "/tmp",
|
||||
"AWX_ISOLATION_SHOW_PATHS": [],
|
||||
"AWX_ISOLATED_CHECK_INTERVAL": 1,
|
||||
"AWX_ISOLATED_LAUNCH_TIMEOUT": 600,
|
||||
"AWX_ISOLATED_CONNECTION_TIMEOUT": 10,
|
||||
@ -34,4 +32,4 @@
|
||||
"DEFAULT_PROJECT_UPDATE_TIMEOUT": 0,
|
||||
"ANSIBLE_FACT_CACHE_TIMEOUT": 0,
|
||||
"MAX_FORKS": 200
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,15 +41,15 @@ extends_documentation_fragment: awx.awx.auth
|
||||
'''
|
||||
|
||||
EXAMPLES = '''
|
||||
- name: Set the value of AWX_PROOT_BASE_PATH
|
||||
- name: Set the value of AWX_ISOLATION_BASE_PATH
|
||||
tower_settings:
|
||||
name: AWX_PROOT_BASE_PATH
|
||||
name: AWX_ISOLATION_BASE_PATH
|
||||
value: "/tmp"
|
||||
register: testing_settings
|
||||
|
||||
- name: Set the value of AWX_PROOT_SHOW_PATHS
|
||||
- name: Set the value of AWX_ISOLATION_SHOW_PATHS
|
||||
tower_settings:
|
||||
name: "AWX_PROOT_SHOW_PATHS"
|
||||
name: "AWX_ISOLATION_SHOW_PATHS"
|
||||
value: "'/var/lib/awx/projects/', '/tmp'"
|
||||
register: testing_settings
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@
|
||||
-----END EC PRIVATE KEY-----
|
||||
organization: Default
|
||||
|
||||
- name: Disable bubblewrap
|
||||
- name: Disable process isolation
|
||||
command: tower-cli setting modify AWX_PROOT_ENABLED false
|
||||
|
||||
- block:
|
||||
@ -54,5 +54,5 @@
|
||||
--credential dummy --module-name command
|
||||
--module-args "mkdir -p {{ project_base_dir }}/{{ project_dir_name }}"
|
||||
always:
|
||||
- name: enable bubblewrap
|
||||
- name: enable process isolation
|
||||
command: tower-cli setting modify AWX_PROOT_ENABLED true
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
---
|
||||
- name: Set the value of AWX_PROOT_SHOW_PATHS to a baseline
|
||||
- name: Set the value of AWX_ISOLATION_SHOW_PATHS to a baseline
|
||||
tower_settings:
|
||||
name: AWX_PROOT_SHOW_PATHS
|
||||
name: AWX_ISOLATION_SHOW_PATHS
|
||||
value: '["/var/lib/awx/projects/"]'
|
||||
|
||||
- name: Set the value of AWX_PROOT_SHOW_PATHS to get an error back from Tower
|
||||
- name: Set the value of AWX_ISOLATION_SHOW_PATHS to get an error back from Tower
|
||||
tower_settings:
|
||||
settings:
|
||||
AWX_PROOT_SHOW_PATHS:
|
||||
AWX_ISOLATION_SHOW_PATHS:
|
||||
'not': 'a valid'
|
||||
'tower': 'setting'
|
||||
register: result
|
||||
@ -17,9 +17,9 @@
|
||||
that:
|
||||
- "result is failed"
|
||||
|
||||
- name: Set the value of AWX_PROOT_SHOW_PATHS
|
||||
- name: Set the value of AWX_ISOLATION_SHOW_PATHS
|
||||
tower_settings:
|
||||
name: AWX_PROOT_SHOW_PATHS
|
||||
name: AWX_ISOLATION_SHOW_PATHS
|
||||
value: '["/var/lib/awx/projects/", "/tmp"]'
|
||||
register: result
|
||||
|
||||
@ -27,9 +27,9 @@
|
||||
that:
|
||||
- "result is changed"
|
||||
|
||||
- name: Attempt to set the value of AWX_PROOT_BASE_PATH to what it already is
|
||||
- name: Attempt to set the value of AWX_ISOLATION_BASE_PATH to what it already is
|
||||
tower_settings:
|
||||
name: AWX_PROOT_BASE_PATH
|
||||
name: AWX_ISOLATION_BASE_PATH
|
||||
value: /tmp
|
||||
register: result
|
||||
|
||||
@ -42,7 +42,7 @@
|
||||
|
||||
- name: Apply a single setting via settings
|
||||
tower_settings:
|
||||
name: AWX_PROOT_SHOW_PATHS
|
||||
name: AWX_ISOLATION_SHOW_PATHS
|
||||
value: '["/var/lib/awx/projects/", "/var/tmp"]'
|
||||
register: result
|
||||
|
||||
@ -53,8 +53,8 @@
|
||||
- name: Apply multiple setting via settings with no change
|
||||
tower_settings:
|
||||
settings:
|
||||
AWX_PROOT_BASE_PATH: /tmp
|
||||
AWX_PROOT_SHOW_PATHS: ["/var/lib/awx/projects/", "/var/tmp"]
|
||||
AWX_ISOLATION_BASE_PATH: /tmp
|
||||
AWX_ISOLATION_SHOW_PATHS: ["/var/lib/awx/projects/", "/var/tmp"]
|
||||
register: result
|
||||
|
||||
- debug:
|
||||
@ -67,8 +67,8 @@
|
||||
- name: Apply multiple setting via settings with change
|
||||
tower_settings:
|
||||
settings:
|
||||
AWX_PROOT_BASE_PATH: /tmp
|
||||
AWX_PROOT_SHOW_PATHS: []
|
||||
AWX_ISOLATION_BASE_PATH: /tmp
|
||||
AWX_ISOLATION_SHOW_PATHS: []
|
||||
register: result
|
||||
|
||||
- assert:
|
||||
@ -77,7 +77,7 @@
|
||||
|
||||
- name: Handle an omit value
|
||||
tower_settings:
|
||||
name: AWX_PROOT_BASE_PATH
|
||||
name: AWX_ISOLATION_BASE_PATH
|
||||
value: '{{ junk_var | default(omit) }}'
|
||||
register: result
|
||||
ignore_errors: true
|
||||
|
||||
@ -103,7 +103,7 @@ When a job is scheduled to run on an "isolated" instance:
|
||||
- a static inventory file
|
||||
- pexpect passwords
|
||||
- environment variables
|
||||
- the `ansible`/`ansible-playbook` command invocation, _i.e._, `bwrap ... ansible-playbook -i /path/to/inventory /path/to/playbook.yml -e ...`
|
||||
- the `ansible`/`ansible-playbook` command invocation, _i.e._, `ansible-playbook -i /path/to/inventory /path/to/playbook.yml -e ...`
|
||||
|
||||
* Once the metadata has been `rsync`ed to the isolated host, the "controller instance" starts a process on the "isolated" instance which consumes the metadata and starts running `ansible`/`ansible-playbook`. As the playbook runs, job artifacts (such as `stdout` and job events) are written to disk on the "isolated" instance.
|
||||
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
## Process Isolation Overview
|
||||
|
||||
In older versions of Ansible Tower, we used a system called `proot` to isolate Tower job processes from the rest of the system.
|
||||
|
||||
Tower version 3.1 and later switched to using `bubblewrap`, which is a much lighter-weight and maintained process isolation system.
|
||||
|
||||
Tower 3.5 and later uses the process isolation feature in Ansible runner to achieve process isolation.
|
||||
|
||||
|
||||
### Activating Process Isolation
|
||||
|
||||
`bubblewrap` is enabled by default; it can be turned off via Tower Config or from a Tower settings file:
|
||||
|
||||
AWX_PROOT_ENABLED = False
|
||||
|
||||
Process isolation, when enabled, will be used for the following Job Types:
|
||||
|
||||
* Job Templates - Launching jobs from regular job templates
|
||||
* Ad-hoc Commands - Launching ad-hoc commands against one or more hosts in inventory
|
||||
|
||||
|
||||
### Tunables
|
||||
|
||||
Process Isolation will, by default, hide the following directories from the tasks mentioned above:
|
||||
|
||||
* `/etc/tower` - To prevent exposing Tower configuration
|
||||
* `/var/lib/awx` - With the exception of the current project being used (for regular job templates)
|
||||
* `/var/log`
|
||||
* `/tmp` (or whatever the system `temp dir` is) - With the exception of the processes's own temp files
|
||||
|
||||
If there is other information on the system that is sensitive and should be hidden, it can be added via the Tower Configuration Screen
|
||||
or by updating the following entry in a tower settings file:
|
||||
|
||||
AWX_PROOT_HIDE_PATHS = ['/list/of/', '/paths']
|
||||
|
||||
If there are any directories that should specifically be exposed that can be set in a similar way:
|
||||
|
||||
AWX_PROOT_SHOW_PATHS = ['/list/of/', '/paths']
|
||||
|
||||
By default, the system will use the system's `tmp dir` (`/tmp` by default) as its staging area. This can be changed via the following setting:
|
||||
|
||||
AWX_PROOT_BASE_PATH = "/opt/tmp"
|
||||
|
||||
|
||||
### Project Folder Isolation
|
||||
|
||||
Starting in AWX versions above 6.0.0, the project folder will be copied for each job run.
|
||||
This allows playbooks to make local changes to the source tree for convenience,
|
||||
such as creating temporary files, without the possibility of interference with
|
||||
other jobs.
|
||||
@ -187,7 +187,7 @@ This task spawns an `ansible` process, which then runs a command using Ansible.
|
||||
- Build a dictionary of passwords for the SSH private key, SSH user and sudo/su.
|
||||
- Build an environment dictionary for Ansible.
|
||||
- Build a command line argument list for running Ansible, optionally using `ssh-agent` for public/private key authentication.
|
||||
- Return whether the task should use `bwrap`.
|
||||
- Return whether the task should use process isolation.
|
||||
|
||||
For more information on ad hoc commands, read the [Running Ad Hoc Commands section](https://docs.ansible.com/ansible-tower/latest/html/userguide/inventories.html#running-ad-hoc-commands) of the Inventories page of the Ansible Tower User Guide.
|
||||
|
||||
|
||||
@ -22,10 +22,6 @@ SECRET_KEY = get_secret()
|
||||
|
||||
ALLOWED_HOSTS = ['*']
|
||||
|
||||
# Container environments don't like chroots
|
||||
AWX_PROOT_ENABLED = False
|
||||
|
||||
|
||||
CLUSTER_HOST_ID = "awx"
|
||||
SYSTEM_UUID = '00000000-0000-0000-0000-000000000000'
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user