Rekey individually encrypted ansible vault variables?

1.3k Views Asked by At

From reading the documentation,

You cannot rekey encrypted variables

For example, if this is the content of group_vars/all.yaml, I would like to rekey all the encrypted variables.

key_tab: !vault |
  $ANSIBLE_VAULT;1.1;AES256
  30333939663734636530386263663437343431353539643366633534366239643763326138653232
  3562383132623937346138613833396563653038646165300a623061363063663132373739373031
  66623133393239376366383235353332366336386532643637343438653634633734346639636334
  3633363032376339340a663531346633623466643163353638303534313937663931633962383637
  3637
certs:
  - file: client.cert
    password: !vault |
      $ANSIBLE_VAULT;1.1;AES256
      35626163653930386265393064326330393433343763626534373330393432373231633365656534
      6237626631326634333963313733356531623239653161370a356666326631663565396633396139
      32303962343064343530383364616235343130373935313161353135613539653061363735336337
      3636633036313565640a663736613065396262336433653564373161393431636661666134643761
      3639

I have tried to do this with a few bash commands, but it gets complicated with indentation.

Is there an automated way to accomplish this rekey?

1

There are 1 best solutions below

3
On

From Gaël's suggestion, I created a python tool that uses ansible libs to accomplish the rekey.

It preserves indentation and updates files in place. Works for vault variables and regular vault files.

Script

#!/usr/bin/env python3

import sys
import re
from tempfile import NamedTemporaryFile
from ansible.parsing.vault import VaultEditor, VaultLib, VaultSecret
from ansible.constants import DEFAULT_VAULT_IDENTITY

def rekey(content, old_secret, new_secret):
  vault_regex = re.compile(r'(^(\s*)\$ANSIBLE_VAULT\S*\n(\s*\w+\n)*)', re.MULTILINE)
  vaults = {match[0]: match[1] for match in vault_regex.findall(content)}
  for old_vault, indentation in vaults.items():
    with NamedTemporaryFile(mode='w', delete=False) as f:
      f.write(old_vault.replace(indentation, ''))
    VaultEditor(VaultLib([(DEFAULT_VAULT_IDENTITY, old_secret)])).rekey_file(f.name, new_secret)
    with open(f.name) as f:
      new_vault = indentation + indentation.join(f.readlines())
      content = content.replace(old_vault, new_vault)
  return content

def main(old_password, new_password, files):
  for file_name in files:
    with open(file_name) as f:
      content = f.read()
    with open(file_name, 'w') as f:
      f.write(rekey(content, VaultSecret(old_password.encode()), VaultSecret(new_password.encode())))

main(sys.argv[1], sys.argv[2], sys.argv[3:])

Usage

./rekey.py my-old-pass my-new-pass $(find . -type f -name "*.yaml") another-file.vault

Explanation

For each input file:

  1. Read the input file and extract sequences that match the vault regex
  2. Save the extracted vaults to temporary files
  3. Rekey the temporary files
  4. Use the contents of the rekeyed files to make a replacement in the input file