Skip to content

Instantly share code, notes, and snippets.

@tigattack
Last active January 14, 2024 21:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tigattack/6dc3ad61b1e2a0f62f35386511829903 to your computer and use it in GitHub Desktop.
Save tigattack/6dc3ad61b1e2a0f62f35386511829903 to your computer and use it in GitHub Desktop.
"""Ansible vault exporter
- Finds all ansible-vault encrypted files and strings in a given path
- Extracts and decrypts each secret
- Writes plaintext secret to a file with the path to secret's the soruce
"""
import re
import os
import subprocess
# Directory to search recursively for vault strings/files
search_directory = './'
# Vault password file path
vault_password_file = '.pass'
# List of file extensions to include
include_extensions = [
"yml",
"yaml"
]
# List of directory paths to exclude
exclude_paths = [
".dependencies"
]
# Regex patterns for vault strings and vault files
secret_string_pattern = re.compile(
r'^\s*(\w*): !vault \|\n((\s*)?\$ANSIBLE_VAULT;1\.1;AES256\n((\s*)?[0-9a-z]*$\n)*)',
re.MULTILINE
)
secret_file_pattern = re.compile(
r'^(\$ANSIBLE_VAULT;1\.1;AES256\n([0-9a-z]*$\n)*)',
re.MULTILINE
)
def handle_secret_string(matches):
if matched_loop_index == 0:
outfile.write('Path: ' + file_path + '\n')
else:
outfile.write('\n' + 'Path: ' + file_path + '\n')
# Write each match to the output file
for match in matches:
secret_name = match[0]
stripped_match = '\n'.join([line.strip() for line in match[1].split('\n')]).strip()
decrypt_cmd = f"echo '{stripped_match}' | ansible-vault decrypt --vault-pass-file='{os.path.join(search_directory, vault_password_file)}'"
decrypted_secret = subprocess.check_output(decrypt_cmd, shell=True)
outfile.write(f"{secret_name}: {decrypted_secret.decode('utf-8').strip()}\n")
def handle_secret_file(matches):
if matched_loop_index == 0:
outfile.write('Path: ' + file_path + '\n')
else:
outfile.write('\n' + 'Path: ' + file_path + '\n')
# Write each match to the output file
for match in matches:
stripped_match = '\n'.join([line.strip() for line in match[0].split('\n')]).strip()
decrypt_cmd = f"echo '{stripped_match}' | ansible-vault decrypt --vault-pass-file='{os.path.join(search_directory, vault_password_file)}'"
decrypted_secret = subprocess.check_output(decrypt_cmd, shell=True)
outfile.write(decrypted_secret.decode('utf-8'))
with open('matches.txt', 'w', encoding='utf-8') as outfile:
matched_loop_index = 0
# Walk through the directory and search for the pattern in each file
for root, dirs, files in os.walk(search_directory):
# Exclude directories with excluded names
dirs[:] = [d for d in dirs if d not in exclude_paths]
for file in files:
# Skip files with excluded extensions
if not any(file.endswith(ext) for ext in include_extensions):
continue
# Open the file and read its contents
with open(os.path.join(root, file), 'r', encoding='utf-8') as f:
file_contents = f.read()
# Find all matches of the pattern in the file contents
string_matches = secret_string_pattern.findall(file_contents)
file_matches = secret_file_pattern.findall(file_contents)
file_path = os.path.relpath(os.path.join(root, file), search_directory)
if len(string_matches) > 0:
matched_loop_index = matched_loop_index+1
handle_secret_string(string_matches)
if len(file_matches) > 0:
matched_loop_index = matched_loop_index+1
handle_secret_file(file_matches)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment