Ansible - dynamically prompt vault password when needed - ansible

Context
In my company, we have a shared repository containing our ansible script for our servers. I would like to introduce vault variables to handle services password in the near future. For now, we use encrypted password prompts at the beginning of our playbooks. This solution is annoying (it asked for 3 passwords on some playbooks).
Need
As most of our ansible users are no experts, I would like them to be able to run playbooks smoothly. It means ansible-playbook commands should be short and work without any mandatory parameters (e.g. no --ask-sudo-pass and whatnot). It also means I prefer prompting for vault password at the beginning of a playbook only when it’s needed.
Moreover, I do not want to use ansible password file, because it is not easily shareable and I don’t like the idea of having a cleartext password file on all our ansible users computers.
Problem
Adding --ask-vault-pass for every concerned playbooks is not an option. Our ansible users will not understand why sometimes this parameter is needed and why sometimes it’s not. On the contrary, asking the vault password for every playbook is a burden, because we have a lot of playbooks and sudo password is already asked each time too.
I tried to achieve the following solution, using prompts and various options. Nothing seems to work. Documentation does not explain how to do this:
The best solution (according to me)
Let’s look at this main.yml file:
- import_tasks: foo.yml
tags: always
- import_tasks: bar.yml
tags: bar
# Only this tasks uses a vault encrypted variable
- import_tasks: baz.yml
tags: [baz, vault]
Now, there’s a playbook.yml, importing this main.yml file.
In a perfect world, I would like this to happen:
example1:
ansible-playbook -i prod playbook.yml
Vault password:
example2:
ansible-playbook -i prod playbook.yml --tags baz
Vault password:
example3:
ansible-playbook -i prod playbook.yml --tags foo
# it runs without asking for vault password, because no tasks needing vault
# password will be run
Question
How can I configure ansible to ask for vault password only when it is needed (meaning: every time a vault encrypted variable is encountered)? Is it even possible? If not, what workaround would be viable given my situation?
Thanks.

I found a workaround.
The idea is to always load all passwords (including sudo) from an ansible vault file, needing only the vault password for each playbook. It means all machines should have the same deployer user password. This is even simpler than before, because there’s a single master password (the vault password) to control them all.
This is how it’s done:
ansible.cfg:
[privilege_escalation]
become_ask_pass = False
become = True
[defaults]
ask_vault_pass = True
vars/vault/env:
_vault:
sudo: !vault |
$ANSIBLE_VAULT;1.1;AES256
BLAHBLAHBLAH
another_pass: !vault |
$ANSIBLE_VAULT;1.1;AES256
ANOTHERENCRYPTEDPASSWORD
And for each playbook:
# Playbook stuff
# [...]
vars:
ansible_become_pass: "{{ _vault.sudo }}"
vars_files:
# env var is set dynamically in inventory file, so I can have different password per env.
# If the file is not found, it loads an empty null file
- [ "vars/vault/{{ env }}", "vars/null"]
# [...] Loading other var files
Now, a simple ansible-playbook -i env/prod myPlayBook.yml will only ask for the vault password, no prompt for sudo or anything else. It’s consistent and easy to share (only the encrypted password file and the vault password must be shared).

Related

module user doesn't accept encrypt password generated by ansible-vault?

Recently I used 'user' module to create user with password provided in vars/main.yml
- name: Create pamuser
user:
name: pamuser
password: "{{ pamuser_pass }}"
groups: wheel
append: yes
tags: pamuser
Once run a playbook, it gives me this warning
TASK [prerequisite : Create pamuser] *****************************************************************************
[WARNING]: The input password appears not to have been hashed. The 'password' argument must be encrypted for this
module to work properly.
Then I use ansible-vault encrypt_string command to encrypt only the specific variable "pamuser_pass"by replace plaintext with vault password that ansible-vault gave me
contents in /vars/main.yml
---
# vars file for prerequisite role
pamuser_pass: !vault |
$ANSIBLE_VAULT;1.1;AES256
65643265346231613137396339303834396663383466636631646337303235306137386534396266
3364333534616238396465626436376561323762303139620a376630643131323133336164373237
64663332363233303032636638306566303034393137636533373332383334333439663930613232
3737
then I remove current pamuser and re-run the playbook with command
ansible-playbook playbook.yaml --tags "pamuser" --ask-pass -K --ask-vault-pass
Along with the running process, it still shows the warning
[WARNING]: The input password appears not to have been hashed. The 'password' argument must be encrypted for this
module to work properly.
the outcome seem fine with id pamuser but once logging in with ssh pamuser#example.com then put the regular password, the password doesn't work. I can't login with that pamuser.
Is there something that I missed?
You should be following one of the recommended ways mentioned
here to provide the hash. It's not the general vault encryption in ansible. This is specific to the user module. Below is from the doc:
How do I generate encrypted passwords for the user module? Ansible
ad-hoc command is the easiest option:
ansible all -i localhost, -m debug -a "msg={{ 'mypassword' | password_hash('sha512',
'mysecretsalt') }}"
The mkpasswd utility that is available on most
Linux systems is also a great option:
mkpasswd --method=sha-512

Ansible and vault

I want to store my user's public keys in vault instead of a file.
How should i change my playbook:
name: push users public_keys
authorized_key:
state: present
user: admin
key: ""{{ lookup('file', '/path/to/your/www_id_rsa.pub') }}""
Thanks
One way could be storing your vault variables in a file or encrypt a string.
I'll show the use of file:
ansible-vault create vault_vars.yml (You'll be asked to create a password)
In the vault_vars.yml you create a normal variable containing the public_key: pub_key: "rsa..."
Include the file in your playbook:
vars_files:
- vault_vars.yml
You can access the variable from the vault just like you would with a variable being defined inside the playbook: {{ pub_key }}
An example printing the variable from the vault:
- hosts: server
vars_files:
- vault_vars.yml
tasks:
- debug: msg="{{pub_key}}"
Running playbook: ansible-playbook playbook.yml --ask-vault-pass
You can either use --ask-vault-pass (being prompted for password) or --vault-password-file (storing your vault password in a file)
EDIT
After reading the question again, and you stated you did not want to use a file meaning the solution would be as suggested below: Encrypt the string.
Personally I am not a big fan of encrypting strings and then put them directly in the playbook. I prefer to encrypt the file instead, making it easier to add/change values if needed.
I don't really get the point of encrypting a public key. You usually want to do this for private keys (or any other kind of sensitive data like passwords, tokens...).
Meanwhile, if you really want to do that, you don't need to change anything to your playbook. You just have to encrypt the file containing your public key:
ansible-vault encrypt --ask-vault-pass /path/to/your/www_id_rsa.pub
Once this is done, ansible will automagically detect this is a vault encryted file when you try to use it and it will decrypt it on the fly. For this to work, you will of course have to provide the same vault password when you execute the playbook (or you will get an error saying that the vault could not be decrypted).
ansible-playbook -i my_inventory --ask-vault-pass my_playbook.yml
For more info on different options to provide the vault pass/id see: https://docs.ansible.com/ansible/latest/user_guide/vault.html

How to combine multiple vars files per host?

I have a playbook running against multiple servers. All servers require a sudo password to be specified, which is specific to each user running the playbook. When running the playbook, I can't use --ask-become-pass, because the sudo passwords on the servers differ. This is the same situation as in another question about multiple sudo passwords.
A working solution is to specify ansible_become_pass in host_vars:
# host_vars/prod01.yml
ansible_become_pass: secret_prod01_password
domain: prod01.example.com
# host_vars/prod02.yml
ansible_become_pass: secret_prod02_password
domain: prod02.example.com
Besides ansible_become_pass, there are other variables defined per host. These variables should be committed to the git repository. However, as ansible_become_pass is specific to each user running the playbook, I'd like to have a separate file (ideally, vaulted) which specifies the password per host.
I imagine the following:
# host_vars/prod01.yml: shared in git
domain: prod01.example.com
# host_vars/prod01_secret.yml: in .gitignore
ansible_become_pass: secret_prod01_password
I imagine both files to be combined by Ansible when running the playbook. Is this possible in Ansible? If so, how?
You should be able to use the include_vars task with the inventory_hostname or ansible_hostname variable. For example:
- name: Include host specific variables
include_vars: "{{ ansible_hostname }}.yml"
- name: Include host specific secret variables
include_vars: "{{ ansible_hostname }}_secret.yml"
An even better solution would be to address the problem of users having unique passwords on different hosts.
You could create a new group in the inventory file, maybe sudo-hosts. Put all your sudo host in this group. Then create a file under the directory group_vars with the name of this goup. In this file put the secret yaml-structured text.
sudo_hosts:
host1:
password: xyz
othersecret_stuff: abc
host2:
...
then use ansbile-vault to encrypt this file with ONE password. Call the playbook with option --ask-vault-pass
and you can use your secrets with
"{{ sudo_host['ansible_host'].password }}"

Ansible multiple password vaults in group_vars file

I am having an issue with Ansible attempting to decode the incorrect vault when passing a command in.
The setup is a follows:
My ansible.cfg
[defaults]
transport=smart
hostfile=./hosts
host_key_checking=False
timeout=5
[ssh_connection]
ssh_args = ""
My hostfile:
[BigIP-Devices]
BigIP-1
BigIP-2
[Cisco-Devices]
Cisco-1
Cisco-2
TEST1
My group_vars:
BigIP-Devices < This is a vault encrypted with bigip-vaultkey
Cisco-Devices < This is a vault encrypted with cisco-vaultkey
bigip-vaultkey
cisco-vaultkey
Both the vaults are like the following with different details for each:
---
ansible_ssh_user: user
ansible_ssh_pass: password
I am trying to use the following command:
ansible -c ssh Cisco-Devices --vault-password-file ./group_vars/cisco-vaultkey --limit TEST1 -m raw -a "show version"
Even though it's calling Cisco-Devices in the command, I get the following error:
ERROR! Decryption failed on /home/users/ansible/device-access/group_vars/BigIP-Devices
However, if I move the BigIP files out of group_vars, it works correctly.
Any one have any ideas?
Many thanks in advance for your help!!!
It seems that this is an expected behaviour.
All host patterns and limits are applied after full inventory is parsed.
In your case Ansible discovers BigIP-Devices and Cisco-Devices groups and tries to load corresponding group variables.
If you never execute your playbook on BigIP-Devices and Cisco-Devices at the same time, you probably want to separate them into different inventories like:
./inventories/
./inventories/bigip/hosts
./inventories/bigip/group_vars/all/BigIP-Devices
./inventories/
./inventories/cisco/hosts
./inventories/cisco/group_vars/all/Cisco-Devices
and add required inventory with -i inventories/bigip.
P.S. Or use same vault password for all files.

Is it possible to have multi password with vault

I have a deployment project that I share with other teams. I have encrypted my secrets with vault.
I would like to encrypt the production file with a password and a staging file with an other password to avoid other teams having access to production secrets.
Is it possible to do that ?
I have done something like that. My secrets :
cat /group_vars/all/vault_production.yml (encrypt with password A)
production_password: 'test1'
cat/group_vars/all/vault_staging.yml (encrypt with password B)
staging_password: 'test2'
My environments :
cat hosts-production
[all:vars]
env_type=production
cat hosts-staging
[all:vars]
env_type=staging
My script :
- copy:
content: |
env PASS={{hostvars[inventory_hostname][env_type + '_password']}}
...
And I launch the playbook like that.
# for production
ansible-playbook -i hosts-staging test.yml --vault-password-file .password_a
# for staging
ansible-playbook -i hosts-staging test.yml --vault-password-file .password_b
But that doesn't work because there is 2 differents passwords (ERROR! Decryption failed).
Do you know how to do that ?
Thanks.
BR,
Eric
Multiple vault passwords are supported since Ansible 2.4:
ansible-playbook --vault-id dev#dev-password --vault-id prod#prompt site.yml
If multiple vault passwords are provided, by default Ansible will attempt to decrypt vault content by trying each vault secret in the order they were provided on the command line.
In the above case, the ‘dev’ password will be tried first, then the ‘prod’ password for cases where Ansible doesn’t know which vault id is used to encrypt something.
Sorry, only one vault password allowed per run today. Best way to work around this in the case where you really only need one or the other is to dynamically load a vaulted file based on a var; eg:
- hosts: localhost
vars_files:
- secretstuff-{{ env_type }}.yml
tasks:
...
or
- hosts: localhost
tasks:
- include_vars: secretstuff-{{ env_type }}.yml
...
depending on if you need the vars to survive for one play or the entire run (the latter will bring them in as facts instead of play vars).

Resources