mirror of
https://github.com/ansible/awx.git
synced 2026-05-16 05:47:38 -02:30
Use randomized file names for injector credential files
This commit is contained in:
@@ -718,8 +718,8 @@ class CredentialType(CommonModelNameNotUnique):
|
|||||||
os.chmod(path, stat.S_IRUSR)
|
os.chmod(path, stat.S_IRUSR)
|
||||||
return path
|
return path
|
||||||
|
|
||||||
path = build_extra_vars_file(extra_vars, private_data_dir)
|
|
||||||
if extra_vars:
|
if extra_vars:
|
||||||
|
path = build_extra_vars_file(extra_vars, private_data_dir)
|
||||||
args.extend(['-e', '@%s' % path])
|
args.extend(['-e', '@%s' % path])
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -30,11 +30,13 @@ def gce(cred, env, private_data_dir):
|
|||||||
'token_uri': 'https://accounts.google.com/o/oauth2/token',
|
'token_uri': 'https://accounts.google.com/o/oauth2/token',
|
||||||
}
|
}
|
||||||
|
|
||||||
path = os.path.join(private_data_dir, 'creds.json')
|
handle, path = tempfile.mkstemp(dir=private_data_dir)
|
||||||
with open(path, 'w') as f:
|
f = os.fdopen(handle, 'w')
|
||||||
json.dump(json_cred, f, indent=2)
|
json.dump(json_cred, f, indent=2)
|
||||||
|
f.close()
|
||||||
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
|
||||||
env['GCE_CREDENTIALS_FILE_PATH'] = path
|
env['GCE_CREDENTIALS_FILE_PATH'] = path
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
def azure_rm(cred, env, private_data_dir):
|
def azure_rm(cred, env, private_data_dir):
|
||||||
|
|||||||
@@ -1324,20 +1324,27 @@ class InventorySourceOptions(BaseModel):
|
|||||||
# in other cases we do not specify which plugin to use
|
# in other cases we do not specify which plugin to use
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_deprecated_credential(self, kind):
|
|
||||||
for cred in self.credentials.all():
|
|
||||||
if cred.credential_type.kind == kind:
|
|
||||||
return cred
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def get_cloud_credential(self):
|
def get_cloud_credential(self):
|
||||||
|
"""Return the credential which is directly tied to the inventory source type.
|
||||||
|
"""
|
||||||
credential = None
|
credential = None
|
||||||
for cred in self.credentials.all():
|
if self.source in CLOUD_PROVIDERS:
|
||||||
if cred.credential_type.kind != 'vault':
|
cred_kind = self.source.replace('ec2', 'aws')
|
||||||
credential = cred
|
for cred in self.credentials.all():
|
||||||
|
if cred.kind == cred_kind:
|
||||||
|
credential = cred
|
||||||
return credential
|
return credential
|
||||||
|
|
||||||
|
def get_extra_credentials(self):
|
||||||
|
"""Return all credentials that are not used by the inventory source injector.
|
||||||
|
"""
|
||||||
|
primary_cred = self.get_cloud_credential()
|
||||||
|
extra_creds = []
|
||||||
|
for cred in self.credentials.all():
|
||||||
|
if primary_cred and cred.pk != primary_cred.pk:
|
||||||
|
extra_creds.append(cred)
|
||||||
|
return extra_creds
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def credential(self):
|
def credential(self):
|
||||||
cred = self.get_cloud_credential()
|
cred = self.get_cloud_credential()
|
||||||
@@ -1711,14 +1718,6 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin,
|
|||||||
def get_ui_url(self):
|
def get_ui_url(self):
|
||||||
return urljoin(settings.TOWER_URL_BASE, "/#/jobs/inventory/{}".format(self.pk))
|
return urljoin(settings.TOWER_URL_BASE, "/#/jobs/inventory/{}".format(self.pk))
|
||||||
|
|
||||||
@property
|
|
||||||
def ansible_virtualenv_path(self):
|
|
||||||
if self.inventory and self.inventory.organization:
|
|
||||||
virtualenv = self.inventory.organization.custom_virtualenv
|
|
||||||
if virtualenv:
|
|
||||||
return virtualenv
|
|
||||||
return settings.ANSIBLE_VENV_PATH
|
|
||||||
|
|
||||||
def get_actual_source_path(self):
|
def get_actual_source_path(self):
|
||||||
'''Alias to source_path that combines with project path for for SCM file based sources'''
|
'''Alias to source_path that combines with project path for for SCM file based sources'''
|
||||||
if self.inventory_source_id is None or self.inventory_source.source_project_id is None:
|
if self.inventory_source_id is None or self.inventory_source.source_project_id is None:
|
||||||
@@ -1834,8 +1833,8 @@ class PluginFileInjector(object):
|
|||||||
def filename(self):
|
def filename(self):
|
||||||
return '{0}.yml'.format(self.plugin_name)
|
return '{0}.yml'.format(self.plugin_name)
|
||||||
|
|
||||||
def inventory_contents(self, inventory_source):
|
def inventory_contents(self, inventory_update, private_data_dir):
|
||||||
return yaml.safe_dump(self.inventory_as_dict(inventory_source), default_flow_style=False)
|
return yaml.safe_dump(self.inventory_as_dict(inventory_update, private_data_dir), default_flow_style=False)
|
||||||
|
|
||||||
def should_use_plugin(self):
|
def should_use_plugin(self):
|
||||||
return bool(
|
return bool(
|
||||||
@@ -1843,36 +1842,59 @@ class PluginFileInjector(object):
|
|||||||
Version(self.ansible_version) >= Version(self.initial_version)
|
Version(self.ansible_version) >= Version(self.initial_version)
|
||||||
)
|
)
|
||||||
|
|
||||||
def build_env(self, *args, **kwargs):
|
@staticmethod
|
||||||
if self.should_use_plugin():
|
def get_builtin_injector(source):
|
||||||
return self.build_plugin_env(*args, **kwargs)
|
from awx.main.models.credential import injectors as builtin_injectors
|
||||||
else:
|
cred_kind = source.replace('ec2', 'aws')
|
||||||
return self.build_script_env(*args, **kwargs)
|
if cred_kind not in dir(builtin_injectors):
|
||||||
|
return None
|
||||||
|
return getattr(builtin_injectors, cred_kind)
|
||||||
|
|
||||||
def build_plugin_env(self, inventory_update, env, private_data_dir):
|
def build_env(self, inventory_update, env, private_data_dir):
|
||||||
|
if self.should_use_plugin():
|
||||||
|
injector_env = self.get_plugin_env(inventory_update, private_data_dir)
|
||||||
|
else:
|
||||||
|
injector_env = self.get_script_env(inventory_update, private_data_dir)
|
||||||
|
env.update(injector_env)
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def build_script_env(self, inventory_update, env, private_data_dir):
|
def get_plugin_env(self, inventory_update, private_data_dir, safe=False):
|
||||||
return env
|
return self.get_script_env(inventory_update, private_data_dir, safe)
|
||||||
|
|
||||||
def build_private_data(self, *args, **kwargs):
|
def get_script_env(self, inventory_update, private_data_dir, safe=False):
|
||||||
|
"""By default, we will apply the standard managed_by_tower injectors
|
||||||
|
for the script injection
|
||||||
|
"""
|
||||||
|
injected_env = {}
|
||||||
|
credential = inventory_update.get_cloud_credential()
|
||||||
|
builtin_injector = self.get_builtin_injector(inventory_update.source)
|
||||||
|
if builtin_injector is None:
|
||||||
|
return {}
|
||||||
|
builtin_injector(credential, injected_env, private_data_dir)
|
||||||
|
if safe:
|
||||||
|
from awx.main.models.credential import build_safe_env
|
||||||
|
injected_env = build_safe_env(injected_env)
|
||||||
|
return injected_env
|
||||||
|
|
||||||
|
def build_private_data(self, inventory_update, private_data_dir):
|
||||||
if self.should_use_plugin():
|
if self.should_use_plugin():
|
||||||
return self.build_private_data(*args, **kwargs)
|
return self.build_plugin_private_data(inventory_update, private_data_dir)
|
||||||
else:
|
else:
|
||||||
return self.build_private_data(*args, **kwargs)
|
return self.build_script_private_data(inventory_update, private_data_dir)
|
||||||
|
|
||||||
def build_script_private_data(self, *args, **kwargs):
|
def build_script_private_data(self, inventory_update, private_data_dir):
|
||||||
pass
|
return None
|
||||||
|
|
||||||
def build_plugin_private_data(self, *args, **kwargs):
|
def build_plugin_private_data(self, inventory_update, private_data_dir):
|
||||||
pass
|
return None
|
||||||
|
|
||||||
|
|
||||||
class gce(PluginFileInjector):
|
class gce(PluginFileInjector):
|
||||||
plugin_name = 'gcp_compute'
|
plugin_name = 'gcp_compute'
|
||||||
initial_version = '2.6'
|
initial_version = '2.6'
|
||||||
|
|
||||||
def build_script_env(self, inventory_update, env, private_data_dir):
|
def get_script_env(self, inventory_update, private_data_dir):
|
||||||
|
env = super(gce, self).get_script_env(inventory_update, private_data_dir)
|
||||||
env['GCE_ZONE'] = inventory_update.source_regions if inventory_update.source_regions != 'all' else '' # noqa
|
env['GCE_ZONE'] = inventory_update.source_regions if inventory_update.source_regions != 'all' else '' # noqa
|
||||||
|
|
||||||
# by default, the GCE inventory source caches results on disk for
|
# by default, the GCE inventory source caches results on disk for
|
||||||
@@ -1886,21 +1908,44 @@ class gce(PluginFileInjector):
|
|||||||
env['GCE_INI_PATH'] = path
|
env['GCE_INI_PATH'] = path
|
||||||
return env
|
return env
|
||||||
|
|
||||||
def inventory_as_dict(self, inventory_source):
|
def inventory_as_dict(self, inventory_update, private_data_dir):
|
||||||
# NOTE: generalizing this to be use templating like credential types would be nice
|
# NOTE: generalizing this to be use templating like credential types would be nice
|
||||||
# but with YAML content that need to inject list parameters into the YAML,
|
# but with YAML content that need to inject list parameters into the YAML,
|
||||||
# it is hard to see any clean way we can possibly do this
|
# it is hard to see any clean way we can possibly do this
|
||||||
|
credential = inventory_update.get_cloud_credential()
|
||||||
|
builtin_injector = self.get_builtin_injector(inventory_update.source)
|
||||||
|
creds_path = builtin_injector(credential, {}, private_data_dir)
|
||||||
ret = dict(
|
ret = dict(
|
||||||
plugin='gcp_compute',
|
plugin='gcp_compute',
|
||||||
projects=[inventory_source.get_cloud_credential().project],
|
projects=[credential.get_input('project', default='')],
|
||||||
filters=None, # necessary cruft, see: https://github.com/ansible/ansible/pull/50025
|
filters=None, # necessary cruft, see: https://github.com/ansible/ansible/pull/50025
|
||||||
service_account_file="creds.json",
|
service_account_file=creds_path,
|
||||||
auth_kind="serviceaccount"
|
auth_kind="serviceaccount"
|
||||||
)
|
)
|
||||||
if inventory_source.source_regions and 'all' not in inventory_source.source_regions:
|
if inventory_update.source_regions and 'all' not in inventory_update.source_regions:
|
||||||
ret['zones'] = inventory_source.source_regions.split(',')
|
ret['zones'] = inventory_update.source_regions.split(',')
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
def get_plugin_env(self, inventory_update, private_data_dir, safe=False):
|
||||||
|
# gce wants everything defined in inventory & cred files
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
class rhv(PluginFileInjector):
|
||||||
|
|
||||||
|
def get_script_env(self, inventory_update, private_data_dir):
|
||||||
|
"""Unlike the others, ovirt uses the custom credential templating
|
||||||
|
"""
|
||||||
|
env = {'INVENTORY_UPDATE_ID': inventory_update.pk}
|
||||||
|
safe_env = env.copy()
|
||||||
|
args = []
|
||||||
|
safe_args = []
|
||||||
|
credential = inventory_update.get_cloud_credential()
|
||||||
|
credential.credential_type.inject_credential(
|
||||||
|
credential, env, safe_env, args, safe_args, private_data_dir
|
||||||
|
)
|
||||||
|
return env
|
||||||
|
|
||||||
|
|
||||||
for cls in PluginFileInjector.__subclasses__():
|
for cls in PluginFileInjector.__subclasses__():
|
||||||
InventorySourceOptions.injectors[cls.__name__] = cls
|
InventorySourceOptions.injectors[cls.__name__] = cls
|
||||||
|
|||||||
@@ -1939,6 +1939,10 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
|
|
||||||
If no private data is needed, return None.
|
If no private data is needed, return None.
|
||||||
"""
|
"""
|
||||||
|
if inventory_update.source in InventorySource.injectors:
|
||||||
|
injector = InventorySource.injectors[inventory_update.source](kwargs['ansible_version'])
|
||||||
|
return injector.build_private_data(inventory_update, kwargs.get('private_data_dir', None))
|
||||||
|
|
||||||
private_data = {'credentials': {}}
|
private_data = {'credentials': {}}
|
||||||
credential = inventory_update.get_cloud_credential()
|
credential = inventory_update.get_cloud_credential()
|
||||||
|
|
||||||
@@ -2283,10 +2287,9 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
src = inventory_update.source
|
src = inventory_update.source
|
||||||
if src in CLOUD_PROVIDERS:
|
if src in CLOUD_PROVIDERS:
|
||||||
if src in InventorySource.injectors:
|
if src in InventorySource.injectors:
|
||||||
cloud_cred = inventory_update.get_cloud_credential()
|
injector = InventorySource.injectors[inventory_update.source](self.get_ansible_version(inventory_update))
|
||||||
injector = InventorySource.injectors[cloud_cred.kind](self.get_ansible_version(inventory_update))
|
|
||||||
if injector.should_use_plugin():
|
if injector.should_use_plugin():
|
||||||
content = injector.inventory_contents(inventory_update)
|
content = injector.inventory_contents(inventory_update, kwargs['private_data_dir'])
|
||||||
# must be a statically named file
|
# must be a statically named file
|
||||||
inventory_path = os.path.join(private_data_dir, injector.filename)
|
inventory_path = os.path.join(private_data_dir, injector.filename)
|
||||||
with open(inventory_path, 'w') as f:
|
with open(inventory_path, 'w') as f:
|
||||||
@@ -2333,8 +2336,8 @@ class RunInventoryUpdate(BaseTask):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
def build_credentials_list(self, inventory_update):
|
def build_credentials_list(self, inventory_update):
|
||||||
# TODO: allow multiple custom creds for inv updates
|
# All credentials not used by inventory source injector
|
||||||
return [inventory_update.get_cloud_credential()]
|
return [inventory_update.get_extra_credentials()]
|
||||||
|
|
||||||
def get_idle_timeout(self):
|
def get_idle_timeout(self):
|
||||||
return getattr(settings, 'INVENTORY_UPDATE_IDLE_TIMEOUT', None)
|
return getattr(settings, 'INVENTORY_UPDATE_IDLE_TIMEOUT', None)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ filters: null
|
|||||||
plugin: gcp_compute
|
plugin: gcp_compute
|
||||||
projects:
|
projects:
|
||||||
- fooo
|
- fooo
|
||||||
service_account_file: creds.json
|
service_account_file: {{ file_reference }}
|
||||||
zones:
|
zones:
|
||||||
- us-east4-a
|
- us-east4-a
|
||||||
- us-west1-b
|
- us-west1-b
|
||||||
|
|||||||
@@ -137,8 +137,6 @@ def read_content(private_data_dir, env, inventory_update):
|
|||||||
references[abs_file_path] = filename # plugin filenames are universal
|
references[abs_file_path] = filename # plugin filenames are universal
|
||||||
except IsADirectoryError:
|
except IsADirectoryError:
|
||||||
dir_contents[abs_file_path] = '<directory>'
|
dir_contents[abs_file_path] = '<directory>'
|
||||||
print('dir contents')
|
|
||||||
print(dir_contents)
|
|
||||||
|
|
||||||
# Declare cross-file references, also use special keywords if it is the cache
|
# Declare cross-file references, also use special keywords if it is the cache
|
||||||
cache_referenced = False
|
cache_referenced = False
|
||||||
@@ -169,8 +167,6 @@ def read_content(private_data_dir, env, inventory_update):
|
|||||||
raise AssertionError(
|
raise AssertionError(
|
||||||
'A cache file was referenced but never created, files:\n{}'.format(
|
'A cache file was referenced but never created, files:\n{}'.format(
|
||||||
json.dumps(dir_contents, indent=4)))
|
json.dumps(dir_contents, indent=4)))
|
||||||
print('dir contents')
|
|
||||||
print(dir_contents)
|
|
||||||
|
|
||||||
content = {}
|
content = {}
|
||||||
for abs_file_path, file_content in dir_contents.items():
|
for abs_file_path, file_content in dir_contents.items():
|
||||||
@@ -181,8 +177,6 @@ def read_content(private_data_dir, env, inventory_update):
|
|||||||
reference_key = references[abs_file_path]
|
reference_key = references[abs_file_path]
|
||||||
file_content = private_key_regex.sub('{{private_key}}', file_content)
|
file_content = private_key_regex.sub('{{private_key}}', file_content)
|
||||||
content[reference_key] = file_content
|
content[reference_key] = file_content
|
||||||
print(' contents')
|
|
||||||
print(content)
|
|
||||||
|
|
||||||
return content
|
return content
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user