From cfc871cce6aefaa0ff64619780a807cba761c6b2 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Mon, 30 Jan 2023 12:20:23 +0100 Subject: ... --- tools/sops-inventory/default.nix | 19 ++++++ tools/sops-inventory/setup.py | 11 ++++ tools/sops-inventory/sops_inventory/__init__.py | 0 tools/sops-inventory/sops_inventory/__main__.py | 85 +++++++++++++++++++++++++ 4 files changed, 115 insertions(+) create mode 100644 tools/sops-inventory/default.nix create mode 100644 tools/sops-inventory/setup.py create mode 100644 tools/sops-inventory/sops_inventory/__init__.py create mode 100644 tools/sops-inventory/sops_inventory/__main__.py (limited to 'tools/sops-inventory') diff --git a/tools/sops-inventory/default.nix b/tools/sops-inventory/default.nix new file mode 100644 index 00000000..94c455e5 --- /dev/null +++ b/tools/sops-inventory/default.nix @@ -0,0 +1,19 @@ +{ system, self, mach-nix, ... }: +let + pkgs = self.legacyPackages.${system}; +in mach-nix.lib.${system}.buildPythonPackage { + pname = "sops-inventory"; + version = "0.0.0"; + + src = pkgs.lib.sourceByRegex ./. ["^setup\.py$" "^sops_inventory(/[^/]+.*)?$"]; + + ignoreDataOutdated = true; + requirements = '' + pyyaml + ''; + + postInstall = '' + wrapProgram $out/bin/sops-inventory \ + --set-default SOPS_INVENTORY_BASE ${self} + ''; +} diff --git a/tools/sops-inventory/setup.py b/tools/sops-inventory/setup.py new file mode 100644 index 00000000..3ea2a5d1 --- /dev/null +++ b/tools/sops-inventory/setup.py @@ -0,0 +1,11 @@ +from setuptools import setup + +setup( + name='sops-inventory', + packages=['sops_inventory'], + entry_points={ + 'console_scripts': [ + 'sops-inventory=sops_inventory.__main__:main' + ], + }, +) diff --git a/tools/sops-inventory/sops_inventory/__init__.py b/tools/sops-inventory/sops_inventory/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/sops-inventory/sops_inventory/__main__.py b/tools/sops-inventory/sops_inventory/__main__.py new file mode 100644 index 00000000..68f72b60 --- /dev/null +++ b/tools/sops-inventory/sops_inventory/__main__.py @@ -0,0 +1,85 @@ +import os,sys + +from pathlib import Path +from collections import deque, defaultdict + +import argparse + +from yaml import load, YAMLError +try: + from yaml import CLoader as Loader +except ImportError: + from yaml import Loader + + +SOPS_TYPES = frozenset({'kms', 'gcp_kms', 'azure_kv', 'hc_vault', 'age', 'pgp'}) + + +class BooleanAction(argparse.Action): + def __init__(self, option_strings, dest, nargs=None, **kwargs): + super(BooleanAction, self).__init__(option_strings, dest, nargs=0, **kwargs) + + def __call__(self, parser, namespace, values, option_string=None): + setattr(namespace, self.dest, False if option_string.startswith('--no') else True) + + +def main(): + default_base = os.getenv('SOPS_INVENTORY_BASE', default=[]) + if default_base: + default_base = Path(default_base) + + parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter) + parser.add_argument('--list-files', '--no-list-files', action=BooleanAction, default=False, help='Only list sops files') + parser.add_argument('path', metavar='PATH', nargs='?' if default_base else None, type=Path, default=default_base, help='Base directory to take inventory of') + args = parser.parse_args() + + inventory = defaultdict(set) + + queue = deque([args.path]) + while queue: + baseDir = queue.popleft() + for child in baseDir.iterdir(): + if child.is_dir(): + queue.append(child) + else: + try: + with child.open(mode='r') as fh: + yaml = load(fh, Loader=Loader) + if not yaml: + raise ValueError('Could not parse YAML') + if not isinstance(yaml, dict) or not 'sops' in yaml: + raise ValueError('Did not find "sops" key') + sops = yaml['sops'] + + key_info = set() + for k in SOPS_TYPES: + if k in sops: + v = sops[k] + if not v: + continue + + match k: + case 'pgp': + for r in v: + key_info.add(r['fp']) + case 'age': + for r in v: + key_info.add(r['recipient']) + case _: + raise NotImplementedError + inventory[frozenset(key_info)].add(child.relative_to(args.path)) + except (YAMLError, ValueError) as e: + pass + + if not args.list_files: + for keys, files in inventory.items(): + print(','.join(keys) + ':') + for file in files: + print(' - ' + str(file)) + else: + for _, files in inventory.items(): + for file in files: + print(file) + +if __name__ == '__main__': + os.exit(main()) -- cgit v1.2.3