diff --git a/lib/ansible/cli/__init__.py b/lib/ansible/cli/__init__.py index cadeb986aa..4e84261c30 100644 --- a/lib/ansible/cli/__init__.py +++ b/lib/ansible/cli/__init__.py @@ -214,6 +214,9 @@ class CLI(with_metaclass(ABCMeta, object)): # certain vault password prompt format, so 'promp_ask_vault_pass' vault_id gets the old format. prompt_formats = {} + # If there are configured default vault identities, they are considered 'first' + # so we prepend them to vault_ids (from cli) here + vault_password_files = vault_password_files or [] if C.DEFAULT_VAULT_PASSWORD_FILE: vault_password_files.append(C.DEFAULT_VAULT_PASSWORD_FILE) diff --git a/lib/ansible/cli/console.py b/lib/ansible/cli/console.py index cb15d7970b..94d8de583e 100644 --- a/lib/ansible/cli/console.py +++ b/lib/ansible/cli/console.py @@ -415,8 +415,11 @@ class ConsoleCLI(CLI, cmd.Cmd): self.loader, self.inventory, self.variable_manager = self._play_prereqs(self.options) + default_vault_ids = C.DEFAULT_VAULT_IDENTITY_LIST + vault_ids = self.options.vault_ids + vault_ids = default_vault_ids + vault_ids vault_secrets = self.setup_vault_secrets(self.loader, - vault_id=self.options.vault_ids, + vault_ids=vault_ids, vault_password_files=self.options.vault_password_files, ask_vault_pass=self.options.ask_vault_pass) self.loader.set_vault_secrets(vault_secrets) diff --git a/lib/ansible/cli/vault.py b/lib/ansible/cli/vault.py index 3ffad1a8a1..7487235537 100644 --- a/lib/ansible/cli/vault.py +++ b/lib/ansible/cli/vault.py @@ -23,6 +23,7 @@ import os import sys from ansible.cli import CLI +from ansible import constants as C from ansible.errors import AnsibleOptionsError from ansible.module_utils._text import to_text, to_bytes from ansible.parsing.dataloader import DataLoader @@ -156,6 +157,9 @@ class VaultCLI(CLI): # ask for a new password and confirm it, and 'read/write (rekey) that asks for the # old password, then asks for a new one and confirms it. + default_vault_ids = C.DEFAULT_VAULT_IDENTITY_LIST + vault_ids = default_vault_ids + vault_ids + # TODO: instead of prompting for these before, we could let VaultEditor # call a callback when it needs it. if self.action in ['decrypt', 'view', 'rekey']: @@ -163,7 +167,6 @@ class VaultCLI(CLI): vault_ids=vault_ids, vault_password_files=self.options.vault_password_files, ask_vault_pass=self.options.ask_vault_pass) - if not vault_secrets: raise AnsibleOptionsError("A vault password is required to use Ansible's Vault") @@ -178,7 +181,6 @@ class VaultCLI(CLI): vault_password_files=self.options.vault_password_files, ask_vault_pass=self.options.ask_vault_pass, create_new_password=True) - if not vault_secrets: raise AnsibleOptionsError("A vault password is required to use Ansible's Vault") diff --git a/lib/ansible/config/data/config.yml b/lib/ansible/config/data/config.yml index 8c70d7b037..82f9c478a0 100644 --- a/lib/ansible/config/data/config.yml +++ b/lib/ansible/config/data/config.yml @@ -1088,6 +1088,15 @@ DEFAULT_VAULT_IDENTITY: - {key: vault_identity, section: defaults} vars: [] yaml: {key: defaults.vault_identity} +DEFAULT_VAULT_IDENTITY_LIST: + default: [] + desc: 'A list of vault-ids to use by default. Equivalent to multiple --vault-id args. Vault-ids are tried in order.' + env: [{name: ANSIBLE_VAULT_IDENTITY_LIST}] + ini: + - {key: vault_identity_list, section: defaults} + value_type: list + vars: [] + yaml: {key: defaults.vault_identity_list} DEFAULT_VAULT_PASSWORD_FILE: default: desc: 'TODO: write it' diff --git a/test/integration/targets/vault/runme.sh b/test/integration/targets/vault/runme.sh index 9b81c58ea8..24a98936e8 100755 --- a/test/integration/targets/vault/runme.sh +++ b/test/integration/targets/vault/runme.sh @@ -98,6 +98,15 @@ WRONG_RC=$? echo "rc was $WRONG_RC (1 is expected)" [ $WRONG_RC -eq 1 ] +# test with a default vault-id list set via config/env, right password +ANSIBLE_VAULT_PASSWORD_FILE=wrong@vault-password-wrong,correct@vault-password ansible-vault view "$@" format_1_1_AES.yml && : + +# test with a default vault-id list set via config/env,wrong passwords +ANSIBLE_VAULT_PASSWORD_FILE=wrong@vault-password-wrong,alsowrong@vault-password-wrong ansible-vault view "$@" format_1_1_AES.yml && : +WRONG_RC=$? +echo "rc was $WRONG_RC (1 is expected)" +[ $WRONG_RC -eq 1 ] + # encrypt it ansible-vault encrypt "$@" --vault-password-file vault-password "${TEST_FILE}" diff --git a/test/units/cli/test_cli.py b/test/units/cli/test_cli.py index 706eea08b0..db2fae0dfb 100644 --- a/test/units/cli/test_cli.py +++ b/test/units/cli/test_cli.py @@ -121,7 +121,7 @@ class TestCliSetupVaultSecrets(unittest.TestCase): vault_id_names = ['prompt1', 'prompt2', 'prompt3', 'default'] self._assert_ids(vault_id_names, res) - @patch('ansible.cli.C', name='MockConfig') + @patch('ansible.cli.C') @patch('ansible.cli.get_file_vault_secret') @patch('ansible.cli.PromptVaultSecret') def test_default_file_vault(self, mock_prompt_secret, @@ -146,6 +146,35 @@ class TestCliSetupVaultSecrets(unittest.TestCase): self.assertEqual(matches[0][1].bytes, b'file1_password') self.assertEqual(matches[1][1].bytes, b'prompt1_password') + @patch('ansible.cli.get_file_vault_secret') + @patch('ansible.cli.PromptVaultSecret') + def test_default_file_vault_identity_list(self, mock_prompt_secret, + mock_file_secret): + default_vault_ids = ['some_prompt@prompt', + 'some_file@/dev/null/secret'] + + mock_prompt_secret.return_value = MagicMock(bytes=b'some_prompt_password', + vault_id='some_prompt') + + filename = '/dev/null/secret' + mock_file_secret.return_value = MagicMock(bytes=b'some_file_password', + vault_id='some_file', + filename=filename) + + vault_ids = default_vault_ids + res = cli.CLI.setup_vault_secrets(loader=self.fake_loader, + vault_ids=vault_ids, + create_new_password=False, + ask_vault_pass=True) + + self.assertIsInstance(res, list) + matches = vault.match_secrets(res, ['some_file']) + # --vault-password-file/DEFAULT_VAULT_PASSWORD_FILE is higher precendce than prompts + # if the same vault-id ('default') regardless of cli order since it didn't matter in 2.3 + self.assertEqual(matches[0][1].bytes, b'some_file_password') + matches = vault.match_secrets(res, ['some_prompt']) + self.assertEqual(matches[0][1].bytes, b'some_prompt_password') + @patch('ansible.cli.PromptVaultSecret') def test_prompt_just_ask_vault_pass(self, mock_prompt_secret): mock_prompt_secret.return_value = MagicMock(bytes=b'prompt1_password',