summaryrefslogtreecommitdiff
path: root/tools/sops-inventory
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2023-01-30 12:20:23 +0100
committerGregor Kleen <gkleen@yggdrasil.li>2023-01-30 12:20:23 +0100
commitcfc871cce6aefaa0ff64619780a807cba761c6b2 (patch)
tree965e8276ed36f11698b6c7d6eadab9f88d5f97c5 /tools/sops-inventory
parentaa54fe89b98d354d21141c589332ce7950ef2e59 (diff)
downloadnixos-cfc871cce6aefaa0ff64619780a807cba761c6b2.tar
nixos-cfc871cce6aefaa0ff64619780a807cba761c6b2.tar.gz
nixos-cfc871cce6aefaa0ff64619780a807cba761c6b2.tar.bz2
nixos-cfc871cce6aefaa0ff64619780a807cba761c6b2.tar.xz
nixos-cfc871cce6aefaa0ff64619780a807cba761c6b2.zip
...
Diffstat (limited to 'tools/sops-inventory')
-rw-r--r--tools/sops-inventory/default.nix19
-rw-r--r--tools/sops-inventory/setup.py11
-rw-r--r--tools/sops-inventory/sops_inventory/__init__.py0
-rw-r--r--tools/sops-inventory/sops_inventory/__main__.py85
4 files changed, 115 insertions, 0 deletions
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 @@
1{ system, self, mach-nix, ... }:
2let
3 pkgs = self.legacyPackages.${system};
4in mach-nix.lib.${system}.buildPythonPackage {
5 pname = "sops-inventory";
6 version = "0.0.0";
7
8 src = pkgs.lib.sourceByRegex ./. ["^setup\.py$" "^sops_inventory(/[^/]+.*)?$"];
9
10 ignoreDataOutdated = true;
11 requirements = ''
12 pyyaml
13 '';
14
15 postInstall = ''
16 wrapProgram $out/bin/sops-inventory \
17 --set-default SOPS_INVENTORY_BASE ${self}
18 '';
19}
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 @@
1from setuptools import setup
2
3setup(
4 name='sops-inventory',
5 packages=['sops_inventory'],
6 entry_points={
7 'console_scripts': [
8 'sops-inventory=sops_inventory.__main__:main'
9 ],
10 },
11)
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
--- /dev/null
+++ b/tools/sops-inventory/sops_inventory/__init__.py
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 @@
1import os,sys
2
3from pathlib import Path
4from collections import deque, defaultdict
5
6import argparse
7
8from yaml import load, YAMLError
9try:
10 from yaml import CLoader as Loader
11except ImportError:
12 from yaml import Loader
13
14
15SOPS_TYPES = frozenset({'kms', 'gcp_kms', 'azure_kv', 'hc_vault', 'age', 'pgp'})
16
17
18class BooleanAction(argparse.Action):
19 def __init__(self, option_strings, dest, nargs=None, **kwargs):
20 super(BooleanAction, self).__init__(option_strings, dest, nargs=0, **kwargs)
21
22 def __call__(self, parser, namespace, values, option_string=None):
23 setattr(namespace, self.dest, False if option_string.startswith('--no') else True)
24
25
26def main():
27 default_base = os.getenv('SOPS_INVENTORY_BASE', default=[])
28 if default_base:
29 default_base = Path(default_base)
30
31 parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
32 parser.add_argument('--list-files', '--no-list-files', action=BooleanAction, default=False, help='Only list sops files')
33 parser.add_argument('path', metavar='PATH', nargs='?' if default_base else None, type=Path, default=default_base, help='Base directory to take inventory of')
34 args = parser.parse_args()
35
36 inventory = defaultdict(set)
37
38 queue = deque([args.path])
39 while queue:
40 baseDir = queue.popleft()
41 for child in baseDir.iterdir():
42 if child.is_dir():
43 queue.append(child)
44 else:
45 try:
46 with child.open(mode='r') as fh:
47 yaml = load(fh, Loader=Loader)
48 if not yaml:
49 raise ValueError('Could not parse YAML')
50 if not isinstance(yaml, dict) or not 'sops' in yaml:
51 raise ValueError('Did not find "sops" key')
52 sops = yaml['sops']
53
54 key_info = set()
55 for k in SOPS_TYPES:
56 if k in sops:
57 v = sops[k]
58 if not v:
59 continue
60
61 match k:
62 case 'pgp':
63 for r in v:
64 key_info.add(r['fp'])
65 case 'age':
66 for r in v:
67 key_info.add(r['recipient'])
68 case _:
69 raise NotImplementedError
70 inventory[frozenset(key_info)].add(child.relative_to(args.path))
71 except (YAMLError, ValueError) as e:
72 pass
73
74 if not args.list_files:
75 for keys, files in inventory.items():
76 print(','.join(keys) + ':')
77 for file in files:
78 print(' - ' + str(file))
79 else:
80 for _, files in inventory.items():
81 for file in files:
82 print(file)
83
84if __name__ == '__main__':
85 os.exit(main())