I'm currently using an Ansible playbook to extract and then transfer a configuration backup from some network devices (a basic text file) to an external storage.
I'd like to encrypt the configuration backups before sending them to their final storage. What would be the most adequate way to encrypt a file from inside an Ansible playbook task? To me, the obvious way would be to use the shell module to either call an external encryption tool (openssl) or an ansible-vault command to encrypt the backup in a format that ansible itself can read later in some other context; i.e. one of the two tasks below (simplified):
- name: Encrypt stuff with OpenSSL using a password read from vault file
shell:
cmd: openssl {{ openssl_parameters }} -k {{ vaulted_encryption_password }} -in {{ file_to_encrypt }} -out {{ encrypted_file }}
- name: Encrypt stuff with Ansible-Vault
shell:
cmd: ansible-vault encrypt {{ file_to_encrypt }} --vault-password-file {{ vault_password_file }}
However, none of these solutions seem completely secure, given they require passing the encryption password to an external tool via a shell (which can expose the password to anyone monitoring the processes on the host this runs in, for example) or require writing the plain-text password on a file for ansible-vault to use.
Is there a better way of doing file encryption inside an Ansible task that I'm missing here? (a dedicated module, or some other solution?).
Updated answer valid since ansible 2.12
The original answer below was one solution until the availability of ansible-core v2.12. Since then, there is a new ansible.builtin.vault
filter which make this much easier.
Here is a complete test (which needs to be hardened for complete security of course...)
First, we create a secret.txt
file we later want to encrypt:
echo "I'm a file that needs to be encrypted" > secret.txt
Then the playbook encrypt.yml
:
---
- hosts: localhost
gather_facts: false
vars_prompt:
name: vault_secret
prompt: Please enter the password to encrypt the file
default: v3rys3cr3t
private: true
vars:
vault_file: secret.txt
tasks:
- name: In-place (re)encrypt file {{ vault_file }}
copy:
content: "{{ lookup('ansible.builtin.file', vault_file) | ansible.builtin.vault(vault_secret) }}"
dest: "{{ vault_file }}"
decrypt: false
Gives:
$ ansible-playbook encrypt.yml
Please enter the password to encrypt the file [v3rys3cr3t]:
PLAY [localhost] ***********************************************************************************************************************************************************************************************************************
TASK [In-place (re)encrypt file secret.txt] ********************************************************************************************************************************************************************************************
changed: [localhost]
PLAY RECAP *****************************************************************************************************************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
And we can now check the file was effectively encrypted and still contains the original data
$ ansible-vault view --ask-vault-pass secret.txt
Vault password:
I'm a file that needs to be encrypted
(END)
Note that the above playbook is not idempotent. If you replay the tasks again:
--ask-vault-pass
or any other supported mechanism) so that the file
lookup can read the content.Previous answer kept for history and still valid for ansible < 2.12
There are no modules I know to use ansible-vault
from playbooks directly (besides the obvious intended use which is to decrypt variables and file contents on the fly).
One possible way to improve security (as far as listing processes is concerned) with your ansible-vault
example through a command would be to use the interactive prompt mode and fill the password with the expect
module. An other security layer can be added by adding the no_log: true
parameter to the task so it does not print content of the variables.
Here is a simple example (you will need to pip install pexpect
on the target host):
---
- hosts: localhost
gather_facts: false
tasks:
- name: Vault encrypt a given file
vars:
vault_pass: v3rys3cur3
expect:
command: ansible-vault encrypt --ask-vault-pass toto.txt
responses:
New Vault password: "{{ vault_pass }}"
Confirm New Vault password: "{{ vault_pass }}"
Which gives (using the verbose mode to illustrate the no_log
feature and provided the given file exist and is not yet encrypted...):
$ ansible-playbook -v test.yml
No config file found; using defaults
PLAY [localhost] **************************************************************************************************************************************************************************************************
TASK [Vault encrypt a given file] *********************************************************************************************************************************************************************************
changed: [localhost] => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true}
PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost : ok=1 changed=1 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0