How to decrypt string with ansible-vault 2.3.0

I have been waiting for ansible 2.3 as it was going to introduce encrypt_string feature.

Unfortuately I'm not sure how can I read the encrypted string.

I did try decrypt_string, decrypt (the file), view (the file) and nothing works.

cat test.yml
---
test: !vault |
$ANSIBLE_VAULT;1.1;AES256
37366638363362303836383335623066343562666662386233306537333232396637346463376430
3664323265333036663736383837326263376637616466610a383430623562633235616531303861
66313432303063343230613665323930386138613334303839626131373033656463303736366166
6635346135636437360a313031376566303238303835353364313434363163343066363932346165
6136

The error I'm geeting is ERROR! input is not vault encrypted data for test.yml

How can I decrypt the string so I know what it's value without the need to run the play?

108738 次浏览

Did you try setting the encrypted string as a variable and then using -debug to get its decrypted output?

i.e.

Define your encrypted string as a variable test in your playbook and then do:

-debug: msg="My Secret value is \{\{test | replace('\n', '')}}"

in your playbook and then run the playbook:

$ ansible-playbook -i localhost YourPlaybook.yml --vault-password-file path/to/your/secret_key_file

You can pipe the input then tell ansible-vault to output to stderr and then redirect the stdout to /dev/null since the tool prints Decryption successful.

The /dev/stdin/ part may not be needed in new Ansible versions.

Something like:

echo 'YOUR_SECRET_VALUE' | ansible-vault decrypt /dev/stdin --output=/dev/stderr > /dev/null

Here is a example:

echo '$ANSIBLE_VAULT;1.1;AES256
30636561663762383436386639353737363431353033326634623639666132623738643764366530
6332363635613832396361333634303135663735356134350a383265333537383739353864663136
30393363653361373738656361613435626237643633383261663138653466393332333036353737
3335396631613239380a616531626235346361333737353831376633633264326566623339663463
6235' | ansible-vault decrypt /dev/stdin --output=/dev/stderr > /dev/null

I hope they implement a simpler way of doing this.

Edit: Environment Variables as Input:

To have a similar behaviour with multi-line environment variables on bash use printf instead of echo

Example (password: 123):

export chiphertext='$ANSIBLE_VAULT;1.1;AES256
65333363656231663530393762613031336662613262326666386233643763636339366235626334
3236636366366131383962323463633861653061346538360a386566363337383133613761313566
31623761656437393862643936373564313565663633636366396231653131386364336534626338
3430343561626237660a333562616537623035396539343634656439356439616439376630396438
3730'


printf "%s\n" $chiphertext | ansible-vault decrypt /dev/stdin --output=/dev/stderr > /dev/null

since whole vault files do not play well with git histories, using vault strings within the variable files is the way to go, it also makes grepping out variables by name much clearer.

Here is a simple worked example:

I want to put fredsSecretString: value into vars.yml , (its value is fastfredfedfourfrankfurters but hush, don't let people know !!)

$ ansible-vault encrypt_string 'fastfredfedfourfrankfurters' -n fredsSecretString >> vars.yml
New Vault password: fred
Confirm New Vault password: fred
$ cat vars.yml
fredsSecretString: !vault |
$ANSIBLE_VAULT;1.1;AES256
36643662303931336362356361373334663632343139383832626130636237333134373034326565
3736626632306265393565653338356138626433333339310a323832663233316666353764373733
30613239313731653932323536303537623362653464376365383963373366336335656635666637
3238313530643164320a336337303734303930303163326235623834383337343363326461653162
33353861663464313866353330376566346636303334353732383564633263373862

To decrypt the value feed the encrypted string back into ansible-vault as follows:

    $ echo '$ANSIBLE_VAULT;1.1;AES256
36643662303931336362356361373334663632343139383832626130636237333134373034326565
3736626632306265393565653338356138626433333339310a323832663233316666353764373733
30613239313731653932323536303537623362653464376365383963373366336335656635666637
3238313530643164320a336337303734303930303163326235623834383337343363326461653162
33353861663464313866353330376566346636303334353732383564633263373862' |
ansible-vault decrypt && echo
Vault password: fred
Decryption successful
fastfredfedfourfrankfurters
$

You can also do with plain ansible command for respective host/group/inventory combination, e.g.:

$ ansible my_server -m debug -a 'var=my_secret'
my_server | SUCCESS => {
"my_secret": "373861663362363036363361663037373661353137303762"
}

Although, there is no problems showing encrypted string values with ansible debug messages or using ansible cli, there is one more solution that may be convenient for automation needs. You can utilize python libs from ansible and use them in your code (basically, all this located in ansible.parsing.*)

1) Provide vault password and generate "vault" with secrets.

# Load vault password and prepare secrets for decryption
loader = DataLoader()
secret = vault.get_file_vault_secret(filename=vault_password_file, loader=loader)
secret.load()
vault_secrets = [('default', secret)]
_vault = vault.VaultLib(vault_secrets)

2) Load yaml file with AnsibleLoader:

with codecs.open(input_file, 'r', encoding='utf-8') as f:
loaded_yaml = AnsibleLoader(f, vault_secrets=_vault.secrets).get_single_data()

3) If you need to encrypt a new string and update your dictionary:

    new_encrypted_value = objects.AnsibleVaultEncryptedUnicode.from_plaintext(source_system_password, _vault, vault_secrets[0][1])
loaded_yaml[target_env]['credentials'][external_system_name]['password'] = new_encrypted_variable

4) Once complete processing, write back with AnsibleDumper:

with open('new_variables.yml','w') as fd:
yaml.dump(loaded_yaml, fd, Dumper=AnsibleDumper, encoding=None, default_flow_style=False)

Here is what works for me, similar to what Scudelletti does but passing in the vault pass i.e.

echo '$ANSIBLE_VAULT;1.1;AES256
31363861346536343331393539323936346464386534346337306565626466393764666366363637
6533373165656431393662653463646430663933363431380a336130363131373238326330393931
39343533396161323834613030383339653633393133393932613562396630303530393030396335
3630656237663038630a363032373633363161633464653431386237333262343231313830363965
31393930343532323133386536376637373463396534623631633234393565373337613530643031
38393862616635326339373731353465303364303365336132613566396666626536636533303839
393465653830393231636638643735313666' | ansible-vault decrypt --vault-password-file /path/to/your/.vault_pass.txt /dev/stdin --output=/dev/stderr > /dev/null && echo

The output will be on its own line for convenience, thanks to the trailing && echo. The permission of my vault pass is 644 if you run into any permission errors.

Hope it helps!

This one command extracts out just the encrypted data and passes it to decrypt. I like it a bit better, as you don't need to manually extract the data.

$ grep -v vault test.yml | awk '{$1=$1;print}' | ansible-vault decrypt

yq extracts the encrypted var value, then will create a temporary file and use it with ansible-vault:

cat ansible_file.yml | yq -r ".variable_name" > tmp_file.txt


# you can also use 'ansible-vault decrypt'
ansible-vault view --ask-vault-pass tmp_file.txt

Here's another way to decrypt strings

$ ansible localhost \
-m debug \
-a "var=mysecret" \
-e "@inventory/group_vars/master"
localhost | SUCCESS => {
"mysecret": "somesecret\n"
}

The trick here is we're passing a file with an Ansible vaulted secret, mysecret within it too ansible and it's able to decrypt it.

NOTE: If you do not have your password to decrypt the Ansible vaulted encrypted secret you can pass that in as well:

$ ansible localhost --vault-password-file=~/.vault_pass.txt \
-m debug \
-a "var=mysecret" \
-e "@inventory/group_vars/master"
localhost | SUCCESS => {
"mysecret": "somesecret\n"
}

This is how I am encrypting and decrypting strings inline, additionally for use as environment variables.

yq is especially useful here for interpreting yaml input.

In one line if I were to test encrypt and decypt a string I would do this-

echo -n "test some input that will be encrypted and decrypted" | ansible-vault encrypt_string --vault-id $vault_key --stdin-name testvar_name | yq r - "testvar_name" | ansible-vault decrypt --vault-id $vault_key

I'm guessing that those usually interested in this are interested in decrypting environment variables. This is how I implement that use case, where testvar is the encrypted environment variable, and and $vault-id is the path to the key you are using to encrypt/decrypt.

testvar=$(echo -n "test some input that will be encrypted and stored as an env var" | ansible-vault encrypt_string --vault-id $vault_key --stdin-name testvar_name | base64 -w 0)
result=$(echo $testvar | base64 -d | /var/lib/snapd/snap/bin/yq r - "testvar_name" | ansible-vault decrypt --vault-id $vault_key); echo $result

For a file like test.yml:

---
test: !vault |
$ANSIBLE_VAULT;1.1;AES256
37366638363362303836383335623066343562666662386233306537333232396637346463376430
3664323265333036663736383837326263376637616466610a383430623562633235616531303861
66313432303063343230613665323930386138613334303839626131373033656463303736366166
6635346135636437360a313031376566303238303835353364313434363163343066363932346165
6136


the following crude implementation (recomended only for some quick manual action obviously):

for row in $(cat test.yml | yq -c '.[]'); do
decrypt() {
printf "decrypting '%s'" $row | sed -e 's/^"//' -e 's/"$//'
echo "---"
printf $row | sed -e 's/^"//' -e 's/"$//' | ansible-vault decrypt -
}
echo -e "==\n: $(decrypt '.')"
done

should work, provided that you have the key that encrypted the data.

Coming across this question and the answers here, I'd just like to add a quick bash script i cooked together that reads through an entire yaml file hunting for strings that can be decrypted dumping to screen.

It's far from perfect and I'm not the hottest at bash but, hope this helps someone who was in the same situation as me wanting to do a general dump.

To use the following script, it is necessary to have your vault password in a file (current working path) called vault_pass, along with yq and jq installed. The file to be parsed should be first argument. e.g. ./vault_reader.sh group_vars/production.yml

#!/bin/bash
KEY_OR_VALUE=key
for row in $(yq read -j $1 | jq); do
if [ "$KEY_OR_VALUE" == "key" ]
then
KEY_OR_VALUE="value"
echo $(sed -e "s/\"//g" -e "s/\://g" <<<$row)
else
KEY_OR_VALUE="key"
ENC_VALUE=$(sed -e "s/\"//g" -e "s/\://g" -e"s/\,//g"<<<$row)
if [[ $ENC_VALUE = '$ANSIBLE_VAULT'* ]]; then
echo -e "$ENC_VALUE" | ansible-vault decrypt --vault-password-file vault_pass
fi
echo ""
fi
done

For those who want to define an alias and forget about pipes and temp files, here is a solution which you can adopt:

function decrypt_ansible_vault_string() {
export FN=$1
export KEY=$2
ansible-vault view <(yq r $FN $KEY)
}

Example usage:

$ head myrole/var/main.yml
# Variables here override defaults
website:
server: 127.0.0.1
port: 8081
session:
hash_key: !vault |
$ANSIBLE_VAULT;1.1;AES256
33626439623630633332343836316334376637323738323061373334373733326566613262373036
6632623432373263613139646432333331313731326232390a653031366564313235323065303865
32383563393261326633306663663437386134666230373332646234656464356331646335636564


$ decrypt_ansible_vault_string myrole/vars/main.yml website.session.hash_key

This answer expands on the comment from @maricn Note, I am using this yq, but any yaml query tool can do. It's the principle of using subshell redirect that matters here (No temp files). Also note, you can add --ask-vault-password. However, using a secret encrypted with GPG is much nicer, since you don't have to type your password every time and this work flow is much better for teams (YMVV and IHMO). Here is a good tutorial on how to use GPG with ansible-vault.

Stick this in your .bashrc and enjoy it.

update

I got frustrated with ansible-vault encrypt\decrypt workflow. So, I created a wrapper for also decrypting strings in the var files. Check it out: https://github.com/oz123/ansible-vault-string-helper

You can do it with a one-liner

ansible localhost -m debug -a var='NAME_OF_ENCRYPTED_VAR' -e "@PATH_TO_FILE_WITH_VARIABLE" --vault-id yourid@/path/to/file

or enter the password from command line

ansible localhost -m debug -a var='NAME_OF_ENCRYPTED_VAR' -e "@PATH_TO_FILE_WITH_VARIABLE" --ask-vault-pass

You can copy the encrypted string to a file but you need to only copy the encrypted part and not the other yml parts.

So you file need to change from:

test: !vault |
$ANSIBLE_VAULT;1.1;AES256
37366638363362303836383335623066343562666662386233306537333232396637346463376430
3664323265333036663736383837326263376637616466610a383430623562633235616531303861
66313432303063343230613665323930386138613334303839626131373033656463303736366166
6635346135636437360a313031376566303238303835353364313434363163343066363932346165
6136

to:

$ANSIBLE_VAULT;1.1;AES256
37366638363362303836383335623066343562666662386233306537333232396637346463376430
3664323265333036663736383837326263376637616466610a383430623562633235616531303861
66313432303063343230613665323930386138613334303839626131373033656463303736366166
6635346135636437360a313031376566303238303835353364313434363163343066363932346136

And you'll be able to decript or view with

ansible-vault decrypt --vault-password-file <path to passwordfile> test.yml

ansible-vault view --vault-password-file <path to passwordfile> test.yml

And perhaps drop the .yml because that doesn't make sense anymore.

I know it's been a while, but it worked for me when I piped it through ansible-vault decrypt without anything else, like this:

$ echo '$ANSIBLE_VAULT;1.1;AES256
38613538323065373061616466616334306237336461333935393261646131616232643238626635
3336633631366539383039343437306664336165326565650a353233303431613362653838643135
34363763366134393366356339343039313035366164636133326639376334313335316565373330
3435633463313334310a653239313039323135363865313933626464663363656164343662303763
34616663626530656630633839346531653862633332396365396432366234333861' | ansible-vault decrypt
Decryption successful
super-secret-string$

Just in case anyone is interested. I have ansible version 2.9.26

Insane but elegant shell script to output a clean yaml file with decrypted inline vars (assumes that you have ANSIBLE_VAULT_PASSWORD_FILE set and yq v4 installed):

VARS_FILE=path/to/your/vars_file_with_encrypted_vars.yml
yq -P e "$(for v in $(grep '\!vault' $VARS_FILE | cut -d: -f1); do val=$(yq e .${v} $VARS_FILE | tr -d ' ' | ansible-vault decrypt); echo .$v = \"$val\" \|; done) null = null" $VARS_FILE

With this you can decrypt a file containing just an ansible vault string:

cat encrypted_vault_string | ansible-vault decrypt

output:

Vault passsword: <enter password, is not echoed to you>
Decryption successful
< decrypted string here>

An ansible vault string looks like:

$ANSIBE_VAULT;1.1;AES256
123456789...
123456789...
123456789...
1234

This also works without an intermediate file

echo -e '$ANSIBLE_VAULT;1.1;AES256\n123456789...789' | ansible-vault decrypt

Trying to decrypt /dev/stdin, as in ansible-vault decrypt /dev/stdin, or using --vault-password-file=/dev/stdin, like other commenters mention, also fails for me with errors like ERROR! [Errno 2] No such file or directory: '/proc/100/fd/pipe:[12930445]'.

However, --vault-password-file also takes an executable to produce the password on stdout, so you can actually use /bin/cat to pipe in the password:

echo password | ansible-vault decrypt --output - --vault-password-file=/bin/cat ./encrypted_vault_file