From 6e080e67ec8519edce5087f9c3b582d6afe4cb1a Mon Sep 17 00:00:00 2001 From: clerie Date: Sat, 15 Feb 2025 18:25:36 +0100 Subject: [PATCH] Manage secrets with sops --- .sops.yaml | 4 ++ bundles/uberspace-clerie-backup/items.py | 19 +++++++++ libs/bwsops.py | 50 ++++++++++++++++++++++++ nodes.py | 19 +++++++++ secrets.json | 32 +++++++++++++++ 5 files changed, 124 insertions(+) create mode 100644 .sops.yaml create mode 100644 libs/bwsops.py create mode 100644 secrets.json diff --git a/.sops.yaml b/.sops.yaml new file mode 100644 index 0000000..57124ee --- /dev/null +++ b/.sops.yaml @@ -0,0 +1,4 @@ +creation_rules: + - pgp: 0C982F87B7AFBA0F504F90A2629E741947C87928 + + diff --git a/bundles/uberspace-clerie-backup/items.py b/bundles/uberspace-clerie-backup/items.py index 5a8166b..2748cf4 100644 --- a/bundles/uberspace-clerie-backup/items.py +++ b/bundles/uberspace-clerie-backup/items.py @@ -1,5 +1,23 @@ uberspaceify = repo.libs.uberspace.Uberspaceify(node) +repo_config_files = {} +for repo, repo_config in node.metadata.get("clerie-backup/repos").items(): + repo_config_files[f'/home/{node.username}/.config/clerie-backup/{repo}/repo_password'] = { + "content": repo_config["repo_password"], + "mode": "0600", + } + repo_config_files[f'/home/{node.username}/.config/clerie-backup/{repo}/repo_url'] = { + "content": repo_config["repo_url"], + } + repo_config_files[f'/home/{node.username}/.config/clerie-backup/{repo}/auth_username'] = { + "content": repo_config["auth_username"], + } + repo_config_files[f'/home/{node.username}/.config/clerie-backup/{repo}/auth_password'] = { + "content": repo_config["auth_password"], + "mode": "0600", + } + + files = uberspaceify.files({ f'/home/{node.username}/.bwdownloads/clerie-backup.sh': { "content_type": "download", @@ -14,6 +32,7 @@ files = uberspaceify.files({ "action:install_restic", ], }, + **repo_config_files, }) actions = { diff --git a/libs/bwsops.py b/libs/bwsops.py new file mode 100644 index 0000000..95c354e --- /dev/null +++ b/libs/bwsops.py @@ -0,0 +1,50 @@ +from bundlewrap.exceptions import FaultUnavailable +from bundlewrap.utils import Fault +import json +from pathlib import Path +import subprocess + +class BwSops: + def __init__(self, file): + self.file = Path(file) + self._secrets = None + + def _fetch_secrets(self): + try: + p = subprocess.run(["sops", "decrypt", str(self.file)], capture_output=True, check=True) + except subprocess.CalledProcessError as e: + raise FaultUnavailable(f"SOPS: \n{e.stderr}") + + self._secrets = json.loads(p.stdout) + + def _get(self, path): + if self._secrets is None: + self._fetch_secrets() + + out = self._secrets + + _path_traversed = [] + + try: + for name in path: + if not isinstance(out, dict): + raise KeyError(name) + out = out[name] + _path_traversed.append(name) + except KeyError as e: + raise FaultUnavailable( + f"SOPS secret not found: {self.file}:{'/'.join(path)}\n" + f" {e.args[0]} not in {'/'.join(_path_traversed)}" + ) + + return out + + def get(self, path): + if isinstance(path, str): + path = path.split("/") + + return Fault( + f"SOPS {self.file}:{'/'.join(path)}", + self._get, + path=path, + ) diff --git a/nodes.py b/nodes.py index 444e6cc..4b2b800 100644 --- a/nodes.py +++ b/nodes.py @@ -1,3 +1,4 @@ +bws = libs.bwsops.BwSops("secrets.json") uberspaceify = libs.uberspace.Uberspaceify() nodes = uberspaceify.nodes({ @@ -6,6 +7,24 @@ nodes = uberspaceify.nodes({ "uberspace-redirect-clerie", "uberspace-clerie-backup", ), + "metadata": { + "clerie-backup": { + "repos": { + "main-cyan": { + "repo_password": bws.get("clerie.uber.space/clerie-backup/main-cyan/repo_password"), + "repo_url": "https://cyan.backup.clerie.de/clerie.uber.space/main", + "auth_username": "clerie.uber.space", + "auth_password": bws.get("clerie.uber.space/clerie-backup/main-cyan/auth_password"), + }, + "main-magenta": { + "repo_password": bws.get("clerie.uber.space/clerie-backup/main-magenta/repo_password"), + "repo_url": "https://magenta.backup.clerie.de/clerie.uber.space/main", + "auth_username": "clerie.uber.space", + "auth_password": bws.get("clerie.uber.space/clerie-backup/main-magenta/auth_password"), + }, + }, + }, + }, }, "cleriewi.uber.space": { "bundles": ( diff --git a/secrets.json b/secrets.json new file mode 100644 index 0000000..fd2a9dd --- /dev/null +++ b/secrets.json @@ -0,0 +1,32 @@ +{ + "clerie.uber.space": { + "clerie-backup": { + "main-cyan": { + "repo_password": "ENC[AES256_GCM,data:BjyMOOGFdcT6e/zuQKPW88XiKhL26KYKebP0dsevMHmKoxQdIw9R/KS/GIlMlJKsz2wwH6nk5adiLBVD9+7TCA==,iv:1/iT4/QwRuZL3pq2D9nYiqTEpMHtf29r1mAYOzkxQeA=,tag:4LxvLRwfJYd9erPhBKb1Bw==,type:str]", + "auth_password": "ENC[AES256_GCM,data:j6QJeJ7SKKoEnpiO+YxvHfcP38KVop0bfAScRk0mWeBr+8S+3sYMGnPcVbhoHcqJsWyeeEDomhm/GniGza2zLA==,iv:bxL7Hh9jVZ4wc4/69qSmRZZHcCxlEpllIu9zKA4tRy0=,tag:W4cNmbJgDZlqshUN39ieLw==,type:str]" + }, + "main-magenta": { + "repo_password": "ENC[AES256_GCM,data:nDPphjFK0gY77Vq+QLhWRhUM+IxgPD+rIgYS99HCDLS5YVLiPK56kizXRWlOPpq/uUr9bdhLnQAJ8FOBbhvBKA==,iv:HE/v/cVfIrjEXdb820pGJe2hqpYaXiIqDTBr0qTg7AY=,tag:yFpn+0rI9mzGv7vdxUBKjQ==,type:str]", + "auth_password": "ENC[AES256_GCM,data:I3NbXg6Gk4/7z2Ct7L10/mASKxInAFbKN9s9O5Lz6mTfbL35n+cu58H2xP/Nxqu/AJMCcOoXwNHhA/iLGH24RQ==,iv:TwdUR7jnVYK0PnQmMs5P+TrXEtyCxQXbxWPDsmdEPbc=,tag:Sat2Wlb4vYMoH+VRgKjaFQ==,type:str]" + } + } + }, + "sops": { + "kms": null, + "gcp_kms": null, + "azure_kv": null, + "hc_vault": null, + "age": null, + "lastmodified": "2025-02-15T17:21:10Z", + "mac": "ENC[AES256_GCM,data:mYmefpdMExQz5PQ9ksHnlfHdPLWrsHLBPxBpghSyMxQgLNuope1D8FiGXPrFq/98MS9X82f326YddmDgGXylfm3URMHCWVtpiPnVIsKNGIknFQDMk2pDtfdkLd+7/FImRu/5xWrDLmpZ1Wtugpoc1831Jx9ouSqFUdwXeUoVeYA=,iv:q7JZGJYalu264UmOk8nD1+orHIa7tnWWK8IGNsuDE2o=,tag:NOTC8SUwBPvtsDVB1lPyhQ==,type:str]", + "pgp": [ + { + "created_at": "2025-02-15T16:00:02Z", + "enc": "-----BEGIN PGP MESSAGE-----\n\n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\ncwQ6WxacmA==\n=e0B2\n-----END PGP MESSAGE-----", + "fp": "0C982F87B7AFBA0F504F90A2629E741947C87928" + } + ], + "unencrypted_suffix": "_unencrypted", + "version": "3.9.2" + } +} \ No newline at end of file