Ansible may need access to sensitive data such as passwords or API keys in order to configure remote servers. Normally, this information might be stored as plain text in inventory variables or other Ansible files. But in that case, any user with access to the Ansible files or a version control system which stores the Ansible files would have access to this sensitive data. This poses an
obvious security risk.
The ansible-vault
command allows file encryption, and requires a password to unencrypt.
Command:
1 |
$ ansible-vault encrypt <file> |
The ansible-vault rekey
command will allow you to re-encrypt a file and reset the password.
To supply the vault password during play execution, you must use either of the --ask-vault-password
or --ask-vault-file
flags.
Since Ansible 2.4 the --vault-id
can be used to indicate which vault ID (‘dev’, ‘prod’, ‘cloud’, etc) a password is for as well as how to source the password (prompt, a file path, etc). To use vault IDs, you must provide an ID label of your choosing and a source to obtain its password (either prompt
or a file path):
1 |
--vault-id label@source |
It is also possible to set no_log
within a module to censor sensitive log output.
The Ansible-Vault Command
Let’s create a text file:
1 2 3 |
[miro@controlnode vault]$ echo "Hello world" > secret.txt [miro@controlnode vault]$ cat secret.txt Hello world |
Now we can ecrypt the file:
1 2 3 4 5 6 7 8 9 10 11 |
[miro@controlnode vault]$ ansible-vault encrypt secret.txt New Vault password: Confirm New Vault password: Encryption successful [miro@controlnode vault]$ cat secret.txt $ANSIBLE_VAULT;1.1;AES256 39333236373633343338333333313933336538383062626339656263653038353862393366373662 6634323132323335333536393736643365633562366162380a386534376463626535346431306535 37306339323964343265643339393162626437396130313463646136363532303163343661323931 3937363931343463610a623630313037333930356263396666363264363132633962323433656230 6239 |
After the file has been encrypted we can also edit it:
1 2 3 4 5 |
[miro@controlnode vault]$ ansible-vault edit secret.txt Vault password: Hello world edited ~ |
And we can view the file after it was changed:
1 2 3 |
[miro@controlnode vault]$ ansible-vault view secret.txt Vault password: Hello world edited |
And we can also decrypt the file:
1 2 3 4 5 |
[miro@controlnode vault]$ ansible-vault decrypt secret.txt Vault password: Decryption successful [miro@controlnode vault]$ cat secret.txt Hello world edited |
We can also encypt the string:
1 2 3 4 5 6 7 8 9 10 11 |
[miro@controlnode vault]$ ansible-vault encrypt_string "Some string" -n meaning New Vault password: Confirm New Vault password: meaning: !vault | $ANSIBLE_VAULT;1.1;AES256 65666136326337656261316630326164333139633135316639366131363335383133326162353961 6534623362646631313961326666613832626635616133660a393062613365646435363762323539 33626136626633323536613736666234633633363662633661336538623866636231356266336665 6334633733356434640a376536633035356534353565313737666365393761353834306637346362 3661 Encryption successful |
The string which we encrypt can be labeled by --vault-id
:
1 2 3 4 5 6 7 8 9 10 11 |
[miro@controlnode vault]$ ansible-vault encrypt_string "Some string" -n meaning --vault-id dev@prompt New vault password (dev): Confirm vew vault password (dev): meaning: !vault | $ANSIBLE_VAULT;1.2;AES256;dev 62353339333430353163643064366237316532633462303863623461356239666535663133323239 3338633135323465313163373865383865396432613037660a626236386331356566366532333362 31333637376139313236323466393338373064313062303833386338613932353465356437366535 3038333936623433320a323565336361363138346364313062386433633965343461663530346339 3163 Encryption successful |
To create a new encrypted data file, run the following command:
1 2 3 |
[miro@controlnode ansible]$ ansible-vault create foo.yml New Vault password: Confirm New Vault password: |
To create a new encrypted data file with the Vault ID ‘password1’ assigned to it and be prompted for the password, run:
1 2 |
[miro@controlnode ansible]$ ansible-vault create --vault-id password1@prompt foo.yml New vault password (password1): |
To edit a file encrypted with the ‘vault2’ password file and assigned the ‘pass2’ vault ID:
1 |
[miro@controlnode ansible]$ ansible-vault edit --vault-id pass2@vault2 foo.yml |
Should you wish to change your password on a vault-encrypted file or files, you can do so with the rekey command:
1 |
$ ansible-vault rekey foo.yml bar.yml baz.yml |
To rekey files encrypted with the ‘preprod2’ vault ID and the ‘ppold’ file and be prompted for the new password:
1 |
$ ansible-vault rekey --vault-id preprod2@ppold --new-vault-id preprod2@prompt foo.yml bar.yml baz.yml |
If you have existing files that you wish to encrypt, use the ansible-vault encrypt command. This command can operate on multiple files at once:
1 |
$ ansible-vault encrypt foo.yml bar.yml baz.yml |
To encrypt existing files with the ‘project’ ID and be prompted for the password:
1 |
$ ansible-vault encrypt --vault-id project@prompt foo.yml bar.yml baz.yml |
Using Vaults in Playbooks
Let’s create playbook with vars located in the encrypted file:
1 2 3 4 5 6 7 8 |
[miro@controlnode vault]$ cat vault.yml --- - hosts: localhost vars_files: - /home/miro/ansible/vault/secure_var tasks: - name: Output message shell: echo {{ message}} > /home/user/vault/deployed.txt |
File with vars:
1 2 |
[miro@controlnode vault]$ cat secure_var message: "Hello world" |
File with password:
1 2 |
[miro@controlnode vault]$ cat vault example_of_password |
The file with password (vault
) should be stored securely because if somebody gets this file, he can decrypt secure_var
file.
Now we can encrypt file with vars (secure_var
) using the label prod
in the “vault” file:
1 2 3 4 5 6 7 8 9 10 |
[miro@controlnode vault]$ ansible-vault encrypt --vault-id prod@vault secure_var Encryption successful [miro@controlnode vault]$ cat secure_var $ANSIBLE_VAULT;1.2;AES256;prod 61386632306362663233343663383435666334303530613331396337613962383035376132656530 3835353062303932373536636633343166373666663238390a396531343930366439386663393563 61306235343664646233623739393065373034663837633065663666643164636262353732393339 6439363665393561360a393863666565366635306661643135613138653234386663306236313539 30303334633331303230613237626336396138313636653935316164356362633236 |
Lets’s run the playbook now:
1 2 |
[miro@controlnode vault]$ ansible-playbook vault.yml ERROR! Attempting to decrypt but no vault secrets found |
We must specify the vault password file with --vault-id
file:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
[miro@controlnode vault]$ ansible-playbook vault.yml --vault-id prod@vault PLAY [localhost] ***************************************************************************************************************** TASK [Gathering Facts] *********************************************************************************************************** ok: [localhost] TASK [Output message] ************************************************************************************************************ changed: [localhost] PLAY RECAP *********************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 [miro@controlnode vault]$ cat deployed.txt Hello world |
And the playbook runs without problems.
Now let’s run the same playbook with verbosity flag:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode vault]$ ansible-playbook -v vault.yml --vault-id prod@vault Using /etc/ansible/ansible.cfg as config file PLAY [localhost] ************************************************************************************************************************************************************************ TASK [Gathering Facts] ****************************************************************************************************************************************************************** ok: [localhost] TASK [Output message] ******************************************************************************************************************************************************************* changed: [localhost] => {"changed": true, "cmd": "echo <strong>Hello world</strong> > /home/miro/ansible/vault/deployed.txt", "delta": "0:00:00.003909", "end": "2020-04-18 15:18:57.860573", "rc": 0, "start": "2020-04-18 15:18:57.856664", "stderr": "", "stderr_lines": [], "stdout": "", "stdout_lines": []} PLAY RECAP ****************************************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 |
And we see that even we encrypt the secure_var file the meesage var can be seen.
Let’s modify the playbook no to log the output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[miro@controlnode vault]$ cat vault.yml --- - hosts: localhost vars_files: - /home/miro/ansible/vault/secure_var tasks: - name: Output message shell: echo {{ message}} > /home/miro/ansible/vault/deployed.txt no_log: true [miro@controlnode vault]$ [miro@controlnode vault]$ [miro@controlnode vault]$ ansible-playbook -v vault.yml --vault-id prod@vault Using /etc/ansible/ansible.cfg as config file PLAY [localhost] ************************************************************************************************************************************************************************ TASK [Gathering Facts] ****************************************************************************************************************************************************************** ok: [localhost] TASK [Output message] ******************************************************************************************************************************************************************* ok: [localhost] => {"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result"} PLAY RECAP ****************************************************************************************************************************************************************************** localhost : ok=2 changed=1 unreachable=0 failed=0 |
And the output has ben hidden.
Example 1.
In this exercise, you will create a new encrypted file, edit the file, and change the password on an existing encrypted file. You would also learn how to encrypt and decrypt an existing file.
Create an encrypted file named super-secret.yml under ~/ansible/vault.
Enter redhat as the vault password when prompted, and confirm.
1 2 3 |
[miro@controlnode vault]$ ansible-vault create super-secret.yml New Vault password: Confirm New Vault password: |
Enter the following content into the file. Save and exit the file when you are finished.
1 |
This is encrypted. |
Attempt to view the content of the encrypted file super-secret.yml.
1 2 3 4 5 6 7 |
[miro@controlnode vault]$ cat super-secret.yml $ANSIBLE_VAULT;1.1;AES256 30623932663266303862663737666561386666383065623665346361373333623131393661626262 6362663464346266633162376130363737303632633232620a346265303061663764383834346465 33303166333561373637376566303739653631386636613334303162303864353031373337653833 6239666463383333390a323330646363343566353735343364626239326365323862643837353863 61333830353566643235613366613165626365366236343539616431366661373139 |
As the file super-secret.yml is an encrypted file, you cannot view the content in plain text. The default cipher used is AES (which is shared-secret based).
To view the content of the Ansible Vault encrypted file, use the command ansible-vault view super-secret.yml. When prompted, enter the vault password as redhat.
1 2 3 |
[miro@controlnode vault]$ ansible-vault view super-secret.yml Vault password: This is encrypted. |
Now edit the encrypted file super-secret.yml to add some new content. Use redhat as the vault password.
1 2 |
[miro@controlnode vault]$ ansible-vault edit super-secret.yml Vault password: |
Enter the following content into the file. Save and exit the file when you are finished.
1 |
This is also encrypted. |
Verify by viewing the content of super-secret.yml, using ansible-vault view
super-secret.yml. Use the vault password as redhat.
1 2 3 4 |
[miro@controlnode vault]$ ansible-vault view super-secret.yml Vault password: This is encrypted. This is also encypted. |
Create file with password:
1 |
[miro@controlnode vault]$ echo 'user_pw: 5pjsBJxAWUs6pRWD5itO/' > passwd |
Encrypt file password
1 2 3 4 |
[miro@controlnode vault]$ ansible-vault encrypt passwd New Vault password: Confirm New Vault password: Encryption successful |
Change the vault password of the passwd
1 2 3 4 5 |
[miro@controlnode vault]$ ansible-vault rekey passwd Vault password: New Vault password: Confirm New Vault password: Rekey successful |
Decrypt the encrypted file passwd.yml and save the file as passwd-decrypted
1 2 3 4 5 |
[miro@controlnode vault]$ ansible-vault decrypt passwd --output=passwd-decrypted Vault password: Decryption successful [miro@controlnode vault]$ cat passwd-decrypted user_pw: 5pjsBJxAWUs6pRWD5itO/ |
Encrypt the existing file passwd-decrypted.yml and save the file as passwd-encrypted
1 2 3 4 5 6 7 8 9 10 11 |
[miro@controlnode vault]$ ansible-vault encrypt passwd-decrypted --output=passwd-encrypted New Vault password: Confirm New Vault password: Encryption successful [miro@controlnode vault]$ cat passwd-encrypted $ANSIBLE_VAULT;1.1;AES256 31616630343665666663393336316637333837616561323033393063326361333434623839646635 6132613232306462633833636235363966633562393733640a383266393463613265323662363430 35343330346430653066643732623031623863353131366336376465626465643330356230346335 6562383561353366360a363833313963653362316137396566646534396135306164393863346664 63656632623339396164313531396665323265303963343632333766353166333462 |
Example 2.
Create an encrypted file named secret.yml in ~/ansible/vault/vars/ which
will contain sensitive playbook variables. Provide a password of redhat for the vault and confirm it.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode vault]$ mkdir vars [miro@controlnode vault]$ ansible-vault create vars/secret.yml New Vault password: Confirm New Vault password: [miro@controlnode vault]$ ansible-vault view vars/secret.yml Vault password: newusers: - name: demouser1 pw: redhat - name: demouser2 pw: RedHat |
Create the create_users.yml playbook. Note how it references vars/
secret.yml as an external playbook variables file.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode vault]$ cat create_users.yml --- - name: create user accounts for all our servers hosts: managedhost1 become: yes vars_files: - vars/secret.yml tasks: - name: Creating users from secret.yml user: name: "{{ item.name }}" password: "{{ item.pw | password_hash('sha512') }}" with_items: "{{ newusers }}" |
Use ansible-playbook –syntax-check to check the syntax of the
create_users.yml playbook,
1 2 |
[miro@controlnode vault]$ ansible-playbook --syntax-check create_users.yml ERROR! Attempting to decrypt but no vault secrets found |
It failed because it was unable to decrypt vars/secret.yml to check its syntax. Add the –ask-vault-pass option to prompt for the vault password while decrypting vars/secret.yml.
1 2 3 4 |
[miro@controlnode vault]$ ansible-playbook --syntax-check --ask-vault-pass create_users.yml Vault password: playbook: create_users.yml |
Create a password file, called vault-pass, to use for the playbook execution instead of asking for a password. Store the vault password redhat as plain text. Change the permission of the file to 0600.
1 2 |
[miro@controlnode vault]$ echo 'redhat' > vault-pass [miro@controlnode vault]$ chmod 0600 vault-pass |
Execute the Ansible playbook, this time using the vault password file. This creates the demouser1 and demouser2 users on the managed hosts using the passwords stored as the pw fields in secret.yml.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode vault]$ ansible-playbook --vault-password-file=vault-pass create_users.yml PLAY [create user accounts for all our servers] ****************************************************************************** TASK [Gathering Facts] ******************************************************************************************************* ok: [managedhost1] TASK [Creating users from secret.yml] **************************************************************************************** changed: [managedhost1] => (item={u'name': u'demouser1', u'pw': u'redhat'}) changed: [managedhost1] => (item={u'name': u'demouser2', u'pw': u'RedHat'}) PLAY RECAP ******************************************************************************************************************* managedhost1 : ok=2 changed=1 unreachable=0 failed=0 |
Verify that both users (demouser1 and demouser2) were created properly by the playbook. Connect to managedhost1 via SSH as those users.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
[miro@controlnode vault]$ ssh demouser1@managedhost1 demouser1@managedhost1's password: This is the system managedhost1. Today's date is: 2020-05-06. Only use this system with permission. You can ask someone@host.example.com for access. [demouser1@managedhost1 ~]$ logout Connection to managedhost1 closed. [miro@controlnode vault]$ ssh demouser2@managedhost1 demouser2@managedhost1's password: Permission denied, please try again. demouser2@managedhost1's password: Last failed login: Mon May 11 18:11:09 CEST 2020 from controlnode.example.com on ssh:notty There was 1 failed login attempt since the last successful login. This is the system managedhost1. Today's date is: 2020-05-06. Only use this system with permission. You can ask someone@host.example.com for access. [demouser2@managedhost1 ~]$ logout Connection to managedhost1 closed. |
Example 3.
In this exercise, you will use Ansible Vault to encypt the file containing passwords on the local system and use that in a playbook to create users on the managedhost1 remote system.
Create an encrypted file named secret2.yml in ~/ansible/vault/. Provide
a password of redhat for the vault and confirm it. This will open a file in the default editor vim.
1 2 3 4 5 6 7 8 9 10 11 |
[miro@controlnode vault]$ ansible-vault create vars/secret2.yml New Vault password: Confirm New Vault password: [miro@controlnode vault]$ ansible-vault view vars/secret2.yml Vault password: newusers: - name: ansibleuser1 pw: redhat - name: ansibleuser2 pw: Re4H1T |
Create a playbook which will use the variables defined in the secret2.yml encrypted file. Name the playbook create_users2.yml and create it under the ~/ansible/vault/ directory. Run this playbook as the miro user on the remote
managed host. Configure the playbook to create the ansibleuser1 and ansibleuser2 users.
The password stored as plain text in the variable, pw, should be converted into password hash using hashing filters password_hash to get SHA512 hashed password and passed as an argument to the user module. For example,
1 2 3 |
user: name: user1 password: "{{ 'example_of_password' | password_hash('sha512') }}" |
The content of the create_users.yml should be:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[miro@controlnode vault]$ cat create_users2.yml --- - name: create user accounts for all our servers hosts: managedhost1 become: yes remote_user: miro vars_files: - vars/secret2.yml tasks: - name: Creating users from secret.yml user: name: "{{ item.name }}" password: "{{ item.pw | password_hash('sha512') }}" with_items: "{{ newusers }}" |
Check the syntax of the create_users.yml using ansible-playbook –syntaxcheck. Use the –ask-vault-pass option to prompt for the vault password set on secret.yml. In case of syntax error, resolve before continuing further.
1 2 3 4 |
[miro@controlnode vault]$ ansible-playbook --syntax-check --ask-vault-pass create_users2.yml Vault password: playbook: create_users2.yml |
Create a password file to use for the playbook execution instead of asking for a password. The file should be called vault-pass and it should store the redhat vault password as a plain text. Change the permission of the file to 0600.
1 2 |
[miro@controlnode vault]$ echo 'redhat' > vault-pass2 [miro@controlnode vault]$ chmod 0600 vault-pass2 |
Execute the Ansible playbook, using the vault password file to create the ansibleuser1 and ansibleuser2 users on a remote system using the passwords stored as variables in the secret.yml Ansible Vault encrypted file. Use the vault password file vault-pass.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[miro@controlnode vault]$ ansible-playbook --vault-password-file=vault-pass2 create_users2.yml PLAY [create user accounts for all our servers] ****************************************************************************** TASK [Gathering Facts] ******************************************************************************************************* ok: [managedhost1] TASK [Creating users from secret.yml] **************************************************************************************** changed: [managedhost1] => (item={u'name': u'ansibleuser1', u'pw': u'redhat'}) changed: [managedhost1] => (item={u'name': u'ansibleuser2', u'pw': u'Re4H1T'}) PLAY RECAP ******************************************************************************************************************* managedhost1 : ok=2 changed=1 unreachable=0 failed=0 |
Verify that both users were created properly by the playbook by connecting via SSH to managedhost2.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
[miro@controlnode vault]$ ssh ansibleuser1@managedhost1 ansibleuser1@managedhost1's password: This is the system managedhost1. Today's date is: 2020-05-06. Only use this system with permission. You can ask someone@host.example.com for access. [ansibleuser1@managedhost1 ~]$ logout Connection to managedhost1 closed. [miro@controlnode vault]$ ssh ansibleuser2@managedhost1 ansibleuser2@managedhost1's password: This is the system managedhost1. Today's date is: 2020-05-06. Only use this system with permission. You can ask someone@host.example.com for access. [ansibleuser2@managedhost1 ~]$ logout Connection to managedhost1 closed. |
Example 4.
In this lab, you will encrypt and decrypt the YAML file containing variables for LUKS encryption which are sensitive. Use the encrypted file containing variables in a playbook to execute remote tasks on serverb.lab.example.com to create a LUKS encrypted partition on /dev/vdb. Edit the encrypted role variable file to add the path of the new 256-bit key file and add tasks to insert this key to an available key slot on the encrypted device /dev/vdb on
managedhostx.
Use the ansible-galaxy command to create a role named encryptdisk and its
directory structure.
1 2 3 |
[miro@controlnode ansible]$ cd roles [miro@controlnode roles]$ ansible-galaxy init --offline encryptdisk - encryptdisk was created successfully |
Edit the encryptdisk role variable file, ~/ansible/roles/encryptdisk/
vars/main.yml, to add the following variables:
1 2 3 4 5 6 |
[miro@controlnode roles]$ cat encryptdisk/vars/main.yml --- # vars file for encryptdisk luks_dev: /dev/vdb luks_name: crypto luks_pass: Re4H1TAns1BLe |
Encrypt the role variable file. Use redhat as the the vault password.
Use ansible-vault to encrypt the roles/encryptdisk/vars/main.yml role variable
file.
1 2 3 4 5 6 7 8 9 10 11 12 |
[miro@controlnode roles]$ ansible-vault encrypt encryptdisk/vars/main.yml New Vault password: Confirm New Vault password: Encryption successful [miro@controlnode roles]$ ansible-vault view encryptdisk/vars/main.yml Vault password: --- # vars file for encryptdisk luks_dev: /dev/vdb luks_name: crypto luks_pass: Re4H1TAns1BLe |
Create task to encrypt a block device as specified using the luks_dev variable.
1 |
TASK |
Example not finished