mirror of
https://github.com/ansible/awx.git
synced 2026-01-12 18:40:01 -03:30
AC-1060 Add API support for vault password.
This commit is contained in:
parent
8ad72426b4
commit
ac0927f430
@ -980,12 +980,13 @@ class CredentialSerializer(BaseSerializer):
|
||||
ssh_key_data = serializers.WritableField(required=False, default='')
|
||||
ssh_key_unlock = serializers.WritableField(required=False, default='')
|
||||
sudo_password = serializers.WritableField(required=False, default='')
|
||||
vault_password = serializers.WritableField(required=False, default='')
|
||||
|
||||
class Meta:
|
||||
model = Credential
|
||||
fields = ('*', 'user', 'team', 'kind', 'cloud', 'username',
|
||||
'password', 'ssh_key_data', 'ssh_key_unlock',
|
||||
'sudo_username', 'sudo_password')
|
||||
'sudo_username', 'sudo_password', 'vault_password')
|
||||
|
||||
def to_native(self, obj):
|
||||
ret = super(CredentialSerializer, self).to_native(obj)
|
||||
|
||||
@ -164,7 +164,7 @@ class Credential(CommonModelNameNotUnique):
|
||||
]
|
||||
|
||||
PASSWORD_FIELDS = ('password', 'ssh_key_data', 'ssh_key_unlock',
|
||||
'sudo_password')
|
||||
'sudo_password', 'vault_password')
|
||||
|
||||
class Meta:
|
||||
app_label = 'main'
|
||||
@ -263,10 +263,14 @@ class Credential(CommonModelNameNotUnique):
|
||||
def needs_sudo_password(self):
|
||||
return self.kind == 'ssh' and self.sudo_password == 'ASK'
|
||||
|
||||
@property
|
||||
def needs_vault_password(self):
|
||||
return self.kind == 'ssh' and self.vault_password == 'ASK'
|
||||
|
||||
@property
|
||||
def passwords_needed(self):
|
||||
needed = []
|
||||
for field in ('password', 'sudo_password', 'ssh_key_unlock'):
|
||||
for field in ('password', 'sudo_password', 'ssh_key_unlock', 'vault_password'):
|
||||
if getattr(self, 'needs_%s' % field):
|
||||
needed.append(field)
|
||||
return needed
|
||||
|
||||
@ -176,9 +176,13 @@ class ProjectOptions(models.Model):
|
||||
# show up.
|
||||
matched = False
|
||||
try:
|
||||
for line in file(playbook):
|
||||
for n, line in enumerate(file(playbook)):
|
||||
if valid_re.match(line):
|
||||
matched = True
|
||||
# Any YAML file can also be encrypted with vault;
|
||||
# allow these to be used as the main playbook.
|
||||
elif n == 0 and line.startswith('$ANSIBLE_VAULT;'):
|
||||
matched = True
|
||||
except IOError:
|
||||
continue
|
||||
if not matched:
|
||||
|
||||
@ -5,7 +5,7 @@
|
||||
import ConfigParser
|
||||
import cStringIO
|
||||
import datetime
|
||||
import distutils.version
|
||||
from distutils.version import StrictVersion as Version
|
||||
import functools
|
||||
import json
|
||||
import logging
|
||||
@ -317,6 +317,8 @@ class BaseTask(Task):
|
||||
instance = self.update_model(pk)
|
||||
status = instance.status
|
||||
raise RuntimeError('not starting %s task' % instance.status)
|
||||
# Fetch ansible version once here to support version-dependent features.
|
||||
kwargs['ansible_version'] = get_ansible_version()
|
||||
kwargs['private_data_file'] = self.build_private_data_file(instance, **kwargs)
|
||||
kwargs['passwords'] = self.build_passwords(instance, **kwargs)
|
||||
args = self.build_args(instance, **kwargs)
|
||||
@ -377,12 +379,13 @@ class RunJob(BaseTask):
|
||||
|
||||
def build_passwords(self, job, **kwargs):
|
||||
'''
|
||||
Build a dictionary of passwords for SSH private key, SSH user and sudo.
|
||||
Build a dictionary of passwords for SSH private key, SSH user, sudo
|
||||
and ansible-vault.
|
||||
'''
|
||||
passwords = super(RunJob, self).build_passwords(job, **kwargs)
|
||||
creds = job.credential
|
||||
if creds:
|
||||
for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password'):
|
||||
for field in ('ssh_key_unlock', 'ssh_password', 'sudo_password', 'vault_password'):
|
||||
if field == 'ssh_password':
|
||||
value = kwargs.get(field, decrypt_field(creds, 'password'))
|
||||
else:
|
||||
@ -413,8 +416,7 @@ class RunJob(BaseTask):
|
||||
# When using Ansible >= 1.3, allow the inventory script to include host
|
||||
# variables inline via ['_meta']['hostvars'].
|
||||
try:
|
||||
Version = distutils.version.StrictVersion
|
||||
if Version(get_ansible_version()) >= Version('1.3'):
|
||||
if Version(kwargs['ansible_version']) >= Version('1.3'):
|
||||
env['INVENTORY_HOSTVARS'] = str(True)
|
||||
except ValueError:
|
||||
pass
|
||||
@ -461,6 +463,15 @@ class RunJob(BaseTask):
|
||||
args.extend(['-U', sudo_username])
|
||||
if 'sudo_password' in kwargs.get('passwords', {}):
|
||||
args.append('--ask-sudo-pass')
|
||||
|
||||
# When using Ansible >= 1.5, support prompting for a vault password.
|
||||
try:
|
||||
if Version(kwargs['ansible_version']) >= Version('1.5'):
|
||||
if 'vault_password' in kwargs.get('passwords', {}):
|
||||
args.append('--ask-vault-pass')
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
if job.forks: # FIXME: Max limit?
|
||||
args.append('--forks=%d' % job.forks)
|
||||
if job.limit:
|
||||
@ -497,6 +508,7 @@ class RunJob(BaseTask):
|
||||
d[re.compile(r'^sudo password.*:\s*?$', re.M)] = 'sudo_password'
|
||||
d[re.compile(r'^SSH password:\s*?$', re.M)] = 'ssh_password'
|
||||
d[re.compile(r'^Password:\s*?$', re.M)] = 'ssh_password'
|
||||
d[re.compile(r'^Vault password:\s*?$', re.M)] = 'vault_password'
|
||||
return d
|
||||
|
||||
def pre_run_check(self, job, **kwargs):
|
||||
@ -591,8 +603,7 @@ class RunProjectUpdate(BaseTask):
|
||||
# the git module.
|
||||
if scm_type == 'git' and scm_url_parts.scheme == 'ssh':
|
||||
try:
|
||||
Version = distutils.version.StrictVersion
|
||||
if Version(get_ansible_version()) >= Version('1.5'):
|
||||
if Version(kwargs['ansible_version']) >= Version('1.5'):
|
||||
extra_vars['scm_accept_hostkey'] = 'true'
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
@ -27,6 +27,7 @@ from awx.main.models import *
|
||||
from awx.main.backend import LDAPSettings
|
||||
from awx.main.management.commands.run_callback_receiver import run_subscriber
|
||||
from awx.main.management.commands.run_task_system import run_taskmanager
|
||||
from awx.main.utils import get_ansible_version
|
||||
|
||||
|
||||
class BaseTestMixin(object):
|
||||
@ -40,6 +41,8 @@ class BaseTestMixin(object):
|
||||
self._temp_project_dirs = []
|
||||
self._current_auth = None
|
||||
self._user_passwords = {}
|
||||
self.ansible_version = get_ansible_version()
|
||||
self.assertNotEqual(self.ansible_version, 'unknown')
|
||||
# Wrap settings so we can redefine them within each test.
|
||||
self._wrapped = settings._wrapped
|
||||
settings._wrapped = UserSettingsHolder(settings._wrapped)
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
# All Rights Reserved.
|
||||
|
||||
# Python
|
||||
from distutils.version import StrictVersion as Version
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
@ -87,6 +88,19 @@ TEST_ASYNC_NOWAIT_PLAYBOOK = '''
|
||||
poll: 0
|
||||
'''
|
||||
|
||||
TEST_VAULT_PLAYBOOK = '''$ANSIBLE_VAULT;1.1;AES256
|
||||
35623233333035633365383330323835353564346534363762366465316263363463396162656432
|
||||
6562643539396330616265616532656466353639303338650a313466333663646431646663333739
|
||||
32623935316439343636633462373633653039646336376361386439386661366434333830383634
|
||||
6266613530626633390a363532373562353262323863343830343865303663306335643430396239
|
||||
63393963623537326366663332656132653465646332343234656237316537643135313932623237
|
||||
66313863396463343232383131633531363239396636363165646562396261626633326561313837
|
||||
32383634326230656230386237333561373630343233353239613463626538356338326633386434
|
||||
36396639313030336165366266646431306665336662663732313762663938666239663233393964
|
||||
30393733393331383132306463656636396566373961383865643562383564356363'''
|
||||
|
||||
TEST_VAULT_PASSWORD = '1234'
|
||||
|
||||
TEST_SSH_KEY_DATA = '''-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIEpQIBAAKCAQEAyQ8F5bbgjHvk4SZJsKI9OmJKMFxZqRhvx4LaqjLTKbBwRBsY
|
||||
1/C00NPiZn70dKbeyV7RNVZxuzM6yd3D3lwTdbDu/eJ0x72t3ch+TdLt/aenyy10
|
||||
@ -198,6 +212,7 @@ class RunJobTest(BaseCeleryTest):
|
||||
'password': '',
|
||||
'sudo_username': '',
|
||||
'sudo_password': '',
|
||||
'vault_password': '',
|
||||
}
|
||||
opts.update(kwargs)
|
||||
self.credential = Credential.objects.create(**opts)
|
||||
@ -845,6 +860,56 @@ class RunJobTest(BaseCeleryTest):
|
||||
self.assertTrue('ssh-agent' in job.job_args)
|
||||
self.assertTrue('Bad passphrase' not in job.result_stdout)
|
||||
|
||||
def test_vault_password(self):
|
||||
self.create_test_credential(vault_password=TEST_VAULT_PASSWORD)
|
||||
self.create_test_project(TEST_VAULT_PLAYBOOK)
|
||||
job_template = self.create_test_job_template()
|
||||
job = self.create_test_job(job_template=job_template)
|
||||
self.assertEqual(job.status, 'new')
|
||||
self.assertFalse(job.passwords_needed_to_start)
|
||||
self.assertTrue(job.signal_start())
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
if Version(self.ansible_version) >= Version('1.5'):
|
||||
self.check_job_result(job, 'successful')
|
||||
self.assertTrue('--ask-vault-pass' in job.job_args)
|
||||
else:
|
||||
self.check_job_result(job, 'failed')
|
||||
self.assertFalse('--ask-vault-pass' in job.job_args)
|
||||
|
||||
def test_vault_ask_password(self):
|
||||
self.create_test_credential(vault_password='ASK')
|
||||
self.create_test_project(TEST_VAULT_PLAYBOOK)
|
||||
job_template = self.create_test_job_template()
|
||||
job = self.create_test_job(job_template=job_template)
|
||||
self.assertEqual(job.status, 'new')
|
||||
self.assertTrue(job.passwords_needed_to_start)
|
||||
self.assertTrue('vault_password' in job.passwords_needed_to_start)
|
||||
self.assertFalse(job.signal_start())
|
||||
self.assertEqual(job.status, 'new')
|
||||
self.assertTrue(job.signal_start(vault_password=TEST_VAULT_PASSWORD))
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
if Version(self.ansible_version) >= Version('1.5'):
|
||||
self.check_job_result(job, 'successful')
|
||||
self.assertTrue('--ask-vault-pass' in job.job_args)
|
||||
else:
|
||||
self.check_job_result(job, 'failed')
|
||||
self.assertFalse('--ask-vault-pass' in job.job_args)
|
||||
|
||||
def test_vault_bad_password(self):
|
||||
self.create_test_credential(vault_password='not it')
|
||||
self.create_test_project(TEST_VAULT_PLAYBOOK)
|
||||
job_template = self.create_test_job_template()
|
||||
job = self.create_test_job(job_template=job_template)
|
||||
self.assertEqual(job.status, 'new')
|
||||
self.assertFalse(job.passwords_needed_to_start)
|
||||
self.assertTrue(job.signal_start())
|
||||
job = Job.objects.get(pk=job.pk)
|
||||
self.check_job_result(job, 'failed')
|
||||
if Version(self.ansible_version) >= Version('1.5'):
|
||||
self.assertTrue('--ask-vault-pass' in job.job_args)
|
||||
else:
|
||||
self.assertFalse('--ask-vault-pass' in job.job_args)
|
||||
|
||||
def _test_cloud_credential_environment_variables(self, kind):
|
||||
if kind == 'aws':
|
||||
env_var1 = 'AWS_ACCESS_KEY'
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user