Move vault key and path detection logic into the exporter module
Marco Ricci

Marco Ricci commited on 2024-08-18 09:41:16
Zeige 3 geänderte Dateien mit 75 Einfügungen und 31 Löschungen.


Instead of having both exporters reimplement the logic for determining
the vault key and vault configuration path, implement this functionality
in the `derivepassphrase.exporter` package, which is otherwise still
empty.
... ...
@@ -0,0 +1,58 @@
1
+import os
2
+
3
+
4
+def get_vault_key() -> bytes:
5
+    """Automatically determine the vault master key/password.
6
+
7
+    Query the `VAULT_KEY`, `LOGNAME`, `USER` and `USERNAME` environment
8
+    variables, in that order.  This is the same algorithm as vault uses.
9
+
10
+    Returns:
11
+        The master key/password.  This is generally used as input to
12
+        a key-derivation function to determine the *actual* encryption
13
+        and signing keys for the vault configuration.
14
+
15
+    Raises:
16
+        KeyError:
17
+            We cannot find any of the named environment variables.
18
+            Please set `VAULT_KEY` manually to the desired value.
19
+
20
+    """
21
+
22
+    username = (
23
+        os.environb.get(b'VAULT_KEY')
24
+        or os.environb.get(b'LOGNAME')
25
+        or os.environb.get(b'USER')
26
+        or os.environb.get(b'USERNAME')
27
+    )
28
+    if not username:
29
+        env_var = 'VAULT_KEY'
30
+        raise KeyError(env_var)
31
+    return username
32
+
33
+
34
+def get_vault_path() -> str | bytes | os.PathLike:
35
+    """Automatically determine the vault configuration path.
36
+
37
+    Query the `VAULT_PATH` environment variable, or default to
38
+    `~/.vault`.  This is the same algorithm as vault uses.  If not
39
+    absolute, then `VAULT_PATH` is relative to the home directory.
40
+
41
+    Returns:
42
+        The vault configuration path.  Depending on the vault version,
43
+        this may be a file or a directory.
44
+
45
+    Raises:
46
+        RuntimeError:
47
+            We cannot determine the home directory.  Please set `HOME`
48
+            manually to the correct value.
49
+
50
+    """
51
+
52
+    result = os.path.join(
53
+        os.path.expanduser('~'), os.environ.get('VAULT_PATH', '.vault')
54
+    )
55
+    if result.startswith('~'):
56
+        msg = 'Cannot determine home directory'
57
+        raise RuntimeError(msg)
58
+    return result
... ...
@@ -15,6 +15,8 @@ from cryptography.hazmat.primitives import ciphers, hashes, hmac, padding
15 15
 from cryptography.hazmat.primitives.ciphers import algorithms, modes
16 16
 from cryptography.hazmat.primitives.kdf import pbkdf2
17 17
 
18
+from derivepassphrase import exporter
19
+
18 20
 if TYPE_CHECKING:
19 21
     from collections.abc import Iterator
20 22
 
... ...
@@ -24,15 +26,6 @@ IV_SIZE = 16
24 26
 KEY_SIZE = MAC_SIZE = 32
25 27
 ENCRYPTED_KEYPAIR_SIZE = 128
26 28
 VERSION_SIZE = 1
27
-MASTER_KEYS_KEY = (
28
-    os.getenv('VAULT_KEY')
29
-    or os.getenv('LOGNAME')
30
-    or os.getenv('USER')
31
-    or os.getenv('USERNAME')
32
-)
33
-VAULT_PATH = os.path.join(
34
-    os.path.expanduser('~'), os.getenv('VAULT_PATH', '.vault')
35
-)
36 29
 
37 30
 logger = logging.getLogger(__name__)
38 31
 
... ...
@@ -448,18 +441,21 @@ def store(config: dict[str, Any], path: str, json_contents: bytes) -> None:
448 441
 
449 442
 
450 443
 def export_storeroom_data(
451
-    storeroom_path: str | bytes | os.PathLike = VAULT_PATH,
452
-    master_keys_key: str | bytes | None = MASTER_KEYS_KEY,
444
+    storeroom_path: str | bytes | os.PathLike | None = None,
445
+    master_keys_key: str | bytes | None = None,
453 446
 ) -> dict[str, Any]:
454 447
     """Export the full configuration stored in the storeroom.
455 448
 
456 449
     Args:
457 450
         storeroom_path:
458
-            Path to the storeroom; usually `~/.vault`.
451
+            Path to the storeroom; usually `~/.vault`.  If not given,
452
+            then query [`derivepassphrase.exporter.get_vault_path`][]
453
+            for the value.
459 454
         master_keys_key:
460
-            Encryption key/password for the master keys.  If not set via
461
-            the `VAULT_KEY` environment variable, this usually is the
462
-            user's username.
455
+            Encryption key/password for the master keys, usually the
456
+            username, or passed via the `VAULT_KEY` environment
457
+            variable.  If not given, then query
458
+            [`derivepassphrase.exporter.get_vault_key`][] for the value.
463 459
 
464 460
     Returns:
465 461
         The full configuration, as stored in the storeroom.
... ...
@@ -477,9 +473,10 @@ def export_storeroom_data(
477 473
 
478 474
     """
479 475
 
476
+    if storeroom_path is None:
477
+        storeroom_path = exporter.get_vault_path()
480 478
     if master_keys_key is None:
481
-        msg = 'Cannot determine master key; please set VAULT_KEY'
482
-        raise RuntimeError(msg)
479
+        master_keys_key = exporter.get_vault_key()
483 480
     with open(
484 481
         os.path.join(os.fsdecode(storeroom_path), '.keys'), encoding='utf-8'
485 482
     ) as master_keys_file:
... ...
@@ -15,7 +15,7 @@ from cryptography.hazmat.primitives import ciphers, hashes, hmac, padding
15 15
 from cryptography.hazmat.primitives.ciphers import algorithms, modes
16 16
 from cryptography.hazmat.primitives.kdf import pbkdf2
17 17
 
18
-from derivepassphrase import vault
18
+from derivepassphrase import exporter, vault
19 19
 
20 20
 logger = logging.getLogger(__name__)
21 21
 
... ...
@@ -252,20 +252,9 @@ if __name__ == '__main__':
252 252
     import os
253 253
 
254 254
     logging.basicConfig(level=('DEBUG' if os.getenv('DEBUG') else 'WARNING'))
255
-    with open(
256
-        os.path.join(
257
-            os.path.expanduser('~'), os.getenv('VAULT_PATH', '.vault')
258
-        ),
259
-        'rb',
260
-    ) as infile:
255
+    with open(exporter.get_vault_path(), 'rb') as infile:
261 256
         contents = base64.standard_b64decode(infile.read())
262
-    password = (
263
-        os.getenv('VAULT_KEY')
264
-        or os.getenv('LOGNAME')
265
-        or os.getenv('USER')
266
-        or os.getenv('USERNAME')
267
-    )
268
-    assert password
257
+    password = exporter.get_vault_key()
269 258
     try:
270 259
         config = V03Reader(contents, password).run()
271 260
     except ValueError:
272 261