From 68c86bb1fdbbf5956d527a9ba5829fce510d48a3 Mon Sep 17 00:00:00 2001 From: Kevin O'Gorman Date: Tue, 20 Apr 2021 19:09:21 -0400 Subject: [PATCH] Adds option to restore from backup file already on server. when the '--no-transfer' argument is used with `./securedrop-admin restore`, instead of transferring the backup tarball to the Application Server, the local copy of the tarball will be compared with an expected remote file of the same name in `/tmp` on the server. If checksums match, the remote copy will be used to perform the backup. This is intended to address cases where backups are too large to reliably transmit over Tor via the Ansible synchronize module. Instead, backups can be copied to the server using rsync, or via an encrypted transfer USB. --- admin/securedrop_admin/__init__.py | 23 ++++++++------- .../roles/restore/defaults/main.yml | 1 + .../roles/restore/tasks/perform_restore.yml | 29 +++++++++++++++++++ 3 files changed, 43 insertions(+), 10 deletions(-) diff --git a/admin/securedrop_admin/__init__.py b/admin/securedrop_admin/__init__.py index 7c4f7f0fa9..c72aa7b897 100755 --- a/admin/securedrop_admin/__init__.py +++ b/admin/securedrop_admin/__init__.py @@ -883,25 +883,23 @@ def restore_securedrop(args: argparse.Namespace) -> int: # Would like readable output if there's a problem os.environ["ANSIBLE_STDOUT_CALLBACK"] = "debug" - ansible_cmd_full_restore = [ + ansible_cmd = [ 'ansible-playbook', os.path.join(args.ansible_path, 'securedrop-restore.yml'), '-e', - "restore_file='{}'".format(restore_file_basename), ] - ansible_cmd_skip_tor = [ - 'ansible-playbook', - os.path.join(args.ansible_path, 'securedrop-restore.yml'), - '-e', - "restore_file='{}' restore_skip_tor='True'".format(restore_file_basename), + ansible_cmd_extras = [ + "restore_file='{}'".format(restore_file_basename), ] if args.restore_skip_tor: - ansible_cmd = ansible_cmd_skip_tor - else: - ansible_cmd = ansible_cmd_full_restore + ansible_cmd_extras.append("restore_skip_tor='True'") + + if args.restore_manual_transfer: + ansible_cmd_extras.append("restore_manual_transfer='True'") + ansible_cmd.append(' '.join(ansible_cmd_extras)) return subprocess.check_call(ansible_cmd, cwd=args.ansible_path) @@ -1161,6 +1159,11 @@ class ArgParseFormatterCombo(argparse.ArgumentDefaultsHelpFormatter, dest='restore_skip_tor', help="Preserve the server's current Tor config") + parse_restore.add_argument("--no-transfer", default=False, + action='store_true', + dest='restore_manual_transfer', + help="Restore using a backup file already present on the server") + parse_update = subparsers.add_parser('update', help=update.__doc__) parse_update.set_defaults(func=update) diff --git a/install_files/ansible-base/roles/restore/defaults/main.yml b/install_files/ansible-base/roles/restore/defaults/main.yml index ef84b64722..a530915e33 100644 --- a/install_files/ansible-base/roles/restore/defaults/main.yml +++ b/install_files/ansible-base/roles/restore/defaults/main.yml @@ -3,3 +3,4 @@ # backup. add the `--preserve-tor-config` to preserve the server's existing config. restore_skip_tor: False +restore_manual_transfer: False diff --git a/install_files/ansible-base/roles/restore/tasks/perform_restore.yml b/install_files/ansible-base/roles/restore/tasks/perform_restore.yml index 2da23dca7d..cf5c532f2d 100644 --- a/install_files/ansible-base/roles/restore/tasks/perform_restore.yml +++ b/install_files/ansible-base/roles/restore/tasks/perform_restore.yml @@ -53,11 +53,40 @@ - "More info: {{ compare_result.stdout }}" when: not restore_skip_tor +- name: Calculate local backup file checksum + connection: local + become: no + stat: + path: "{{ restore_file }}" + checksum_algorithm: sha256 + register: local_backup_file + when: restore_manual_transfer + +- name: Calculate remote backup file checksum + stat: + path: "/tmp/{{ restore_file }}" + checksum_algorithm: sha256 + register: remote_backup_file + when: restore_manual_transfer + +- name: Verify that local and remote backup file checksums match + assert: + that: + - "remote_backup_file.stat.checksum == local_backup_file.stat.checksum" + fail_msg: + - "Checksum comparison for the local and remote copies of the backup file failed." + - "When using the --no-transfer flag. the same file must be present both locally" + - "in `install_files/ansible-base and on the Application Server in `/tmp`, to allow" + - "for config verification. Please copy the backup file into place on the" + - "Application Server before proceeding, or restore without the --no-transfer flag." + when: restore_manual_transfer + - name: Copy backup to application server synchronize: src: "{{ restore_file }}" dest: /tmp/{{ restore_file }} partial: yes + when: not restore_manual_transfer - name: Extract backup unarchive: