Browse Source

Allow to read secrets from keepass

jherve 1 month ago
parent
commit
ff9d23c86f
1 changed files with 52 additions and 14 deletions
  1. 52 14
      start.py

+ 52 - 14
start.py

@@ -4,6 +4,8 @@ import subprocess
 import socket
 from pathlib import Path, PurePosixPath
 from dataclasses import dataclass
+from typing import Any
+from enum import StrEnum
 
 is_windows = os.name == "nt"
 
@@ -15,21 +17,59 @@ def read_data_sources(hostname: str, login: str) -> list[Path]:
         return [Path(p_str.strip()).expanduser() for p_str in paths]
 
 
+@dataclass
+class KeePass:
+    path: Path
+    bin: str | Path
+
+    def read_entry_attribute(self, key, attribute):
+        return self._exec(["show", "-a", attribute, self.path, key]).strip()
+
+    def _exec(self, args: list[Any]):
+        return subprocess.check_output([self.bin] + args, text=True)
+
+    @classmethod
+    def new(cls, path: Path):
+        binary = Path("C:\\") / "Program Files" / "KeePassXC" / "keepassxc-cli.exe" if is_windows else "keepassxc-cli"
+        return cls(path=path, bin=binary)
+
+
+class SecretType(StrEnum):
+    File="file"
+    KeepassAttribute="keepass-attribute"
+
+
 @dataclass
 class Secret:
-    host_path: Path
     name: str
     mode: int
-
-    def create(self):
-        args = ["podman", "secret", "create", "--replace", self.name, self.host_path]
-        print(args)
-        subprocess.run(args)
+    type: SecretType
+    host_path: Path | None = None
+    key: str | None = None
+    attribute: str | None  = None
+
+    def create(self, keepass: KeePass):
+        match self.type:
+            case SecretType.File:
+                args = ["podman", "secret", "create", "--replace", self.name, self.host_path]
+                print(args)
+                subprocess.run(args)
+
+            case SecretType.KeepassAttribute:
+                value = keepass.read_entry_attribute(self.key, self.attribute)
+                args = ["podman", "secret", "create", "--replace", self.name, "-"]
+                print(args)
+                subprocess.run(args, input=value.encode())
 
     @classmethod
     def from_line(cls, line: str):
-        path = Path(line).expanduser()
-        return cls(host_path=path, name=path.name, mode=0o0400)
+        type_, *args = line.split(",")
+        match (SecretType(type_), *args):
+            case (SecretType.File, path):
+                path = Path(path).expanduser()
+                return cls(host_path=path, name=path.name, mode=0o0400, type=SecretType.File)
+            case (SecretType.KeepassAttribute, key, attribute):
+                return cls(name=key, key=key, mode=0o0400, type=SecretType.KeepassAttribute, attribute=attribute)
 
     @classmethod
     def read_sources(cls, hostname: str, login: str) -> list["Secret"]:
@@ -46,11 +86,6 @@ def to_source_path(path: Path):
     return mount_base / with_drive.relative_to(with_drive.anchor)
 
 
-def create_secrets(secret_sources: list[Secret]):
-    for s in secret_sources:
-        s.create()
-
-
 def start_borgmatic_container(hostname: str, login: str, secret_sources: list[Secret]):
     data_sources = read_data_sources(hostname, login)
     container_name = f"borgmatic_{login}"
@@ -116,7 +151,10 @@ def main():
 
     try:
         if sys.argv[1] == "create_secrets":
-            create_secrets(secret_sources)
+            keepass_path = Path(sys.argv[2])
+            keepass = KeePass.new(keepass_path)
+            for s in secret_sources:
+                s.create(keepass)
 
         elif sys.argv[1] == "start":
             start_borgmatic_container(hostname, login, secret_sources)