Last active
January 14, 2024 21:48
-
-
Save tigattack/6dc3ad61b1e2a0f62f35386511829903 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"""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