Wrapper for Mozilla's sops tool to work with encrypted configs and render them into a classic `.env` format
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

65 lines
1.9 KiB

import logging
import subprocess
import yaml
from pprint import pprint
try:
from yaml import CLoader as YamlLoader, CDumper as YamlDumper
except ImportError:
from yaml import Loader as YamlLoader, Dumper as YamlDumper
class Sops:
def __init__(self, config):
self.config = config
self.logger = logging.getLogger("configs")
def resolve(self, config, path):
self.logger.debug("Resolving " + path)
# Convert data back to yaml
config_str = config.export()
# Decrypt file
result = subprocess.run([
'sops',
'--ignore-mac',
'--input-type', 'yaml',
'--output-type', 'yaml',
'--decrypt', '/dev/stdin'
], stdout=subprocess.PIPE, input=config_str.encode('utf-8'))
# Read decrypted file back
decrypted_config = yaml.load(result.stdout.decode('utf-8'), Loader=YamlLoader)
data = Sops._merge_dicts(decrypted_config['config'], decrypted_config['secrets_encrypted'])
return Sops._resolve_in_object(data, path.split('.'))
def _resolve_in_object(obj, path):
nextkey = path.pop(0)
if not nextkey in obj:
return None
if len(path) == 0:
return obj[nextkey]
else:
return Sops._resolve_in_object(obj[nextkey], path)
def _merge_dicts(a, b, path=None):
# https://stackoverflow.com/questions/7204805/dictionaries-of-dictionaries-merge
if path is None: path = []
for key in b:
if key in a:
if isinstance(a[key], dict) and isinstance(b[key], dict):
Sops._merge_dicts(a[key], b[key], path + [str(key)])
elif a[key] == b[key]:
pass # same leaf value
else:
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
else:
a[key] = b[key]
return a