From 94e3f989c1aabbee67d3f1d5a0e638164487161b Mon Sep 17 00:00:00 2001
From: Kevin O'Gorman <kog@freedom.press>
Date: Mon, 29 Mar 2021 13:40:34 -0400
Subject: [PATCH] Updated restore playbook to preserve server-side SSH
 configuration

---
 .../roles/restore/tasks/cleanup_v2.yml        |  40 +++++
 .../ansible-base/roles/restore/tasks/main.yml | 162 +-----------------
 .../roles/restore/tasks/perform_restore.yml   | 102 +++++++++++
 .../roles/restore/tasks/update_tor.yml        |  16 ++
 4 files changed, 165 insertions(+), 155 deletions(-)
 create mode 100644 install_files/ansible-base/roles/restore/tasks/cleanup_v2.yml
 create mode 100644 install_files/ansible-base/roles/restore/tasks/perform_restore.yml
 create mode 100644 install_files/ansible-base/roles/restore/tasks/update_tor.yml

diff --git a/install_files/ansible-base/roles/restore/tasks/cleanup_v2.yml b/install_files/ansible-base/roles/restore/tasks/cleanup_v2.yml
new file mode 100644
index 0000000000..cd90f5d0e2
--- /dev/null
+++ b/install_files/ansible-base/roles/restore/tasks/cleanup_v2.yml
@@ -0,0 +1,40 @@
+---
+- name: Copy disable_v2.py script
+  copy:
+    src: "{{ role_path }}/files/disable_v2.py"
+    dest: /opt/disable_v2.py
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Execute disable_v2 script
+  command: python3 /opt/disable_v2.py /etc/tor/torrc /etc/tor/torrc
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Remove v2 tor source directory
+  file:
+    state: absent
+    path: /var/lib/tor/services/source
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Remove v2 tor journalist directory
+  file:
+    state: absent
+    path: /var/lib/tor/services/journalist
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Remove v2 tor ssh directory
+  file:
+    state: absent
+    path: /var/lib/tor/services/ssh
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Remove v2 source_url application file
+  file:
+    state: absent
+    path: /var/lib/securedrop/source_v2_url
+  when: ("V3 services only" in compare_result.stdout)
+
+- name: Remove disable_v2.py script
+  file:
+    state: absent
+    path: /opt/disable_v2.py
+  when: ("V3 services only" in compare_result.stdout)
diff --git a/install_files/ansible-base/roles/restore/tasks/main.yml b/install_files/ansible-base/roles/restore/tasks/main.yml
index 3cd847b6de..a6e3eb89fe 100644
--- a/install_files/ansible-base/roles/restore/tasks/main.yml
+++ b/install_files/ansible-base/roles/restore/tasks/main.yml
@@ -1,159 +1,11 @@
 ---
-- name: Create temporary directory for Tor configuration check
-  connection: local
-  become: no
-  tempfile:
-    state: directory
-  register: torrc_check_dir
+- name: Apply backup to Application Server
+  include: perform_restore.yml
 
-- name: Fetch current Tor configuration from app server
-  become: no
-  fetch:
-    src: /etc/tor/torrc
-    dest: "{{ torrc_check_dir.path }}"
-
-- name: Create directory to hold the Tor configuration from the backup
-  connection: local
-  become: no
-  file:
-    path: "{{ torrc_check_dir.path }}/backup"
-    state: directory
-
-- name: Extract Tor configuration from backup
-  connection: local
-  become: no
-  unarchive:
-    dest: "{{ torrc_check_dir.path }}/backup/"
-    src: "{{ restore_file }}"
-    extra_opts:
-      - "etc/tor/torrc"
-
-- name: Check for Tor configuration differences between the backup and server
-  connection: local
-  become: no
-  command: "python {{ role_path }}/files/compare_torrc.py {{ torrc_check_dir.path }}"
-  ignore_errors: yes
-  register: compare_result
-
-- name: Remove temporary directory for Tor configuration check
-  connection: local
-  become: no
-  file:
-    path: "{{ torrc_check_dir.path }}"
-    state: absent
-  when: torrc_check_dir.path is defined
-
-- name: Verify that the backup Tor config is compatible with the server Tor config
-  assert:
-    that:
-      - "'Valid configuration' in compare_result.stdout"
-    fail_msg:
-      - "This backup's tor configuration cannot be applied on this server."
-      - "A data-only restore can be applied using the --preserve-tor-config argument"
-      - "More info: {{ compare_result.stdout }}"
+- name: Remove deprecated v2 onion service configuration
+  include: cleanup_v2.yml
   when: not restore_skip_tor
 
-- name: Copy backup to application server
-  synchronize:
-    src: "{{ restore_file }}"
-    dest: /tmp/{{ restore_file }}
-    partial: yes
-
-- name: Extract backup
-  unarchive:
-    dest: /
-    remote_src: yes
-    src: "/tmp/{{ restore_file}}"
-  when: (not restore_skip_tor) and
-        ("V3 services only" not in compare_result.stdout)
-
-- name: Extract backup, using v3 services only
-  unarchive:
-    dest: /
-    remote_src: yes
-    src: "/tmp/{{ restore_file}}"
-    exclude: "var/lib/tor/services/source,var/lib/tor/services/journalist,var/lib/tor/services/ssh"
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Extract backup, skipping tor service configuration
-  unarchive:
-    dest: /
-    remote_src: yes
-    src: "/tmp/{{ restore_file}}"
-    exclude: "var/lib/tor,etc/tor/torrc"
-  when: restore_skip_tor
-
-- name: Reconfigure securedrop-app-code
-  command: dpkg-reconfigure securedrop-app-code
-
-- name: Reconfigure securedrop-config
-  command: dpkg-reconfigure securedrop-config
-
-- name: Reload Apache service
-  service:
-    name: apache2
-    state: reloaded
-
-- name: Copy disable_v2.py script
-  copy:
-    src: "{{ role_path }}/files/disable_v2.py"
-    dest: /opt/disable_v2.py
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Execute disable_v2 script
-  command: python3 /opt/disable_v2.py /etc/tor/torrc /etc/tor/torrc
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Remove v2 tor source directory
-  file:
-    state: absent
-    path: /var/lib/tor/services/source
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Remove v2 tor journalist directory
-  file:
-    state: absent
-    path: /var/lib/tor/services/journalist
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Remove v2 tor ssh directory
-  file:
-    state: absent
-    path: /var/lib/tor/services/ssh
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Remove v2 source_url application file
-  file:
-    state: absent
-    path: /var/lib/securedrop/source_v2_url
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Remove disable_v2.py script
-  file:
-    state: absent
-    path: /opt/disable_v2.py
-  when: (not restore_skip_tor) and
-        ("V3 services only" in compare_result.stdout)
-
-- name: Reload Tor service
-  service:
-    name: tor
-    state: reloaded
-  async: 60
-  poll: 0
-  register: tor_reload_job
-
-- name: Wait for Tor reload
-  async_status:
-    jid: "{{ tor_reload_job.ansible_job_id }}"
-  register: tor_reload
-  until: tor_reload.finished
-  retries: 6
-  delay: 10
+- name: Restart Tor
+  include: update_tor.yml
+  when: not restore_skip_tor
diff --git a/install_files/ansible-base/roles/restore/tasks/perform_restore.yml b/install_files/ansible-base/roles/restore/tasks/perform_restore.yml
new file mode 100644
index 0000000000..2da23dca7d
--- /dev/null
+++ b/install_files/ansible-base/roles/restore/tasks/perform_restore.yml
@@ -0,0 +1,102 @@
+---
+- name: Create temporary directory for Tor configuration check
+  connection: local
+  become: no
+  tempfile:
+    state: directory
+  register: torrc_check_dir
+
+- name: Fetch current Tor configuration from app server
+  become: no
+  fetch:
+    src: /etc/tor/torrc
+    dest: "{{ torrc_check_dir.path }}"
+
+- name: Create directory to hold the Tor configuration from the backup
+  connection: local
+  become: no
+  file:
+    path: "{{ torrc_check_dir.path }}/backup"
+    state: directory
+
+- name: Extract Tor configuration from backup
+  connection: local
+  become: no
+  unarchive:
+    dest: "{{ torrc_check_dir.path }}/backup/"
+    src: "{{ restore_file }}"
+    extra_opts:
+      - "etc/tor/torrc"
+
+- name: Check for Tor configuration differences between the backup and server
+  connection: local
+  become: no
+  command: "python {{ role_path }}/files/compare_torrc.py {{ torrc_check_dir.path }}"
+  ignore_errors: yes
+  register: compare_result
+
+- name: Remove temporary directory for Tor configuration check
+  connection: local
+  become: no
+  file:
+    path: "{{ torrc_check_dir.path }}"
+    state: absent
+  when: torrc_check_dir.path is defined
+
+- name: Verify that the backup Tor config is compatible with the server Tor config
+  assert:
+    that:
+      - "'Valid configuration' in compare_result.stdout"
+    fail_msg:
+      - "This backup's tor configuration cannot be applied on this server."
+      - "A data-only restore can be applied using the --preserve-tor-config argument"
+      - "More info: {{ compare_result.stdout }}"
+  when: not restore_skip_tor
+
+- name: Copy backup to application server
+  synchronize:
+    src: "{{ restore_file }}"
+    dest: /tmp/{{ restore_file }}
+    partial: yes
+
+- name: Extract backup
+  unarchive:
+    dest: /
+    remote_src: yes
+    src: "/tmp/{{ restore_file}}"
+    exclude:
+      - "var/lib/tor/services/ssh"
+      - "var/lib/tor/services/sshv3"
+  when: (not restore_skip_tor) and
+        ("V3 services only" not in compare_result.stdout)
+
+- name: Extract backup, using v3 services only
+  unarchive:
+    dest: /
+    remote_src: yes
+    src: "/tmp/{{ restore_file}}"
+    exclude:
+      - "var/lib/tor/services/source,var/lib/tor/services/journalist"
+      - "var/lib/tor/services/ssh"
+      -  "var/lib/tor/services/sshv3"
+  when: (not restore_skip_tor) and
+        ("V3 services only" in compare_result.stdout)
+
+- name: Extract backup, skipping tor service configuration
+  unarchive:
+    dest: /
+    remote_src: yes
+    src: "/tmp/{{ restore_file}}"
+    exclude: "var/lib/tor,etc/tor/torrc"
+  when: restore_skip_tor
+
+- name: Reconfigure securedrop-app-code
+  command: dpkg-reconfigure securedrop-app-code
+
+- name: Reconfigure securedrop-config
+  command: dpkg-reconfigure securedrop-config
+
+- name: Reload Apache service
+  service:
+    name: apache2
+    state: reloaded
diff --git a/install_files/ansible-base/roles/restore/tasks/update_tor.yml b/install_files/ansible-base/roles/restore/tasks/update_tor.yml
new file mode 100644
index 0000000000..9d24c23363
--- /dev/null
+++ b/install_files/ansible-base/roles/restore/tasks/update_tor.yml
@@ -0,0 +1,16 @@
+---
+- name: Reload Tor service
+  service:
+    name: tor
+    state: reloaded
+  async: 60
+  poll: 0
+  register: tor_reload_job
+
+- name: Wait for Tor reload
+  async_status:
+    jid: "{{ tor_reload_job.ansible_job_id }}"
+  register: tor_reload
+  until: tor_reload.finished
+  retries: 6
+  delay: 10