remove unnecessary references to bwrap, bubblewrap, and proot

This commit is contained in:
Ryan Petrello 2021-03-23 15:41:52 -04:00
parent 73cc9e7b35
commit 5d210a1063
No known key found for this signature in database
GPG Key ID: F2AA5F2122351777
28 changed files with 76 additions and 341 deletions

View 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)]

View File

@ -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'),

View File

@ -52,7 +52,6 @@ ENV_BLOCKLIST = frozenset(
'VIRTUAL_ENV',
'PATH',
'PYTHONPATH',
'PROOT_TMP_DIR',
'JOB_ID',
'INVENTORY_ID',
'INVENTORY_SOURCE_ID',

View File

@ -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)

View File

@ -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:

View File

@ -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

View File

@ -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:

View File

@ -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'] == []

View File

@ -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`

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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:",

View File

@ -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', '[]');

View File

@ -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} />}

View File

@ -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
}
}

View File

@ -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",

View File

@ -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":[]}
}
}
}

View File

@ -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
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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'