Browse Source

read a passphrase from a file descriptor

Read a passpharase from a file descriptor specified in the
BORG_PASSPHRASE_FD environment variable.
Łukasz Stelmach 6 years ago
parent
commit
548355125e
2 changed files with 21 additions and 1 deletions
  1. 7 0
      docs/usage_general.rst.inc
  2. 14 1
      src/borg/crypto/key.py

+ 7 - 0
docs/usage_general.rst.inc

@@ -174,6 +174,13 @@ General:
         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_PASSPHRASE_FD
+        When set, specifies a file descriptor to read a passphrase
+        from. Programs starting borg may choose to open an anonymous pipe
+        and use it to pass a passphrase. This is safer than passing via
+        BORG_PASSPHRASE, because on some systems (e.g. Linux) environment
+        can be examined by other processes.
+        If BORG_PASSPHRASE or BORG_PASSCOMMAND are also set, they take precedence.
     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 and BORG_PASSCOMMAND will also

+ 14 - 1
src/borg/crypto/key.py

@@ -36,7 +36,7 @@ class NoPassphraseFailure(Error):
 
 
 class PassphraseWrong(Error):
-    """passphrase supplied in BORG_PASSPHRASE or by BORG_PASSCOMMAND is incorrect."""
+    """passphrase supplied in BORG_PASSPHRASE, by BORG_PASSCOMMAND or via BORG_PASSPHRASE_FD is incorrect."""
 
 
 class PasscommandFailure(Error):
@@ -429,6 +429,9 @@ class Passphrase(str):
         passphrase = cls.env_passcommand()
         if passphrase is not None:
             return passphrase
+        passphrase = cls.fd_passphrase()
+        if passphrase is not None:
+            return passphrase
 
     @classmethod
     def env_passcommand(cls, default=None):
@@ -442,6 +445,16 @@ class Passphrase(str):
                 raise PasscommandFailure(e)
             return cls(passphrase.rstrip('\n'))
 
+    @classmethod
+    def fd_passphrase(cls):
+        try:
+            fd = int(os.environ.get('BORG_PASSPHRASE_FD'))
+        except (ValueError, TypeError):
+            return None
+        with os.fdopen(fd, mode='r') as f:
+            passphrase = f.read()
+        return cls(passphrase.rstrip('\n'))
+
     @classmethod
     def env_new_passphrase(cls, default=None):
         return cls._env_passphrase('BORG_NEW_PASSPHRASE', default)