diff --git a/docs/usage_general.rst.inc b/docs/usage_general.rst.inc index aaefb23c4..eff2dedda 100644 --- a/docs/usage_general.rst.inc +++ b/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 diff --git a/src/borg/crypto/key.py b/src/borg/crypto/key.py index 498eb142f..7bc87fbd1 100644 --- a/src/borg/crypto/key.py +++ b/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 @@ def env_passphrase(cls, default=None): 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 @@ def env_passcommand(cls, default=None): 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)