lots of stuff
This commit is contained in:
Executable
+123
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent
|
||||
KEY = re.compile(r"^(?P<indent>[ \t]*)(?:-\s+)?(?P<name>[\w.-]+):\s*!vault\s*\|-?\s*$")
|
||||
HEADER = "$ANSIBLE_VAULT"
|
||||
|
||||
|
||||
def password_file():
|
||||
override = os.environ.get("ANSIBLE_VAULT_PASSWORD_FILE")
|
||||
path = Path(override) if override else ROOT / ".vault-password"
|
||||
if not path.is_file():
|
||||
sys.exit(f"vault password file not found: {path}")
|
||||
return str(path)
|
||||
|
||||
|
||||
def decrypt(blob, pw):
|
||||
result = subprocess.run(
|
||||
["ansible-vault", "decrypt", "--vault-password-file", pw, "--output", "-"],
|
||||
input=blob,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
return None, result.stderr.strip()
|
||||
return result.stdout, None
|
||||
|
||||
|
||||
def extract(text):
|
||||
lines = text.splitlines()
|
||||
found = []
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
match = KEY.match(lines[i])
|
||||
if not match:
|
||||
i += 1
|
||||
continue
|
||||
indent = len(match.group("indent").expandtabs(8))
|
||||
body = []
|
||||
i += 1
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
if not line.strip():
|
||||
body.append("")
|
||||
i += 1
|
||||
continue
|
||||
width = len(line.expandtabs(8)) - len(line.expandtabs(8).lstrip())
|
||||
if width <= indent:
|
||||
break
|
||||
body.append(line)
|
||||
i += 1
|
||||
blob = textwrap.dedent("\n".join(body)).strip()
|
||||
if blob.startswith(HEADER):
|
||||
found.append((match.group("name"), blob))
|
||||
return found
|
||||
|
||||
|
||||
def show(name, value):
|
||||
value = value.rstrip("\n")
|
||||
if "\n" in value:
|
||||
print(f"{name}:")
|
||||
print(textwrap.indent(value, " "))
|
||||
else:
|
||||
print(f"{name}: {value}")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Decrypt inline !vault variables in a playbook or vars file."
|
||||
)
|
||||
parser.add_argument("file", help="path to the file, or - to read a blob from stdin")
|
||||
parser.add_argument("names", nargs="*", help="only show these variable names")
|
||||
args = parser.parse_args()
|
||||
|
||||
pw = password_file()
|
||||
|
||||
if args.file == "-":
|
||||
blob = textwrap.dedent(sys.stdin.read()).strip()
|
||||
if not blob.startswith(HEADER):
|
||||
sys.exit("stdin does not contain a vault blob")
|
||||
value, error = decrypt(blob, pw)
|
||||
if error:
|
||||
sys.exit(error)
|
||||
print(value.rstrip("\n"))
|
||||
return
|
||||
|
||||
path = Path(args.file)
|
||||
text = path.read_text()
|
||||
|
||||
if text.lstrip().startswith(HEADER):
|
||||
value, error = decrypt(text.strip(), pw)
|
||||
if error:
|
||||
sys.exit(error)
|
||||
sys.stdout.write(value)
|
||||
return
|
||||
|
||||
variables = extract(text)
|
||||
if args.names:
|
||||
variables = [(n, b) for n, b in variables if n in args.names]
|
||||
if not variables:
|
||||
sys.exit(f"no inline vault variables found in {path}")
|
||||
|
||||
failures = 0
|
||||
for name, blob in variables:
|
||||
value, error = decrypt(blob, pw)
|
||||
if error:
|
||||
print(f"{name}: <failed: {error}>", file=sys.stderr)
|
||||
failures += 1
|
||||
continue
|
||||
show(name, value)
|
||||
|
||||
sys.exit(1 if failures else 0)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user