Added BORG_PASSCOMMAND environment variable (#2573)

This commit is contained in:
TuXicc 2017-05-31 19:25:21 +02:00 committed by enkore
parent 02689174ab
commit 578b76af3a
4 changed files with 39 additions and 9 deletions

View File

@ -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``

View File

@ -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; }

View File

@ -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.

View File

@ -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):