|
#!/usr/bin/env python3 |
|
|
|
import argparse |
|
import json |
|
import os |
|
import sys |
|
from typing import List, Optional |
|
|
|
|
|
def parse_args() -> argparse.Namespace: |
|
p = argparse.ArgumentParser( |
|
description="Fetch vault password from Semaphore survey JSON") |
|
p.add_argument( |
|
"--vault-id", |
|
dest="vault_id", |
|
required=True, |
|
help="The vault-id supplied by Ansible (e.g. 'prod' in --vault-id prod)", |
|
) |
|
return p.parse_args() |
|
|
|
|
|
def read_cmdline(pid: int) -> List[str]: |
|
"""Return the argv of *pid* as a list, using /proc.""" |
|
path = f"/proc/{pid}/cmdline" |
|
try: |
|
with open(path, "rb") as fh: |
|
raw = fh.read() |
|
except FileNotFoundError: |
|
sys.stderr.write(f"[vault-client] Parent process {pid} no longer exists\n") |
|
sys.exit(1) |
|
|
|
argv = raw.decode(errors="replace").split("\0") |
|
if argv and argv[-1] == "": |
|
argv.pop() # final empty after last NUL |
|
return argv |
|
|
|
|
|
def iter_extra_vars(argv: List[str]): |
|
"""Yield every --extra-vars value (handles spaced and = variants).""" |
|
i = 0 |
|
while i < len(argv): |
|
tok = argv[i] |
|
if tok == "--extra-vars": |
|
if i + 1 < len(argv): |
|
yield argv[i + 1] |
|
i += 2 |
|
continue |
|
if tok.startswith("--extra-vars="): |
|
yield tok.split("=", 1)[1] |
|
i += 1 |
|
|
|
|
|
def maybe_password(extra: str, vault_key: str) -> Optional[str]: |
|
"""Return password if *extra* is JSON containing *vault_key*, else None.""" |
|
if extra.startswith("@"): |
|
return None # file reference; skip |
|
if not extra.lstrip().startswith("{"): |
|
return None # not JSON; skip |
|
try: |
|
data = json.loads(extra) |
|
except json.JSONDecodeError: |
|
return None |
|
return data.get(vault_key) |
|
|
|
|
|
def main() -> int: |
|
args = parse_args() |
|
vault_key = f"vault#{args.vault_id}" |
|
|
|
ppid = os.getppid() |
|
argv = read_cmdline(ppid) |
|
|
|
for extra in iter_extra_vars(argv): |
|
pwd = maybe_password(extra, vault_key) |
|
if pwd is not None: |
|
# Ansible accepts either with or without the trailing newline. |
|
print(pwd) |
|
return 0 |
|
|
|
sys.stderr.write( |
|
f"[vault-client] Key '{vault_key}' not found in parent's --extra-vars\n" |
|
) |
|
return 1 |
|
|
|
|
|
if __name__ == "__main__": |
|
sys.exit(main()) |