Repository: freedomofpress/securedrop

Issue:
[v3 onion migration] Modify securedrop-admin tailsconfig to use v3 if available
## Description

This ticket is to modify `securedrop-admin tailsconfig` (used for configuring desktop shortcuts and `torrc` additions in the SecureDrop Tails workstations) to:

* Use v3 addresses if v3 files are present for desktop shortcuts, else continue to use v2
* Add v3 Client auth details to `torrc` if they are present (and NOT add v2 HidServAuth so that it’s easier in the future to rip out v2 without making a round of updates in journalist and admin drives)

Subticket of #2951



Discussion:

Resolving patch:
diff --git a/install_files/ansible-base/roles/tails-config/files/securedrop_init.py b/install_files/ansible-base/roles/tails-config/files/securedrop_init.py
--- a/install_files/ansible-base/roles/tails-config/files/securedrop_init.py
+++ b/install_files/ansible-base/roles/tails-config/files/securedrop_init.py
@@ -7,6 +7,8 @@
 import sys
 import subprocess
 
+from shutil import copyfile
+
 
 # check for root
 if os.geteuid() != 0:
@@ -26,6 +28,16 @@
 path_gui_updater = os.path.join(path_securedrop_root,
                                 'journalist_gui/SecureDropUpdater')
 
+paths_v3_authfiles = {
+        "app-journalist": os.path.join(path_securedrop_root,
+                                       'install_files/ansible-base/app-journalist.auth_private'),
+        "app-ssh": os.path.join(path_securedrop_root,
+                                'install_files/ansible-base/app-ssh.auth_private'),
+        "mon-ssh": os.path.join(path_securedrop_root,
+                                'install_files/ansible-base/mon-ssh.auth_private')
+}
+path_onion_auth_dir = '/var/lib/tor/onion_auth'
+
 # load torrc_additions
 if os.path.isfile(path_torrc_additions):
     with io.open(path_torrc_additions) as f:
@@ -52,11 +64,35 @@
 with io.open(path_torrc, 'w') as f:
     f.write(torrc + torrc_additions)
 
-# reload tor
+# check for v3 aths files
+v3_authfiles_present = False
+for f in paths_v3_authfiles.values():
+    if os.path.isfile(f):
+        v3_authfiles_present = True
+
+# if there are v3 authfiles, make dir and copy them into place
+debian_tor_uid = pwd.getpwnam("debian-tor").pw_uid
+debian_tor_gid = grp.getgrnam("debian-tor").gr_gid
+
+if not os.path.isdir(path_onion_auth_dir):
+    os.mkdir(path_onion_auth_dir)
+
+os.chmod(path_onion_auth_dir, 0o700)
+os.chown(path_onion_auth_dir, debian_tor_uid, debian_tor_gid)
+
+for key, f in paths_v3_authfiles.items():
+    if os.path.isfile(f):
+        filename = os.path.basename(f)
+        new_f = os.path.join(path_onion_auth_dir, filename)
+        copyfile(f, new_f)
+        os.chmod(new_f, 0o400)
+        os.chown(new_f, debian_tor_uid, debian_tor_gid)
+
+# restart tor
 try:
-    subprocess.check_call(['systemctl', 'reload', 'tor@default.service'])
+    subprocess.check_call(['systemctl', 'restart', 'tor@default.service'])
 except subprocess.CalledProcessError:
-    sys.exit('Error reloading Tor')
+    sys.exit('Error restarting Tor')
 
 # Turn off "automatic-decompression" in Nautilus to ensure the original
 # submission filename is restored (see


Test patch:
diff --git a/admin/tests/test_securedrop-admin.py b/admin/tests/test_securedrop-admin.py
--- a/admin/tests/test_securedrop-admin.py
+++ b/admin/tests/test_securedrop-admin.py
@@ -665,7 +665,7 @@ def test_only_v3_onion_services(self, tmpdir):
                                   ansible_path='.',
                                   app_path=dirname(__file__))
         site_config = securedrop_admin.SiteConfig(args)
-        with open("app-source-ths", "w") as fobj:
+        with open("app-sourcev3-ths", "w") as fobj:
             fobj.write("a" * 56 + ".onion\n")
         site_config.update_onion_version_config()
         site_config.save()
@@ -676,7 +676,7 @@ def test_only_v3_onion_services(self, tmpdir):
         v2_onion_services: false
         v3_onion_services: true
         """)
-        os.remove("app-source-ths")
+        os.remove("app-sourcev3-ths")
         assert expected == data
 
     def test_validate_gpg_key(self, caplog):