duplicity-team team mailing list archive
-
duplicity-team team
-
Mailing list archive
-
Message #00577
[Merge] lp:~lekensteyn/duplicity/multipass into lp:duplicity
Lekensteyn has proposed merging lp:~lekensteyn/duplicity/multipass into lp:duplicity.
Requested reviews:
duplicity-team (duplicity-team)
Related bugs:
Bug #684025 in ProjectStats: "Funktionalität von aus den Games in die Tabellen verlagern"
https://bugs.launchpad.net/projectstats/+bug/684025
Bug #793096 in Duplicity: "Allow to pass different passwords for --sign-key and --encrypt-key"
https://bugs.launchpad.net/duplicity/+bug/793096
For more details, see:
https://code.launchpad.net/~lekensteyn/duplicity/multipass/+merge/64307
Enables the use of a different passphrase for the GPG signing and encryption key. (Closes #793096)
Allows to specify a different secret keyring for the GPG encryption key.
Updated manual page with the above two changes.
Do not keep asking for a passphrase confirmation, but start over on asking the passphrase to prevent an infinite loop. (Closes #680425)
--
https://code.launchpad.net/~lekensteyn/duplicity/multipass/+merge/64307
Your team duplicity-team is requested to review the proposed merge of lp:~lekensteyn/duplicity/multipass into lp:duplicity.
=== modified file 'duplicity-bin'
--- duplicity-bin 2011-04-16 21:25:32 +0000
+++ duplicity-bin 2011-06-11 16:21:34 +0000
@@ -58,7 +58,7 @@
exit_val = None
-def get_passphrase(n, action):
+def get_passphrase(n, action, for_signing = False):
"""
Check to make sure passphrase is indeed needed, then get
the passphrase from environment, from gpg-agent, or user
@@ -68,13 +68,23 @@
verification for the time being.
@type n: int
- @param n: action to perform
+ @param n: verification level for a passphrase being requested
+ @type action: string
+ @param action: action to perform
+ @type for_signing: boolean
+ @param for_signing: true if the passphrase is for a signing key, false if not
@rtype: string
@return: passphrase
"""
# First try the environment
try:
+ if for_signing:
+ return os.environ['SIGN_PASSPHRASE']
+ except KeyError:
+ pass
+
+ try:
return os.environ['PASSPHRASE']
except KeyError:
pass
@@ -90,7 +100,7 @@
if not globals.encryption or globals.use_agent:
return ""
- # no passphrase if --list-current
+ # no passphrase needed if --list-current
elif (action == "list-current"):
return ""
@@ -121,12 +131,23 @@
log.Info("PASSPHRASE variable not set, asking user.")
while 1:
if n == 2:
- pass1 = globals.gpg_profile.passphrase
- else:
- if globals.gpg_profile.passphrase:
+ if for_signing:
+ pass1 = globals.gpg_profile.signing_passphrase
+ else:
pass1 = globals.gpg_profile.passphrase
+ else:
+ if for_signing:
+ if globals.gpg_profile.signing_passphrase and n != 3:
+ pass1 = globals.gpg_profile.signing_passphrase
+ else:
+ pass1 = getpass.getpass("GnuPG passphrase for signing key: ")
else:
- pass1 = getpass.getpass("GnuPG passphrase: ")
+ # do not hang in an infinite loop if the first passphrase
+ # was wrong, just ask again
+ if globals.gpg_profile.passphrase and n != 3:
+ pass1 = globals.gpg_profile.passphrase
+ else:
+ pass1 = getpass.getpass("GnuPG passphrase: ")
if n == 1:
pass2 = pass1
@@ -953,6 +974,7 @@
if not globals.dry_run:
log.Notice(_("Synchronizing remote metadata to local cache..."))
if local_missing and (rem_needpass or loc_needpass):
+ # password for the --encrypt-key
globals.gpg_profile.passphrase = get_passphrase(1, "sync")
for fn in local_spurious:
remove_local(fn)
@@ -1135,19 +1157,16 @@
else:
log.FatalError("Unable to locate pydevd.", log.ErrorCode.user_error)
- # get the passphrase if we need to based on action/options
- globals.gpg_profile.passphrase = get_passphrase(1, action)
-
# log some debugging status info
log_startup_parms(log.INFO)
# check for disk space and available file handles
check_resources(action)
- # check archive synch with remote, fix if needed
+ # synchronize local and remote, the passphrase is requested if needed
sync_archive()
- # get current collection status
+ # get current collection status, no passphrase needed
col_stats = collections.CollectionsStatus(globals.backend,
globals.archive_dir).set_values()
@@ -1197,6 +1216,11 @@
os.umask(077)
+ # the passphrase is not always needed for full/inc; symmetric crypto is another story
+ if not action in ["full", "inc"] or not globals.gpg_profile.recipients:
+ # get the passphrase if we need to based on action/options
+ globals.gpg_profile.passphrase = get_passphrase(1, action)
+
if action == "restore":
restore(col_stats)
elif action == "verify":
@@ -1215,16 +1239,28 @@
sync_archive(col_stats)
else:
assert action == "inc" or action == "full", action
+ # the passphrase for full and inc is used by --sign-key
+ # the sign key can have a different passphrase than the encrypt
+ # key, therefore request a passphrase
+ if globals.gpg_profile.sign_key:
+ globals.gpg_profile.signing_passphrase = get_passphrase(3, action, True)
+
if action == "full":
- globals.gpg_profile.passphrase = get_passphrase(2, action)
+ if not globals.gpg_profile.sign_key:
+ globals.gpg_profile.passphrase = get_passphrase(2, action)
full_backup(col_stats)
else: # attempt incremental
sig_chain = check_sig_chain(col_stats)
+ # action == "inc" was requested, but no full backup is available
if not sig_chain:
- globals.gpg_profile.passphrase = get_passphrase(2, action)
+ if not globals.gpg_profile.sign_key:
+ globals.gpg_profile.passphrase = get_passphrase(2, action)
full_backup(col_stats)
else:
if not globals.restart:
+ # only ask for a passphrase if there was a previous backup
+ if col_stats.all_backup_chains:
+ globals.gpg_profile.passphrase = get_passphrase(1, action)
check_last_manifest(col_stats) # not needed for full backup
incremental_backup(sig_chain)
globals.backend.close()
=== modified file 'duplicity.1'
--- duplicity.1 2011-04-04 15:50:11 +0000
+++ duplicity.1 2011-06-11 16:21:34 +0000
@@ -74,6 +74,13 @@
passphrase to give to GnuPG. If this is not set, the user will be
prompted for the passphrase.
+When signing the backup with a GPG key as specified by
+.B --sign-key
+, the environment variable SIGN_PASSPHRASE can be used to set a
+passphrase for this key. If SIGN_PASSPHRASE is not set but PASSPHRASE is set,
+the latter will be used. Otherwise, if no passphrase is available, the user
+will be prompted for it.
+
If you are backing up the root directory /, remember to --exclude
/proc, or else duplicity will probably crash on the weird stuff in
there.
@@ -299,6 +306,15 @@
symmetric (traditional) encryption. Can be specified multiple times.
.TP
+.BI "--encrypt-secret-keyring " filename
+This option can only be used with
+.BR --encrypt-key ,
+and changes the path to the secret keyring for the encrypt key to
+.I filename
+This keyring is not used when creating a backup. If not specified, the
+default secret keyring is used which is usually located at .gnupg/secring.gpg
+
+.TP
.BI "--exclude " shell_pattern
Exclude the file or files matched by
.IR shell_pattern .
=== modified file 'duplicity/commandline.py'
--- duplicity/commandline.py 2011-05-11 13:49:54 +0000
+++ duplicity/commandline.py 2011-06-11 16:21:34 +0000
@@ -246,6 +246,9 @@
dest="", action="callback",
callback=lambda o, s, v, p: globals.gpg_profile.recipients.append(v)) #@UndefinedVariable
+ # secret keyring in which the private encrypt key can be found
+ parser.add_option("--encrypt-secret-keyring", type="string", metavar=_("path"))
+
# TRANSL: Used in usage help to represent a "glob" style pattern for
# matching one or more files, as described in the documentation.
# Example:
=== modified file 'duplicity/gpg.py'
--- duplicity/gpg.py 2011-04-16 20:25:01 +0000
+++ duplicity/gpg.py 2011-06-11 16:21:34 +0000
@@ -66,7 +66,9 @@
assert recipients # can only sign with asym encryption
self.passphrase = passphrase
+ self.signing_passphrase = passphrase
self.sign_key = sign_key
+ self.encrypt_secring = None
if recipients is not None:
assert type(recipients) is types.ListType # must be list, not tuple
self.recipients = recipients
@@ -111,6 +113,11 @@
if profile.sign_key:
gnupg.options.default_key = profile.sign_key
cmdlist.append("--sign")
+ # the passphrase for --encrypt-key may not be the same as --sign-key
+ if encrypt and profile.recipients:
+ passphrase = profile.signing_passphrase
+ else:
+ passphrase = profile.passphrase
if encrypt:
if profile.recipients:
@@ -124,17 +131,20 @@
attach_fhs={'stdout': encrypt_path.open("wb"),
'stderr': self.stderr_fp,
'logger': self.logger_fp})
- p1.handles['passphrase'].write(profile.passphrase)
+ p1.handles['passphrase'].write(passphrase)
p1.handles['passphrase'].close()
self.gpg_input = p1.handles['stdin']
else:
+ if profile.recipients and profile.encrypt_secring:
+ cmdlist.append('--secret-keyring')
+ cmdlist.append(profile.encrypt_secring)
self.status_fp = tempfile.TemporaryFile()
p1 = gnupg.run(['--decrypt'], create_fhs=['stdout', 'passphrase'],
attach_fhs={'stdin': encrypt_path.open("rb"),
'status': self.status_fp,
'stderr': self.stderr_fp,
'logger': self.logger_fp})
- p1.handles['passphrase'].write(profile.passphrase)
+ p1.handles['passphrase'].write(passphrase)
p1.handles['passphrase'].close()
self.gpg_output = p1.handles['stdout']
self.gpg_process = p1
Follow ups