Browse Source

Add an "ask_for_password" option to the KeePassXC credential hook for disabling KeePassXC's password prompt (#1181).

Dan Helfman 2 weeks ago
parent
commit
0e9193c46b

+ 2 - 0
NEWS

@@ -1,4 +1,6 @@
 2.0.12.dev0
+ * #1181: Add an "ask_for_password" option to the KeePassXC credential hook for disabling
+   KeePassXC's password prompt, e.g. if you're only using a key file to decrypt your database.
  * In the SQLite database hook, run SQLite such that it exits upon encountering an error instead of,
    you know, not doing that.
  * Add documentation on repositories, including SSH, Rclone, S3, and B2:

+ 8 - 0
borgmatic/config/schema.yaml

@@ -3172,6 +3172,14 @@ properties:
                 description: |
                     Command to use instead of "keepassxc-cli".
                 example: /usr/local/bin/keepassxc-cli
+            ask_for_password:
+                type: boolean
+                description: |
+                    Whether keepassxc-cli should prompt the user for a password.
+                    Disabling this is only really useful if you're unlocking
+                    your KeePassXC database with a key file instead of a
+                    password. Defaults to true.
+                example: false
             key_file:
                 type: string
                 description: |

+ 1 - 0
borgmatic/hooks/credential/keepassxc.py

@@ -29,6 +29,7 @@ def load_credential(hook_config, config, credential_parameters):
     command = (
         tuple(shlex.split((hook_config or {}).get('keepassxc_cli_command', 'keepassxc-cli')))
         + ('show', '--show-protected', '--attributes', 'Password')
+        + (('--no-password',) if not hook_config.get('ask_for_password', True) else ())
         + (
             ('--key-file', hook_config['key_file'])
             if hook_config and hook_config.get('key_file')

+ 47 - 0
docs/reference/configuration/credentials/keepassxc.md

@@ -42,6 +42,9 @@ postgresql_databases:
       password: "{credential keepassxc /etc/keys.kdbx database}"
 ```
 
+
+### Custom command
+
 You can also optionally override the `keepassxc-cli` command that borgmatic calls to load
 passwords:
 
@@ -49,3 +52,47 @@ passwords:
 keepassxc:
     keepassxc_cli_command: /usr/local/bin/keepassxc-cli
 ```
+
+Another example:
+
+```yaml
+keepassxc:
+    keepassxc_cli_command: docker exec keepassxc keepassxc-cli
+```
+
+### Key file
+
+KeePassXC supports unlocking a database with a separate [key
+file](https://keepassxc.org/docs/#faq-keyfile-howto) instead of or in addition
+to a password. To configure borgmatic for that, use the `key_file` option:
+
+```yaml
+keepassxc:
+    key_file: /path/to/keyfile
+```
+
+By default, keepassxc-cli prompts the user for the password to unlock a
+database. But if you only want to provide a key file to unlock your database and
+not a password, for instance to support unattended backups, use the
+`ask_for_password` option:
+
+```yaml
+keepassxc:
+    ask_for_password: false
+    key_file: /path/to/keyfile
+```
+
+### YubiKey
+
+KeePassXC also supports unlocking a database with the help of [a
+YubiKey](https://keepassxc.org/docs/#faq-yubikey-2fa). To configure borgmatic
+for that, use the `yubikey` option:
+
+```yaml
+keepassxc:
+    yubikey: 1:7370001
+```
+
+The value here is the YubiKey slot number (e.g., `1` or `2`) and optional serial
+number (e.g., `7370001`) used to access the KeePassXC database. Join the two
+values with a colon, but omit the colon if you're leaving out the serial number.

+ 32 - 0
tests/unit/hooks/credential/test_keepassxc.py

@@ -151,6 +151,38 @@ def test_load_credential_with_key_file():
     )
 
 
+def test_load_credential_with_key_file_and_ask_for_password_false():
+    flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
+        'database.kdbx',
+    )
+    flexmock(module.os.path).should_receive('exists').and_return(True)
+    flexmock(module.borgmatic.execute).should_receive(
+        'execute_command_and_capture_output',
+    ).with_args(
+        (
+            'keepassxc-cli',
+            'show',
+            '--show-protected',
+            '--attributes',
+            'Password',
+            '--no-password',
+            '--key-file',
+            '/path/to/keyfile',
+            'database.kdbx',
+            'mypassword',
+        ),
+    ).and_return('password').once()
+
+    assert (
+        module.load_credential(
+            hook_config={'key_file': '/path/to/keyfile', 'ask_for_password': False},
+            config={},
+            credential_parameters=('database.kdbx', 'mypassword'),
+        )
+        == 'password'
+    )
+
+
 def test_load_credential_with_yubikey():
     flexmock(module.os.path).should_receive('expanduser').with_args('database.kdbx').and_return(
         'database.kdbx',