فهرست منبع

Added BORG_PASSCOMMAND environment variable (#2573)

TuXicc 8 سال پیش
والد
کامیت
578b76af3a
4فایلهای تغییر یافته به همراه39 افزوده شده و 9 حذف شده
  1. 5 5
      docs/faq.rst
  2. 2 0
      docs/quickstart.rst
  3. 10 2
      docs/usage_general.rst.inc
  4. 22 2
      src/borg/crypto/key.py

+ 5 - 5
docs/faq.rst

@@ -257,11 +257,11 @@ Security
 How can I specify the encryption passphrase programmatically?
 -------------------------------------------------------------
 
-The encryption passphrase can be specified programmatically using the
-`BORG_PASSPHRASE` environment variable. This is convenient when setting up
-automated encrypted backups. Another option is to use
-key file based encryption with a blank passphrase. See
-:ref:`encrypted_repos` for more details.
+The encryption passphrase or a command to retrieve the passphrase can be
+specified programmatically using the `BORG_PASSPHRASE` or `BORG_PASSCOMMAND`
+environment variables. This is convenient when setting up automated encrypted
+backups. Another option is to use key file based encryption with a blank passphrase.
+See :ref:`encrypted_repos` for more details.
 
 .. _password_env:
 .. note:: Be careful how you set the environment; using the ``env``

+ 2 - 0
docs/quickstart.rst

@@ -86,6 +86,8 @@ backed up and that the ``prune`` command is keeping and deleting the correct bac
 
     # Setting this, so you won't be asked for your repository passphrase:
     export BORG_PASSPHRASE='XYZl0ngandsecurepa_55_phrasea&&123'
+    # or this to ask an external program to supply the passphrase:
+    export BORG_PASSCOMMAND='pass show backup'
 
     # some helpers and error handling:
     function info  () { echo -e "\n"`date` $@"\n" >&2; }

+ 10 - 2
docs/usage_general.rst.inc

@@ -131,12 +131,20 @@ General:
         can either leave it away or abbreviate as `::`, if a positional parameter is required.
     BORG_PASSPHRASE
         When set, use the value to answer the passphrase question for encrypted repositories.
-        It is used when a passphrase is needed to access a encrypted repo as well as when a new
+        It is used when a passphrase is needed to access an encrypted repo as well as when a new
         passphrase should be initially set when initializing an encrypted repo.
         See also BORG_NEW_PASSPHRASE.
+    BORG_PASSCOMMAND
+        When set, use the standard output of the command (trailing newlines are stripped) to answer the
+        passphrase question for encrypted repositories.
+        It is used when a passphrase is needed to access an encrypted repo as well as when a new
+        passphrase should be initially set when initializing an encrypted repo.
+        If BORG_PASSPHRASE is also set, it takes precedence.
+        See also BORG_NEW_PASSPHRASE.
     BORG_NEW_PASSPHRASE
         When set, use the value to answer the passphrase question when a **new** passphrase is asked for.
-        This variable is checked first. If it is not set, BORG_PASSPHRASE will be checked also.
+        This variable is checked first. If it is not set, BORG_PASSPHRASE and BORG_PASSCOMMAND will also
+        be checked.
         Main usecase for this is to fully automate ``borg change-passphrase``.
     BORG_DISPLAY_PASSPHRASE
         When set, use the value to answer the "display the passphrase for verification" question when defining a new passphrase for encrypted repositories.

+ 22 - 2
src/borg/crypto/key.py

@@ -3,6 +3,7 @@ import getpass
 import os
 import sys
 import textwrap
+import subprocess
 from binascii import a2b_base64, b2a_base64, hexlify
 from hashlib import sha256, sha512, pbkdf2_hmac
 from hmac import HMAC, compare_digest
@@ -29,7 +30,11 @@ PREFIX = b'\0' * 8
 
 
 class PassphraseWrong(Error):
-    """passphrase supplied in BORG_PASSPHRASE is incorrect"""
+    """passphrase supplied in BORG_PASSPHRASE or by BORG_PASSCOMMAND is incorrect."""
+
+
+class PasscommandFailure(Error):
+    """passcommand supplied in BORG_PASSCOMMAND failed: {}"""
 
 
 class PasswordRetriesExceeded(Error):
@@ -413,7 +418,22 @@ class Passphrase(str):
 
     @classmethod
     def env_passphrase(cls, default=None):
-        return cls._env_passphrase('BORG_PASSPHRASE', default)
+        passphrase = cls._env_passphrase('BORG_PASSPHRASE', default)
+        if passphrase is not None:
+            return passphrase
+        passphrase = cls.env_passcommand()
+        if passphrase is not None:
+            return passphrase
+
+    @classmethod
+    def env_passcommand(cls, default=None):
+        passcommand = os.environ.get('BORG_PASSCOMMAND', None)
+        if passcommand is not None:
+            try:
+                passphrase = subprocess.check_output(passcommand.split(), universal_newlines=True)
+            except (subprocess.CalledProcessError, FileNotFoundError) as e:
+                raise PasscommandFailure(e)
+            return cls(passphrase.rstrip('\n'))
 
     @classmethod
     def env_new_passphrase(cls, default=None):