We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Prompt users to fix upgrade issue for Tails 4.14 - 4.18 (or fix it for them?)
## Description

Tails automatic upgrades were broken between versions 4.14 - 4.18 inclusive ([Tails announcement](https://tails.boum.org/doc/upgrade/error/check/index.en.html#4.18)), and some users on older versions of Tails may not realize that they are missing auto-updates, and that there is a manual step required to fix them. 

We could:
- Make no code changes, and continue to use support messaging channels etc to remind folks of this issue
- Add text to the SecureDrop updater wizard, prompting users to update if their version of Tails is too old, or
- Perform the steps to fix automatic Tails updates ourselves, which (according to the above link) consist of 
```
torsocks curl --silent https://tails.boum.org/isrg-root-x1-cross-signed.pem \
| sudo tee --append /usr/local/etc/ssl/certs/tails.boum.org-CA.pem \
&& systemctl --user restart tails-upgrade-frontend
```  

I'm kind of in favour of the last option, and I can put in a PR for a check at the end of `securedrop_init` right before our GUI updater runs. What do others think?  [edit: filing now so we can discuss inclusion in 2.1.0]

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `install_files/ansible-base/roles/tails-config/files/securedrop_init.py`
Content:
```
1 #!/usr/bin/python3
2 
3 import grp
4 import os
5 import io
6 import pwd
7 import sys
8 import subprocess
9 
10 from shutil import copyfile
11 
12 
13 # check for root
14 if os.geteuid() != 0:
15     sys.exit('You need to run this as root')
16 
17 # paths
18 path_torrc_additions = '/home/amnesia/Persistent/.securedrop/torrc_additions'
19 path_torrc_backup = '/etc/tor/torrc.bak'
20 path_torrc = '/etc/tor/torrc'
21 path_desktop = '/home/amnesia/Desktop/'
22 path_persistent_desktop = '/lib/live/mount/persistence/TailsData_unlocked/dotfiles/Desktop/'  # noqa: E501
23 path_securedrop_root = '/home/amnesia/Persistent/securedrop'
24 path_securedrop_admin_venv = os.path.join(path_securedrop_root,
25                                           'admin/.venv3/bin/python')
26 path_securedrop_admin_init = os.path.join(path_securedrop_root,
27                                           'admin/securedrop_admin/__init__.py')
28 path_gui_updater = os.path.join(path_securedrop_root,
29                                 'journalist_gui/SecureDropUpdater')
30 
31 paths_v3_authfiles = {
32         "app-journalist": os.path.join(path_securedrop_root,
33                                        'install_files/ansible-base/app-journalist.auth_private'),
34         "app-ssh": os.path.join(path_securedrop_root,
35                                 'install_files/ansible-base/app-ssh.auth_private'),
36         "mon-ssh": os.path.join(path_securedrop_root,
37                                 'install_files/ansible-base/mon-ssh.auth_private')
38 }
39 path_onion_auth_dir = '/var/lib/tor/onion_auth'
40 
41 # load torrc_additions
42 if os.path.isfile(path_torrc_additions):
43     with io.open(path_torrc_additions) as f:
44         torrc_additions = f.read()
45 else:
46     sys.exit('Error opening {0} for reading'.format(path_torrc_additions))
47 
48 # load torrc
49 if os.path.isfile(path_torrc_backup):
50     with io.open(path_torrc_backup) as f:
51         torrc = f.read()
52 else:
53     if os.path.isfile(path_torrc):
54         with io.open(path_torrc) as f:
55             torrc = f.read()
56     else:
57         sys.exit('Error opening {0} for reading'.format(path_torrc))
58 
59     # save a backup
60     with io.open(path_torrc_backup, 'w') as f:
61         f.write(torrc)
62 
63 # append the additions
64 with io.open(path_torrc, 'w') as f:
65     f.write(torrc + torrc_additions)
66 
67 # check for v3 aths files
68 v3_authfiles_present = False
69 for f in paths_v3_authfiles.values():
70     if os.path.isfile(f):
71         v3_authfiles_present = True
72 
73 # if there are v3 authfiles, make dir and copy them into place
74 debian_tor_uid = pwd.getpwnam("debian-tor").pw_uid
75 debian_tor_gid = grp.getgrnam("debian-tor").gr_gid
76 
77 if not os.path.isdir(path_onion_auth_dir):
78     os.mkdir(path_onion_auth_dir)
79 
80 os.chmod(path_onion_auth_dir, 0o700)
81 os.chown(path_onion_auth_dir, debian_tor_uid, debian_tor_gid)
82 
83 for key, f in paths_v3_authfiles.items():
84     if os.path.isfile(f):
85         filename = os.path.basename(f)
86         new_f = os.path.join(path_onion_auth_dir, filename)
87         copyfile(f, new_f)
88         os.chmod(new_f, 0o400)
89         os.chown(new_f, debian_tor_uid, debian_tor_gid)
90 
91 # restart tor
92 try:
93     subprocess.check_call(['systemctl', 'restart', 'tor@default.service'])
94 except subprocess.CalledProcessError:
95     sys.exit('Error restarting Tor')
96 
97 # Set journalist.desktop and source.desktop links as trusted with Nautilus (see
98 # https://github.com/freedomofpress/securedrop/issues/2586)
99 # set euid and env variables to amnesia user
100 amnesia_gid = grp.getgrnam('amnesia').gr_gid
101 amnesia_uid = pwd.getpwnam('amnesia').pw_uid
102 os.setresgid(amnesia_gid, amnesia_gid, -1)
103 os.setresuid(amnesia_uid, amnesia_uid, -1)
104 env = os.environ.copy()
105 env['XDG_CURRENT_DESKTOP'] = 'GNOME'
106 env['DESKTOP_SESSION'] = 'default'
107 env['DISPLAY'] = ':1'
108 env['XDG_RUNTIME_DIR'] = '/run/user/{}'.format(amnesia_uid)
109 env['XDG_DATA_DIR'] = '/usr/share/gnome:/usr/local/share/:/usr/share/'
110 env['HOME'] = '/home/amnesia'
111 env['LOGNAME'] = 'amnesia'
112 env['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(
113         amnesia_uid)
114 
115 # remove existing shortcut, recreate symlink and change metadata attribute
116 # to trust .desktop
117 for shortcut in ['source.desktop', 'journalist.desktop']:
118     subprocess.call(['rm', path_desktop + shortcut], env=env)
119     subprocess.call(['ln', '-s', path_persistent_desktop + shortcut,
120                      path_desktop + shortcut], env=env)
121     subprocess.call(['gio', 'set', path_desktop + shortcut,
122                      'metadata::trusted', 'true'], env=env)
123 
124 # in Tails 4, reload gnome-shell desktop icons extension to update with changes above
125 cmd = ["lsb_release", "--id", "--short"]
126 p = subprocess.check_output(cmd)
127 distro_id = p.rstrip()
128 if distro_id == 'Debian' and os.uname()[1] == 'amnesia':
129     subprocess.call(['gnome-shell-extension-tool', '-r', 'desktop-icons@csoriano'], env=env)
130 
131 # reacquire uid0 and notify the user
132 os.setresuid(0, 0, -1)
133 os.setresgid(0, 0, -1)
134 success_message = 'You can now access the Journalist Interface.\nIf you are an admin, you can now SSH to the servers.'  # noqa: E501
135 subprocess.call(['tails-notify-user',
136                  'SecureDrop successfully auto-configured!',
137                  success_message])
138 
139 # As the amnesia user, check for SecureDrop workstation updates.
140 os.setresgid(amnesia_gid, amnesia_gid, -1)
141 os.setresuid(amnesia_uid, amnesia_uid, -1)
142 output = subprocess.check_output([path_securedrop_admin_venv,
143                                   path_securedrop_admin_init,
144                                   '--root', path_securedrop_root,
145                                   'check_for_updates'], env=env)
146 
147 flag_location = "/home/amnesia/Persistent/.securedrop/securedrop_update.flag"
148 if b'Update needed' in output or os.path.exists(flag_location):
149     # Start the SecureDrop updater GUI.
150     subprocess.Popen(['python3', path_gui_updater], env=env)
151
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

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,7 +7,8 @@
 import sys
 import subprocess
 
-from shutil import copyfile
+import tempfile
+from shutil import copyfile, copyfileobj
 
 
 # check for root
@@ -148,3 +149,70 @@
 if b'Update needed' in output or os.path.exists(flag_location):
     # Start the SecureDrop updater GUI.
     subprocess.Popen(['python3', path_gui_updater], env=env)
+
+# Check for Tails < 4.19 and apply a fix to the auto-updater.
+# See https://tails.boum.org/news/version_4.18/
+# (Suggested removal: 2022/01)
+tails_4_min_version = 19
+needs_update = False
+tails_current_version = None
+
+with open('/etc/os-release') as file:
+    for line in file:
+        try:
+            k, v = line.strip().split("=")
+            if k == "TAILS_VERSION_ID":
+                tails_current_version = v.strip("\"").split(".")
+        except ValueError:
+            continue
+
+if tails_current_version:
+    try:
+        needs_update = (len(tails_current_version) >= 2 and
+                        int(tails_current_version[1]) < tails_4_min_version)
+
+    except (TypeError, ValueError):
+        sys.exit(0)  # Don't break tailsconfig trying to fix this
+
+    if needs_update:
+        cert_name = 'isrg-root-x1-cross-signed.pem'
+        pem_file = tempfile.NamedTemporaryFile(delete=True)
+
+        try:
+            subprocess.call(['torsocks', 'curl', '--silent',
+                             'https://tails.boum.org/' + cert_name],
+                            stdout=pem_file, env=env)
+
+            # Verify against /etc/ssl/certs/DST_Root_CA_X3.pem, which cross-signs
+            # the new LetsEncrypt cert but is expiring
+            verify_proc = subprocess.check_output(['openssl', 'verify',
+                                                   '-no_check_time', '-no-CApath',
+                                                   '-CAfile',
+                                                   '/etc/ssl/certs/DST_Root_CA_X3.pem',
+                                                   pem_file.name],
+                                                  universal_newlines=True, env=env)
+
+            if 'OK' in verify_proc:
+
+                # Updating the cert chain requires sudo privileges
+                os.setresgid(0, 0, -1)
+                os.setresuid(0, 0, -1)
+
+                with open('/usr/local/etc/ssl/certs/tails.boum.org-CA.pem', 'a') as chain:
+                    pem_file.seek(0)
+                    copyfileobj(pem_file, chain)
+
+                # As amnesia user, start updater GUI
+                os.setresgid(amnesia_gid, amnesia_gid, -1)
+                os.setresuid(amnesia_uid, amnesia_uid, -1)
+                restart_proc = subprocess.call(['systemctl', '--user', 'restart',
+                                                'tails-upgrade-frontend'], env=env)
+
+        except subprocess.CalledProcessError:
+            sys.exit(0)   # Don't break tailsconfig trying to fix this
+
+        except IOError:
+            sys.exit(0)
+
+        finally:
+            pem_file.close()

{"golden_diff": "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\n--- a/install_files/ansible-base/roles/tails-config/files/securedrop_init.py\n+++ b/install_files/ansible-base/roles/tails-config/files/securedrop_init.py\n@@ -7,7 +7,8 @@\n import sys\n import subprocess\n \n-from shutil import copyfile\n+import tempfile\n+from shutil import copyfile, copyfileobj\n \n \n # check for root\n@@ -148,3 +149,70 @@\n if b'Update needed' in output or os.path.exists(flag_location):\n     # Start the SecureDrop updater GUI.\n     subprocess.Popen(['python3', path_gui_updater], env=env)\n+\n+# Check for Tails < 4.19 and apply a fix to the auto-updater.\n+# See https://tails.boum.org/news/version_4.18/\n+# (Suggested removal: 2022/01)\n+tails_4_min_version = 19\n+needs_update = False\n+tails_current_version = None\n+\n+with open('/etc/os-release') as file:\n+    for line in file:\n+        try:\n+            k, v = line.strip().split(\"=\")\n+            if k == \"TAILS_VERSION_ID\":\n+                tails_current_version = v.strip(\"\\\"\").split(\".\")\n+        except ValueError:\n+            continue\n+\n+if tails_current_version:\n+    try:\n+        needs_update = (len(tails_current_version) >= 2 and\n+                        int(tails_current_version[1]) < tails_4_min_version)\n+\n+    except (TypeError, ValueError):\n+        sys.exit(0)  # Don't break tailsconfig trying to fix this\n+\n+    if needs_update:\n+        cert_name = 'isrg-root-x1-cross-signed.pem'\n+        pem_file = tempfile.NamedTemporaryFile(delete=True)\n+\n+        try:\n+            subprocess.call(['torsocks', 'curl', '--silent',\n+                             'https://tails.boum.org/' + cert_name],\n+                            stdout=pem_file, env=env)\n+\n+            # Verify against /etc/ssl/certs/DST_Root_CA_X3.pem, which cross-signs\n+            # the new LetsEncrypt cert but is expiring\n+            verify_proc = subprocess.check_output(['openssl', 'verify',\n+                                                   '-no_check_time', '-no-CApath',\n+                                                   '-CAfile',\n+                                                   '/etc/ssl/certs/DST_Root_CA_X3.pem',\n+                                                   pem_file.name],\n+                                                  universal_newlines=True, env=env)\n+\n+            if 'OK' in verify_proc:\n+\n+                # Updating the cert chain requires sudo privileges\n+                os.setresgid(0, 0, -1)\n+                os.setresuid(0, 0, -1)\n+\n+                with open('/usr/local/etc/ssl/certs/tails.boum.org-CA.pem', 'a') as chain:\n+                    pem_file.seek(0)\n+                    copyfileobj(pem_file, chain)\n+\n+                # As amnesia user, start updater GUI\n+                os.setresgid(amnesia_gid, amnesia_gid, -1)\n+                os.setresuid(amnesia_uid, amnesia_uid, -1)\n+                restart_proc = subprocess.call(['systemctl', '--user', 'restart',\n+                                                'tails-upgrade-frontend'], env=env)\n+\n+        except subprocess.CalledProcessError:\n+            sys.exit(0)   # Don't break tailsconfig trying to fix this\n+\n+        except IOError:\n+            sys.exit(0)\n+\n+        finally:\n+            pem_file.close()\n", "issue": "Prompt users to fix upgrade issue for Tails 4.14 - 4.18 (or fix it for them?)\n## Description\r\n\r\nTails automatic upgrades were broken between versions 4.14 - 4.18 inclusive ([Tails announcement](https://tails.boum.org/doc/upgrade/error/check/index.en.html#4.18)), and some users on older versions of Tails may not realize that they are missing auto-updates, and that there is a manual step required to fix them. \r\n\r\nWe could:\r\n- Make no code changes, and continue to use support messaging channels etc to remind folks of this issue\r\n- Add text to the SecureDrop updater wizard, prompting users to update if their version of Tails is too old, or\r\n- Perform the steps to fix automatic Tails updates ourselves, which (according to the above link) consist of \r\n```\r\ntorsocks curl --silent https://tails.boum.org/isrg-root-x1-cross-signed.pem \\\r\n| sudo tee --append /usr/local/etc/ssl/certs/tails.boum.org-CA.pem \\\r\n&& systemctl --user restart tails-upgrade-frontend\r\n```  \r\n\r\nI'm kind of in favour of the last option, and I can put in a PR for a check at the end of `securedrop_init` right before our GUI updater runs. What do others think?  [edit: filing now so we can discuss inclusion in 2.1.0]\n", "before_files": [{"content": "#!/usr/bin/python3\n\nimport grp\nimport os\nimport io\nimport pwd\nimport sys\nimport subprocess\n\nfrom shutil import copyfile\n\n\n# check for root\nif os.geteuid() != 0:\n    sys.exit('You need to run this as root')\n\n# paths\npath_torrc_additions = '/home/amnesia/Persistent/.securedrop/torrc_additions'\npath_torrc_backup = '/etc/tor/torrc.bak'\npath_torrc = '/etc/tor/torrc'\npath_desktop = '/home/amnesia/Desktop/'\npath_persistent_desktop = '/lib/live/mount/persistence/TailsData_unlocked/dotfiles/Desktop/'  # noqa: E501\npath_securedrop_root = '/home/amnesia/Persistent/securedrop'\npath_securedrop_admin_venv = os.path.join(path_securedrop_root,\n                                          'admin/.venv3/bin/python')\npath_securedrop_admin_init = os.path.join(path_securedrop_root,\n                                          'admin/securedrop_admin/__init__.py')\npath_gui_updater = os.path.join(path_securedrop_root,\n                                'journalist_gui/SecureDropUpdater')\n\npaths_v3_authfiles = {\n        \"app-journalist\": os.path.join(path_securedrop_root,\n                                       'install_files/ansible-base/app-journalist.auth_private'),\n        \"app-ssh\": os.path.join(path_securedrop_root,\n                                'install_files/ansible-base/app-ssh.auth_private'),\n        \"mon-ssh\": os.path.join(path_securedrop_root,\n                                'install_files/ansible-base/mon-ssh.auth_private')\n}\npath_onion_auth_dir = '/var/lib/tor/onion_auth'\n\n# load torrc_additions\nif os.path.isfile(path_torrc_additions):\n    with io.open(path_torrc_additions) as f:\n        torrc_additions = f.read()\nelse:\n    sys.exit('Error opening {0} for reading'.format(path_torrc_additions))\n\n# load torrc\nif os.path.isfile(path_torrc_backup):\n    with io.open(path_torrc_backup) as f:\n        torrc = f.read()\nelse:\n    if os.path.isfile(path_torrc):\n        with io.open(path_torrc) as f:\n            torrc = f.read()\n    else:\n        sys.exit('Error opening {0} for reading'.format(path_torrc))\n\n    # save a backup\n    with io.open(path_torrc_backup, 'w') as f:\n        f.write(torrc)\n\n# append the additions\nwith io.open(path_torrc, 'w') as f:\n    f.write(torrc + torrc_additions)\n\n# check for v3 aths files\nv3_authfiles_present = False\nfor f in paths_v3_authfiles.values():\n    if os.path.isfile(f):\n        v3_authfiles_present = True\n\n# if there are v3 authfiles, make dir and copy them into place\ndebian_tor_uid = pwd.getpwnam(\"debian-tor\").pw_uid\ndebian_tor_gid = grp.getgrnam(\"debian-tor\").gr_gid\n\nif not os.path.isdir(path_onion_auth_dir):\n    os.mkdir(path_onion_auth_dir)\n\nos.chmod(path_onion_auth_dir, 0o700)\nos.chown(path_onion_auth_dir, debian_tor_uid, debian_tor_gid)\n\nfor key, f in paths_v3_authfiles.items():\n    if os.path.isfile(f):\n        filename = os.path.basename(f)\n        new_f = os.path.join(path_onion_auth_dir, filename)\n        copyfile(f, new_f)\n        os.chmod(new_f, 0o400)\n        os.chown(new_f, debian_tor_uid, debian_tor_gid)\n\n# restart tor\ntry:\n    subprocess.check_call(['systemctl', 'restart', 'tor@default.service'])\nexcept subprocess.CalledProcessError:\n    sys.exit('Error restarting Tor')\n\n# Set journalist.desktop and source.desktop links as trusted with Nautilus (see\n# https://github.com/freedomofpress/securedrop/issues/2586)\n# set euid and env variables to amnesia user\namnesia_gid = grp.getgrnam('amnesia').gr_gid\namnesia_uid = pwd.getpwnam('amnesia').pw_uid\nos.setresgid(amnesia_gid, amnesia_gid, -1)\nos.setresuid(amnesia_uid, amnesia_uid, -1)\nenv = os.environ.copy()\nenv['XDG_CURRENT_DESKTOP'] = 'GNOME'\nenv['DESKTOP_SESSION'] = 'default'\nenv['DISPLAY'] = ':1'\nenv['XDG_RUNTIME_DIR'] = '/run/user/{}'.format(amnesia_uid)\nenv['XDG_DATA_DIR'] = '/usr/share/gnome:/usr/local/share/:/usr/share/'\nenv['HOME'] = '/home/amnesia'\nenv['LOGNAME'] = 'amnesia'\nenv['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(\n        amnesia_uid)\n\n# remove existing shortcut, recreate symlink and change metadata attribute\n# to trust .desktop\nfor shortcut in ['source.desktop', 'journalist.desktop']:\n    subprocess.call(['rm', path_desktop + shortcut], env=env)\n    subprocess.call(['ln', '-s', path_persistent_desktop + shortcut,\n                     path_desktop + shortcut], env=env)\n    subprocess.call(['gio', 'set', path_desktop + shortcut,\n                     'metadata::trusted', 'true'], env=env)\n\n# in Tails 4, reload gnome-shell desktop icons extension to update with changes above\ncmd = [\"lsb_release\", \"--id\", \"--short\"]\np = subprocess.check_output(cmd)\ndistro_id = p.rstrip()\nif distro_id == 'Debian' and os.uname()[1] == 'amnesia':\n    subprocess.call(['gnome-shell-extension-tool', '-r', 'desktop-icons@csoriano'], env=env)\n\n# reacquire uid0 and notify the user\nos.setresuid(0, 0, -1)\nos.setresgid(0, 0, -1)\nsuccess_message = 'You can now access the Journalist Interface.\\nIf you are an admin, you can now SSH to the servers.'  # noqa: E501\nsubprocess.call(['tails-notify-user',\n                 'SecureDrop successfully auto-configured!',\n                 success_message])\n\n# As the amnesia user, check for SecureDrop workstation updates.\nos.setresgid(amnesia_gid, amnesia_gid, -1)\nos.setresuid(amnesia_uid, amnesia_uid, -1)\noutput = subprocess.check_output([path_securedrop_admin_venv,\n                                  path_securedrop_admin_init,\n                                  '--root', path_securedrop_root,\n                                  'check_for_updates'], env=env)\n\nflag_location = \"/home/amnesia/Persistent/.securedrop/securedrop_update.flag\"\nif b'Update needed' in output or os.path.exists(flag_location):\n    # Start the SecureDrop updater GUI.\n    subprocess.Popen(['python3', path_gui_updater], env=env)\n", "path": "install_files/ansible-base/roles/tails-config/files/securedrop_init.py"}], "after_files": [{"content": "#!/usr/bin/python3\n\nimport grp\nimport os\nimport io\nimport pwd\nimport sys\nimport subprocess\n\nimport tempfile\nfrom shutil import copyfile, copyfileobj\n\n\n# check for root\nif os.geteuid() != 0:\n    sys.exit('You need to run this as root')\n\n# paths\npath_torrc_additions = '/home/amnesia/Persistent/.securedrop/torrc_additions'\npath_torrc_backup = '/etc/tor/torrc.bak'\npath_torrc = '/etc/tor/torrc'\npath_desktop = '/home/amnesia/Desktop/'\npath_persistent_desktop = '/lib/live/mount/persistence/TailsData_unlocked/dotfiles/Desktop/'  # noqa: E501\npath_securedrop_root = '/home/amnesia/Persistent/securedrop'\npath_securedrop_admin_venv = os.path.join(path_securedrop_root,\n                                          'admin/.venv3/bin/python')\npath_securedrop_admin_init = os.path.join(path_securedrop_root,\n                                          'admin/securedrop_admin/__init__.py')\npath_gui_updater = os.path.join(path_securedrop_root,\n                                'journalist_gui/SecureDropUpdater')\n\npaths_v3_authfiles = {\n        \"app-journalist\": os.path.join(path_securedrop_root,\n                                       'install_files/ansible-base/app-journalist.auth_private'),\n        \"app-ssh\": os.path.join(path_securedrop_root,\n                                'install_files/ansible-base/app-ssh.auth_private'),\n        \"mon-ssh\": os.path.join(path_securedrop_root,\n                                'install_files/ansible-base/mon-ssh.auth_private')\n}\npath_onion_auth_dir = '/var/lib/tor/onion_auth'\n\n# load torrc_additions\nif os.path.isfile(path_torrc_additions):\n    with io.open(path_torrc_additions) as f:\n        torrc_additions = f.read()\nelse:\n    sys.exit('Error opening {0} for reading'.format(path_torrc_additions))\n\n# load torrc\nif os.path.isfile(path_torrc_backup):\n    with io.open(path_torrc_backup) as f:\n        torrc = f.read()\nelse:\n    if os.path.isfile(path_torrc):\n        with io.open(path_torrc) as f:\n            torrc = f.read()\n    else:\n        sys.exit('Error opening {0} for reading'.format(path_torrc))\n\n    # save a backup\n    with io.open(path_torrc_backup, 'w') as f:\n        f.write(torrc)\n\n# append the additions\nwith io.open(path_torrc, 'w') as f:\n    f.write(torrc + torrc_additions)\n\n# check for v3 aths files\nv3_authfiles_present = False\nfor f in paths_v3_authfiles.values():\n    if os.path.isfile(f):\n        v3_authfiles_present = True\n\n# if there are v3 authfiles, make dir and copy them into place\ndebian_tor_uid = pwd.getpwnam(\"debian-tor\").pw_uid\ndebian_tor_gid = grp.getgrnam(\"debian-tor\").gr_gid\n\nif not os.path.isdir(path_onion_auth_dir):\n    os.mkdir(path_onion_auth_dir)\n\nos.chmod(path_onion_auth_dir, 0o700)\nos.chown(path_onion_auth_dir, debian_tor_uid, debian_tor_gid)\n\nfor key, f in paths_v3_authfiles.items():\n    if os.path.isfile(f):\n        filename = os.path.basename(f)\n        new_f = os.path.join(path_onion_auth_dir, filename)\n        copyfile(f, new_f)\n        os.chmod(new_f, 0o400)\n        os.chown(new_f, debian_tor_uid, debian_tor_gid)\n\n# restart tor\ntry:\n    subprocess.check_call(['systemctl', 'restart', 'tor@default.service'])\nexcept subprocess.CalledProcessError:\n    sys.exit('Error restarting Tor')\n\n# Set journalist.desktop and source.desktop links as trusted with Nautilus (see\n# https://github.com/freedomofpress/securedrop/issues/2586)\n# set euid and env variables to amnesia user\namnesia_gid = grp.getgrnam('amnesia').gr_gid\namnesia_uid = pwd.getpwnam('amnesia').pw_uid\nos.setresgid(amnesia_gid, amnesia_gid, -1)\nos.setresuid(amnesia_uid, amnesia_uid, -1)\nenv = os.environ.copy()\nenv['XDG_CURRENT_DESKTOP'] = 'GNOME'\nenv['DESKTOP_SESSION'] = 'default'\nenv['DISPLAY'] = ':1'\nenv['XDG_RUNTIME_DIR'] = '/run/user/{}'.format(amnesia_uid)\nenv['XDG_DATA_DIR'] = '/usr/share/gnome:/usr/local/share/:/usr/share/'\nenv['HOME'] = '/home/amnesia'\nenv['LOGNAME'] = 'amnesia'\nenv['DBUS_SESSION_BUS_ADDRESS'] = 'unix:path=/run/user/{}/bus'.format(\n        amnesia_uid)\n\n# remove existing shortcut, recreate symlink and change metadata attribute\n# to trust .desktop\nfor shortcut in ['source.desktop', 'journalist.desktop']:\n    subprocess.call(['rm', path_desktop + shortcut], env=env)\n    subprocess.call(['ln', '-s', path_persistent_desktop + shortcut,\n                     path_desktop + shortcut], env=env)\n    subprocess.call(['gio', 'set', path_desktop + shortcut,\n                     'metadata::trusted', 'true'], env=env)\n\n# in Tails 4, reload gnome-shell desktop icons extension to update with changes above\ncmd = [\"lsb_release\", \"--id\", \"--short\"]\np = subprocess.check_output(cmd)\ndistro_id = p.rstrip()\nif distro_id == 'Debian' and os.uname()[1] == 'amnesia':\n    subprocess.call(['gnome-shell-extension-tool', '-r', 'desktop-icons@csoriano'], env=env)\n\n# reacquire uid0 and notify the user\nos.setresuid(0, 0, -1)\nos.setresgid(0, 0, -1)\nsuccess_message = 'You can now access the Journalist Interface.\\nIf you are an admin, you can now SSH to the servers.'  # noqa: E501\nsubprocess.call(['tails-notify-user',\n                 'SecureDrop successfully auto-configured!',\n                 success_message])\n\n# As the amnesia user, check for SecureDrop workstation updates.\nos.setresgid(amnesia_gid, amnesia_gid, -1)\nos.setresuid(amnesia_uid, amnesia_uid, -1)\noutput = subprocess.check_output([path_securedrop_admin_venv,\n                                  path_securedrop_admin_init,\n                                  '--root', path_securedrop_root,\n                                  'check_for_updates'], env=env)\n\nflag_location = \"/home/amnesia/Persistent/.securedrop/securedrop_update.flag\"\nif b'Update needed' in output or os.path.exists(flag_location):\n    # Start the SecureDrop updater GUI.\n    subprocess.Popen(['python3', path_gui_updater], env=env)\n\n# Check for Tails < 4.19 and apply a fix to the auto-updater.\n# See https://tails.boum.org/news/version_4.18/\n# (Suggested removal: 2022/01)\ntails_4_min_version = 19\nneeds_update = False\ntails_current_version = None\n\nwith open('/etc/os-release') as file:\n    for line in file:\n        try:\n            k, v = line.strip().split(\"=\")\n            if k == \"TAILS_VERSION_ID\":\n                tails_current_version = v.strip(\"\\\"\").split(\".\")\n        except ValueError:\n            continue\n\nif tails_current_version:\n    try:\n        needs_update = (len(tails_current_version) >= 2 and\n                        int(tails_current_version[1]) < tails_4_min_version)\n\n    except (TypeError, ValueError):\n        sys.exit(0)  # Don't break tailsconfig trying to fix this\n\n    if needs_update:\n        cert_name = 'isrg-root-x1-cross-signed.pem'\n        pem_file = tempfile.NamedTemporaryFile(delete=True)\n\n        try:\n            subprocess.call(['torsocks', 'curl', '--silent',\n                             'https://tails.boum.org/' + cert_name],\n                            stdout=pem_file, env=env)\n\n            # Verify against /etc/ssl/certs/DST_Root_CA_X3.pem, which cross-signs\n            # the new LetsEncrypt cert but is expiring\n            verify_proc = subprocess.check_output(['openssl', 'verify',\n                                                   '-no_check_time', '-no-CApath',\n                                                   '-CAfile',\n                                                   '/etc/ssl/certs/DST_Root_CA_X3.pem',\n                                                   pem_file.name],\n                                                  universal_newlines=True, env=env)\n\n            if 'OK' in verify_proc:\n\n                # Updating the cert chain requires sudo privileges\n                os.setresgid(0, 0, -1)\n                os.setresuid(0, 0, -1)\n\n                with open('/usr/local/etc/ssl/certs/tails.boum.org-CA.pem', 'a') as chain:\n                    pem_file.seek(0)\n                    copyfileobj(pem_file, chain)\n\n                # As amnesia user, start updater GUI\n                os.setresgid(amnesia_gid, amnesia_gid, -1)\n                os.setresuid(amnesia_uid, amnesia_uid, -1)\n                restart_proc = subprocess.call(['systemctl', '--user', 'restart',\n                                                'tails-upgrade-frontend'], env=env)\n\n        except subprocess.CalledProcessError:\n            sys.exit(0)   # Don't break tailsconfig trying to fix this\n\n        except IOError:\n            sys.exit(0)\n\n        finally:\n            pem_file.close()\n", "path": "install_files/ansible-base/roles/tails-config/files/securedrop_init.py"}]}