From e61d0c5cb793b36e8bad4ea2d9614450266c1c48 Mon Sep 17 00:00:00 2001 From: Rebeccah Date: Fri, 26 Mar 2021 10:27:08 -0400 Subject: [PATCH] credential validation for execution envs to allow only registry credentials to be associated with them, also adding security precautions for authfile and password, also combined token & password into one term to align with Quay, and added handling to account for users not filling in credential data and add a has_inputs function to simplify checking if the host, username, and password are present in the credential --- awx/api/serializers.py | 5 +++++ awx/main/models/credential/__init__.py | 12 +++++++++--- awx/main/tasks.py | 24 +++++++++++++++--------- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/awx/api/serializers.py b/awx/api/serializers.py index cae3724ca3..8ac06b6793 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -1412,6 +1412,11 @@ class ExecutionEnvironmentSerializer(BaseSerializer): res['credential'] = self.reverse('api:credential_detail', kwargs={'pk': obj.credential.pk}) return res + def validate_credential(self, value): + if value and value.kind != 'registry': + raise serializers.ValidationError(_('Only Container Registry credentials can be associated with an Execution Environment')) + return value + def validate(self, attrs): # prevent changing organization of ee. Unsetting (change to null) is allowed if self.instance: diff --git a/awx/main/models/credential/__init__.py b/awx/main/models/credential/__init__.py index 77ee3fe106..bd1f608d62 100644 --- a/awx/main/models/credential/__init__.py +++ b/awx/main/models/credential/__init__.py @@ -295,6 +295,12 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin): return True return field_name in self.inputs and self.inputs[field_name] not in ('', None) + def has_inputs(self, field_names=()): + for name in field_names: + if name not in self.inputs: + return False + return True + def _get_dynamic_input(self, field_name): for input_source in self.input_sources.all(): if input_source.input_field_name == field_name: @@ -1096,11 +1102,11 @@ ManagedCredentialType( 'type': 'string', }, { - 'id': 'password/token', - 'label': ugettext_noop('Password/Token'), + 'id': 'password', + 'label': ugettext_noop('Password'), 'type': 'string', 'secret': True, - 'help_text': ugettext_noop('A token to use to authenticate with. ' 'This should not be set if username/password are being used.'), + 'help_text': ugettext_noop('A password or token used to authenticate with'), }, ], 'required': ['host'], diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 31d029daf0..012eb9387d 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -852,15 +852,21 @@ class BaseTask(object): } if instance.execution_environment.credential: - with open('/tmp/auth.json', 'w') as authfile: - host = instance.execution_environment.credential.get_input('host') - username = instance.execution_environment.credential.get_input('username') - password = instance.execution_environment.credential.get_input('password') - token = "{}:{}".format(username, password) - auth_data = {'auths': {host: {'auth': b64encode(token.encode('ascii')).decode()}}} - authfile.write(json.dumps(auth_data, indent=4)) - authfile.close() - params["container_options"].append(f'--authfile={authfile.name}') + cred = instance.execution_environment.credential + if cred.has_inputs(field_names=('host', 'username', 'password')): + path = self.build_private_data_dir(instance) + with open(path + '/auth.json', 'w') as authfile: + host = cred.get_input('host') + username = cred.get_input('username') + password = cred.get_input('password') + token = "{}:{}".format(username, password) + auth_data = {'auths': {host: {'auth': b64encode(token.encode('ascii')).decode()}}} + authfile.write(json.dumps(auth_data, indent=4)) + authfile.close() + os.chmod(authfile.name, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR) + params["container_options"].append(f'--authfile={authfile.name}') + else: + logger.exception('Please recheck that your host, username, and password fields are all filled.') pull = instance.execution_environment.pull if pull: