Repository: freedomofpress/securedrop

Issue:
Remove support for v2 Onion Services
## Description

Tor Onion Services V2 support will be disabled in clients on October 15th 2021 [1]
In https://github.com/freedomofpress/securedrop/issues/2951, we introduced support for v3 onion services, and #4768 , we on restricting the use of Onion Services to v3-only

This ticket is to track the removal of all onion service v2 functionality alongside the transition to Ubuntu 20.04.

[1] https://blog.torproject.org/v2-deprecation-timeline



Discussion:
we should probably remove outdated tor2web logic as part of this effort as it no longer seems to be maintained. @redshiftzero pays closer attention to the ongoings in torland, mind providing feedback here?
Since there isn't v3 support in tor2web and it hasn't had commits in a couple years (I also couldn't find any working relays), I think it's fine to remove... if Tor2Web sees a big resurgence of use you could add it back if/when that happens

Resolving patch:
diff --git a/admin/securedrop_admin/__init__.py b/admin/securedrop_admin/__init__.py
--- a/admin/securedrop_admin/__init__.py
+++ b/admin/securedrop_admin/__init__.py
@@ -64,7 +64,7 @@
 sdlog = logging.getLogger(__name__)
 RELEASE_KEY = '22245C81E3BAEB4138B36061310F561200F4AD77'
 DEFAULT_KEYSERVER = 'hkps://keys.openpgp.org'
-SUPPORT_ONION_URL = 'http://support6kv2242qx.onion'
+SUPPORT_ONION_URL = 'http://sup6h5iyiyenvjkfxbgrjynm5wsgijjoatvnvdgyyi7je3xqm4kh6uqd.onion'
 SUPPORT_URL = 'https://support.freedom.press'
 EXIT_SUCCESS = 0
 EXIT_SUBPROCESS_ERROR = 1
@@ -193,23 +193,6 @@ def validate(self, document: Document) -> bool:
                 return True
             raise ValidationError(message="Must be either yes or no")
 
-    class ValidateYesNoForV3(Validator):
-
-        def __init__(self, *args: Any, **kwargs: Any) -> None:
-            Validator.__init__(*args, **kwargs)
-            self.caller = args[0]
-
-        def validate(self, document: Document) -> bool:
-            text = document.text.lower()
-            # Raise error if admin tries to disable v3 when v2
-            # is already disabled.
-            if text == 'no' and \
-                    not self.caller._config_in_progress.get("v2_onion_services"):  # noqa: E501
-                raise ValidationError(message="Since you disabled v2 onion services, you must enable v3 onion services.")  # noqa: E501
-            if text == 'yes' or text == 'no':
-                return True
-            raise ValidationError(message="Must be either yes or no")
-
     class ValidateFingerprint(Validator):
         def validate(self, document: Document) -> bool:
             text = document.text.replace(' ', '')
@@ -457,17 +440,6 @@ def __init__(self, args: argparse.Namespace) -> None:
              SiteConfig.ValidateLocales(self.args.app_path),
              str.split,
              lambda config: True),
-            ('v2_onion_services', self.check_for_v2_onion(), bool,
-             'WARNING: v2 onion services cannot be installed on servers ' +
-             'running Ubuntu 20.04. Do you want to enable v2 onion services?',
-             SiteConfig.ValidateYesNo(),
-             lambda x: x.lower() == 'yes',
-             lambda config: True),
-            ('v3_onion_services', self.check_for_v3_onion, bool,
-             'Do you want to enable v3 onion services (recommended)?',
-             SiteConfig.ValidateYesNoForV3(self),
-             lambda x: x.lower() == 'yes',
-             lambda config: True),
         ]  # type: List[_DescEntryType]
 
     def load_and_update_config(self, validate: bool = True, prompt: bool = True) -> bool:
@@ -485,52 +457,8 @@ def update_config(self, prompt: bool = True) -> bool:
         self.save()
         self.validate_gpg_keys()
         self.validate_journalist_alert_email()
-        self.validate_https_and_v3()
         return True
 
-    def validate_https_and_v3(self) -> bool:
-        """
-        Checks if https is enabled with v3 onion service.
-
-        :returns: False if both v3 and https enabled, True otherwise.
-        """
-        warning_msg = ("You have configured HTTPS on your source interface "
-                       "and v3 onion services. "
-                       "IMPORTANT: Ensure that you update your certificate "
-                       "to include your v3 source URL before advertising "
-                       "it to sources! ")
-
-        if self.config.get("v3_onion_services", False) and \
-                self.config.get("securedrop_app_https_certificate_cert_src"):
-            print(warning_msg)
-            return False
-        return True
-
-    def check_for_v2_onion(self) -> bool:
-        """
-        Check if v2 onion services are already enabled or not.
-        """
-        source_ths = os.path.join(self.args.ansible_path, "app-source-ths")
-        if os.path.exists(source_ths):  # Means old installation
-            data = ""
-            with open(source_ths) as fobj:
-                data = fobj.read()
-
-            data = data.strip()
-            if len(data) < 56:  # Old v2 onion address
-                return True
-        return False
-
-    def check_for_v3_onion(self) -> bool:
-        """
-        Check if v3 onion services should be enabled by default or not.
-        """
-        v2_value = self._config_in_progress.get("v2_onion_services", False)
-        # We need to see the value in the configuration file
-        # for v3_onion_services
-        v3_value = self.config.get("v3_onion_services", True)
-        return v3_value or not v2_value
-
     def user_prompt_config(self) -> Dict[str, Any]:
         self._config_in_progress = {}
         for desc in self.desc:
@@ -547,10 +475,7 @@ def user_prompt_config_one(
         self, desc: _DescEntryType, from_config: Optional[Any]
     ) -> Any:
         (var, default, type, prompt, validator, transform, condition) = desc
-        if from_config is not None and var != "v3_onion_services":
-            # v3_onion_services must be true if v2 is disabled by the admin
-            # otherwise, we may end up in a situation where both v2 and v3
-            # are disabled by the admin (by mistake).
+        if from_config is not None:
             default = from_config
         prompt += ': '
 


Test patch:
diff --git a/admin/tests/test_integration.py b/admin/tests/test_integration.py
--- a/admin/tests/test_integration.py
+++ b/admin/tests/test_integration.py
@@ -47,76 +47,6 @@
 smtp_relay: smtp.gmail.com
 smtp_relay_port: 587
 ssh_users: sd
-v2_onion_services: false
-v3_onion_services: true
-'''
-
-WHEN_BOTH_TRUE = '''app_hostname: app
-app_ip: 10.20.2.2
-daily_reboot_time: 5
-dns_server:
-- 8.8.8.8
-- 8.8.4.4
-enable_ssh_over_tor: true
-journalist_alert_email: ''
-journalist_alert_gpg_public_key: ''
-journalist_gpg_fpr: ''
-monitor_hostname: mon
-monitor_ip: 10.20.3.2
-ossec_alert_email: test@gmail.com
-ossec_alert_gpg_public_key: sd_admin_test.pub
-ossec_gpg_fpr: 1F544B31C845D698EB31F2FF364F1162D32E7E58
-sasl_domain: gmail.com
-sasl_password: testpassword
-sasl_username: testuser
-securedrop_app_gpg_fingerprint: 1F544B31C845D698EB31F2FF364F1162D32E7E58
-securedrop_app_gpg_public_key: sd_admin_test.pub
-securedrop_app_https_certificate_cert_src: ''
-securedrop_app_https_certificate_chain_src: ''
-securedrop_app_https_certificate_key_src: ''
-securedrop_app_https_on_source_interface: false
-securedrop_supported_locales:
-- de_DE
-- es_ES
-smtp_relay: smtp.gmail.com
-smtp_relay_port: 587
-ssh_users: sd
-v2_onion_services: true
-v3_onion_services: true
-'''
-
-WHEN_ONLY_V2 = '''app_hostname: app
-app_ip: 10.20.2.2
-daily_reboot_time: 5
-dns_server:
-- 8.8.8.8
-- 8.8.4.4
-enable_ssh_over_tor: true
-journalist_alert_email: ''
-journalist_alert_gpg_public_key: ''
-journalist_gpg_fpr: ''
-monitor_hostname: mon
-monitor_ip: 10.20.3.2
-ossec_alert_email: test@gmail.com
-ossec_alert_gpg_public_key: sd_admin_test.pub
-ossec_gpg_fpr: 1F544B31C845D698EB31F2FF364F1162D32E7E58
-sasl_domain: gmail.com
-sasl_password: testpassword
-sasl_username: testuser
-securedrop_app_gpg_fingerprint: 1F544B31C845D698EB31F2FF364F1162D32E7E58
-securedrop_app_gpg_public_key: sd_admin_test.pub
-securedrop_app_https_certificate_cert_src: ''
-securedrop_app_https_certificate_chain_src: ''
-securedrop_app_https_certificate_key_src: ''
-securedrop_app_https_on_source_interface: false
-securedrop_supported_locales:
-- de_DE
-- es_ES
-smtp_relay: smtp.gmail.com
-smtp_relay_port: 587
-ssh_users: sd
-v2_onion_services: true
-v3_onion_services: false
 '''
 
 JOURNALIST_ALERT_OUTPUT = '''app_hostname: app
@@ -149,8 +79,6 @@
 smtp_relay: smtp.gmail.com
 smtp_relay_port: 587
 ssh_users: sd
-v2_onion_services: false
-v3_onion_services: true
 '''
 
 HTTPS_OUTPUT = '''app_hostname: app
@@ -183,8 +111,6 @@
 smtp_relay: smtp.gmail.com
 smtp_relay_port: 587
 ssh_users: sd
-v2_onion_services: false
-v3_onion_services: true
 '''
 
 
@@ -339,21 +265,6 @@ def verify_locales_prompt(child):
     child.expect(rb'Space separated list of additional locales to support')  # noqa: E501
 
 
-def verify_v2_onion_for_first_time(child):
-    child.expect(rb'Do you want to enable v2 onion services\?\:', timeout=2)  # noqa: E501
-    assert ANSI_ESCAPE.sub('', child.buffer.decode("utf-8")).strip() == 'no'  # noqa: E501
-
-
-def verify_v3_onion_for_first_time(child):
-    child.expect(rb'Do you want to enable v3 onion services \(recommended\)\?\:', timeout=2)  # noqa: E501
-    assert ANSI_ESCAPE.sub('', child.buffer.decode("utf-8")).strip() == 'yes'  # noqa: E501
-
-
-def verify_v3_onion_when_v2_is_enabled(child):
-    child.expect(rb'Do you want to enable v3 onion services \(recommended\)\?\:', timeout=2)  # noqa: E501
-    assert ANSI_ESCAPE.sub('', child.buffer.decode("utf-8")).strip() == 'yes'  # noqa: E501
-
-
 def verify_install_has_valid_config():
     """
     Checks that securedrop-admin install validates the configuration.
@@ -424,9 +335,7 @@ def test_sdconfig_on_first_run():
     child.sendline('')
     verify_locales_prompt(child)
     child.sendline('de_DE es_ES')
-    verify_v2_onion_for_first_time(child)
     child.sendline('\b' * 3 + 'no')
-    verify_v3_onion_for_first_time(child)
     child.sendline('\b' * 4 + 'yes')
 
     child.expect(pexpect.EOF, timeout=10)  # Wait for validation to occur
@@ -441,134 +350,6 @@ def test_sdconfig_on_first_run():
     verify_install_has_valid_config()
 
 
-def test_sdconfig_both_v2_v3_true():
-    cmd = os.path.join(os.path.dirname(CURRENT_DIR),
-                       'securedrop_admin/__init__.py')
-    child = pexpect.spawn('python {0} --force --root {1} sdconfig'.format(cmd, SD_DIR))
-    verify_username_prompt(child)
-    child.sendline('')
-    verify_reboot_prompt(child)
-    child.sendline('\b5')  # backspace and put 5
-    verify_ipv4_appserver_prompt(child)
-    child.sendline('')
-    verify_ipv4_monserver_prompt(child)
-    child.sendline('')
-    verify_hostname_app_prompt(child)
-    child.sendline('')
-    verify_hostname_mon_prompt(child)
-    child.sendline('')
-    verify_dns_prompt(child)
-    child.sendline('')
-    verify_app_gpg_key_prompt(child)
-    child.sendline('\b' * 14 + 'sd_admin_test.pub')
-    verify_https_prompt(child)
-    # Default answer is no
-    child.sendline('')
-    verify_app_gpg_fingerprint_prompt(child)
-    child.sendline('1F544B31C845D698EB31F2FF364F1162D32E7E58')
-    verify_ossec_gpg_key_prompt(child)
-    child.sendline('\b' * 9 + 'sd_admin_test.pub')
-    verify_ossec_gpg_fingerprint_prompt(child)
-    child.sendline('1F544B31C845D698EB31F2FF364F1162D32E7E58')
-    verify_admin_email_prompt(child)
-    child.sendline('test@gmail.com')
-    verify_journalist_gpg_key_prompt(child)
-    child.sendline('')
-    verify_smtp_relay_prompt(child)
-    child.sendline('')
-    verify_smtp_port_prompt(child)
-    child.sendline('')
-    verify_sasl_domain_prompt(child)
-    child.sendline('')
-    verify_sasl_username_prompt(child)
-    child.sendline('testuser')
-    verify_sasl_password_prompt(child)
-    child.sendline('testpassword')
-    verify_ssh_over_lan_prompt(child)
-    child.sendline('')
-    verify_locales_prompt(child)
-    child.sendline('de_DE es_ES')
-    verify_v2_onion_for_first_time(child)
-    child.sendline('\b' * 3 + 'yes')
-    verify_v3_onion_when_v2_is_enabled(child)
-    child.sendline('\b' * 3 + 'yes')
-
-    child.expect(pexpect.EOF, timeout=10)  # Wait for validation to occur
-    child.close()
-    assert child.exitstatus == 0
-    assert child.signalstatus is None
-
-    with open(os.path.join(SD_DIR, 'install_files/ansible-base/group_vars/all/site-specific')) as fobj:    # noqa: E501
-        data = fobj.read()
-    assert data == WHEN_BOTH_TRUE
-
-    verify_install_has_valid_config()
-
-
-def test_sdconfig_only_v2_true():
-    cmd = os.path.join(os.path.dirname(CURRENT_DIR),
-                       'securedrop_admin/__init__.py')
-    child = pexpect.spawn('python {0} --force --root {1} sdconfig'.format(cmd, SD_DIR))
-    verify_username_prompt(child)
-    child.sendline('')
-    verify_reboot_prompt(child)
-    child.sendline('\b5')  # backspace and put 5
-    verify_ipv4_appserver_prompt(child)
-    child.sendline('')
-    verify_ipv4_monserver_prompt(child)
-    child.sendline('')
-    verify_hostname_app_prompt(child)
-    child.sendline('')
-    verify_hostname_mon_prompt(child)
-    child.sendline('')
-    verify_dns_prompt(child)
-    child.sendline('')
-    verify_app_gpg_key_prompt(child)
-    child.sendline('\b' * 14 + 'sd_admin_test.pub')
-    verify_https_prompt(child)
-    # Default answer is no
-    child.sendline('')
-    verify_app_gpg_fingerprint_prompt(child)
-    child.sendline('1F544B31C845D698EB31F2FF364F1162D32E7E58')
-    verify_ossec_gpg_key_prompt(child)
-    child.sendline('\b' * 9 + 'sd_admin_test.pub')
-    verify_ossec_gpg_fingerprint_prompt(child)
-    child.sendline('1F544B31C845D698EB31F2FF364F1162D32E7E58')
-    verify_admin_email_prompt(child)
-    child.sendline('test@gmail.com')
-    verify_journalist_gpg_key_prompt(child)
-    child.sendline('')
-    verify_smtp_relay_prompt(child)
-    child.sendline('')
-    verify_smtp_port_prompt(child)
-    child.sendline('')
-    verify_sasl_domain_prompt(child)
-    child.sendline('')
-    verify_sasl_username_prompt(child)
-    child.sendline('testuser')
-    verify_sasl_password_prompt(child)
-    child.sendline('testpassword')
-    verify_ssh_over_lan_prompt(child)
-    child.sendline('')
-    verify_locales_prompt(child)
-    child.sendline('de_DE es_ES')
-    verify_v2_onion_for_first_time(child)
-    child.sendline('\b' * 3 + 'yes')
-    verify_v3_onion_when_v2_is_enabled(child)
-    child.sendline('\b' * 3 + 'no')
-
-    child.expect(pexpect.EOF, timeout=10)  # Wait for validation to occur
-    child.close()
-    assert child.exitstatus == 0
-    assert child.signalstatus is None
-
-    with open(os.path.join(SD_DIR, 'install_files/ansible-base/group_vars/all/site-specific')) as fobj:    # noqa: E501
-        data = fobj.read()
-    assert data == WHEN_ONLY_V2
-
-    verify_install_has_valid_config()
-
-
 def test_sdconfig_enable_journalist_alerts():
     cmd = os.path.join(os.path.dirname(CURRENT_DIR),
                        'securedrop_admin/__init__.py')
@@ -621,10 +402,6 @@ def test_sdconfig_enable_journalist_alerts():
     child.sendline('')
     verify_locales_prompt(child)
     child.sendline('de_DE es_ES')
-    verify_v2_onion_for_first_time(child)
-    child.sendline('\b' * 3 + 'no')
-    verify_v3_onion_for_first_time(child)
-    child.sendline('\b' * 4 + 'yes')
 
     child.expect(pexpect.EOF, timeout=10)  # Wait for validation to occur
     child.close()
@@ -697,10 +474,6 @@ def test_sdconfig_enable_https_on_source_interface():
     child.sendline('')
     verify_locales_prompt(child)
     child.sendline('de_DE es_ES')
-    verify_v2_onion_for_first_time(child)
-    child.sendline('\b' * 3 + 'no')
-    verify_v3_onion_for_first_time(child)
-    child.sendline('\b' * 4 + 'yes')
 
     child.expect(pexpect.EOF, timeout=10)  # Wait for validation to occur
     child.close()
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
@@ -814,15 +814,6 @@ def verify_desc_consistency_optional(self, site_config, desc):
 
     def verify_desc_consistency(self, site_config, desc):
         self.verify_desc_consistency_optional(site_config, desc)
-        (var, default, etype, prompt, validator, transform, condition) = desc
-        with pytest.raises(ValidationError):
-            site_config.user_prompt_config_one(desc, '')
-            # If we are testing v3_onion_services, that will create a default
-            # value of 'yes', means it will not raise the ValidationError. We
-            # are generating it below for test to behave properly with all
-            # other test cases.
-            if var == "v3_onion_services":
-                raise ValidationError()
 
     def verify_prompt_boolean(
             self, site_config, desc):
@@ -833,34 +824,6 @@ def verify_prompt_boolean(
         assert site_config.user_prompt_config_one(desc, 'YES') is True
         assert site_config.user_prompt_config_one(desc, 'NO') is False
 
-    def verify_prompt_boolean_for_v3(
-            self, site_config, desc):
-        """As v3_onion_services input depends on input of
-           v2_onion_service, the answers will change.
-        """
-        self.verify_desc_consistency(site_config, desc)
-        (var, default, etype, prompt, validator, transform, condition) = desc
-        assert site_config.user_prompt_config_one(desc, True) is True
-        # Because if no v2_onion_service, v3 will become True
-        assert site_config.user_prompt_config_one(desc, False) is True
-        assert site_config.user_prompt_config_one(desc, 'YES') is True
-        # Because if no v2_onion_service, v3 will become True
-        assert site_config.user_prompt_config_one(desc, 'NO') is True
-
-        # Now we will set v2_onion_services as True so that we
-        # can set v3_onion_service as False. This is the case
-        # when an admin particularly marked v3 as False.
-        site_config._config_in_progress = {"v2_onion_services": True}
-        site_config.config = {"v3_onion_services": False}
-
-        # The next two tests should use the default from the above line,
-        # means it will return False value.
-        assert site_config.user_prompt_config_one(desc, True) is False
-        assert site_config.user_prompt_config_one(desc, 'YES') is False
-
-        assert site_config.user_prompt_config_one(desc, False) is False
-        assert site_config.user_prompt_config_one(desc, 'NO') is False
-
     def test_desc_conditional(self):
         """Ensure that conditional prompts behave correctly.
 
@@ -915,8 +878,6 @@ def auto_prompt(prompt, default, **kwargs):
     verify_prompt_enable_ssh_over_tor = verify_prompt_boolean
 
     verify_prompt_securedrop_app_gpg_public_key = verify_desc_consistency
-    verify_prompt_v2_onion_services = verify_prompt_boolean
-    verify_prompt_v3_onion_services = verify_prompt_boolean_for_v3
 
     def verify_prompt_not_empty(self, site_config, desc):
         with pytest.raises(ValidationError):
@@ -1094,22 +1055,3 @@ def test_find_or_generate_new_torv3_keys_subsequent_run(tmpdir, capsys):
         v3_onion_service_keys = json.load(f)
 
     assert v3_onion_service_keys == old_keys
-
-
-def test_v3_and_https_cert_message(tmpdir, capsys):
-    args = argparse.Namespace(site_config='UNKNOWN',
-                              ansible_path='tests/files',
-                              app_path=dirname(__file__))
-    site_config = securedrop_admin.SiteConfig(args)
-    site_config.config = {"v3_onion_services": False,
-                          "securedrop_app_https_certificate_cert_src": "ab.crt"}  # noqa: E501
-    # This should return True as v3 is not setup
-    assert site_config.validate_https_and_v3()
-
-    # This should return False as v3 and https are both setup
-    site_config.config.update({"v3_onion_services": True})
-    assert not site_config.validate_https_and_v3()
-
-    # This should return True as https is not setup
-    site_config.config.update({"securedrop_app_https_certificate_cert_src": ""})  # noqa: E501
-    assert site_config.validate_https_and_v3()
diff --git a/install_files/ansible-base/roles/app-test/tasks/extract_apptor_test_config.yml b/install_files/ansible-base/roles/app-test/tasks/extract_apptor_test_config.yml
--- a/install_files/ansible-base/roles/app-test/tasks/extract_apptor_test_config.yml
+++ b/install_files/ansible-base/roles/app-test/tasks/extract_apptor_test_config.yml
@@ -21,7 +21,6 @@
 - name: Gather apptest facts to dict to prepare for output
   set_fact:
     _tbb_selenium_dict:
-      hidserv_token: "{{ ansible_local.tor_app.hidserv_token }}"
       journalist_location: "{{ ansible_local.tor_app.journalist_location  }}"
       source_location: "{{ ansible_local.tor_app.source_location  }}"
       timeout: "{{ tbb_timeout }}"
diff --git a/molecule/testinfra/app/test_tor_config.py b/molecule/testinfra/app/test_tor_config.py
--- a/molecule/testinfra/app/test_tor_config.py
+++ b/molecule/testinfra/app/test_tor_config.py
@@ -64,14 +64,11 @@ def test_tor_torrc_sandbox(host):
 
 
 @pytest.mark.skip_in_prod
-def test_tor_v2_onion_url_readable_by_app(host):
+def test_tor_v2_onion_url_file_absent(host):
     v2_url_filepath = "/var/lib/securedrop/source_v2_url"
     with host.sudo():
         f = host.file(v2_url_filepath)
-        assert f.is_file
-        assert f.user == "www-data"
-        assert f.mode == 0o644
-        assert re.search(r"^[a-z0-9]{16}\.onion$", f.content_string)
+        assert not f.exists
 
 
 @pytest.mark.skip_in_prod
diff --git a/molecule/testinfra/app/test_tor_hidden_services.py b/molecule/testinfra/app/test_tor_hidden_services.py
--- a/molecule/testinfra/app/test_tor_hidden_services.py
+++ b/molecule/testinfra/app/test_tor_hidden_services.py
@@ -26,9 +26,8 @@ def test_tor_service_directories(host, tor_service):
 @pytest.mark.parametrize('tor_service', sdvars.tor_services)
 def test_tor_service_hostnames(host, tor_service):
     """
-    Check contents of Tor service hostname file. For normal onion services,
-    the file should contain only hostname (.onion URL). For authenticated
-    onion services, it should also contain the HidServAuth cookie.
+    Check contents of Tor service hostname file. For v3 onion services,
+    the file should contain only hostname (.onion URL).
     """
     # Declare regex only for THS; we'll build regex for ATHS only if
     # necessary, since we won't have the required values otherwise.
@@ -46,22 +45,13 @@ def test_tor_service_hostnames(host, tor_service):
         # All hostnames should contain at *least* the hostname.
         assert re.search(ths_hostname_regex, f.content_string)
 
-        if tor_service['authenticated'] and tor_service['version'] == 2:
-            # HidServAuth regex is approximately [a-zA-Z0-9/+], but validating
-            # the entire entry is sane, and we don't need to nitpick the
-            # charset.
-            aths_hostname_regex = ths_hostname_regex + " .{22} # client: " + \
-                                  tor_service['client']
-            assert re.search("^{}$".format(aths_hostname_regex), f.content_string)
-        elif tor_service['authenticated'] and tor_service['version'] == 3:
+        if tor_service['authenticated'] and tor_service['version'] == 3:
             # For authenticated version 3 onion services, the authorized_client
             # directory will exist and contain a file called client.auth.
             client_auth = host.file(
                 "/var/lib/tor/services/{}/authorized_clients/client.auth".format(
                     tor_service['name']))
             assert client_auth.is_file
-        elif tor_service['version'] == 2:
-            assert re.search("^{}$".format(ths_hostname_regex), f.content_string)
         else:
             assert re.search("^{}$".format(ths_hostname_regex_v3), f.content_string)
 
@@ -75,12 +65,6 @@ def test_tor_services_config(host, tor_service):
 
       * HiddenServiceDir
       * HiddenServicePort
-
-    Only v2 authenticated onion services must also include:
-
-      * HiddenServiceAuthorizeClient
-
-    Check for each as appropriate.
     """
     f = host.file("/etc/tor/torrc")
     dir_regex = "HiddenServiceDir /var/lib/tor/services/{}".format(
@@ -94,29 +78,12 @@ def test_tor_services_config(host, tor_service):
     except IndexError:
         local_port = remote_port
 
-    # Ensure that service is hardcoded to v2, for compatibility
-    # with newer versions of Tor, which default to v3.
-    if tor_service['version'] == 2:
-        version_string = "HiddenServiceVersion 2"
-    else:
-        version_string = ""
-
     port_regex = "HiddenServicePort {} 127.0.0.1:{}".format(
             remote_port, local_port)
 
     assert f.contains("^{}$".format(dir_regex))
     assert f.contains("^{}$".format(port_regex))
 
-    if version_string:
-        service_regex = "\n".join([dir_regex, version_string, port_regex])
-    else:
-        service_regex = "\n".join([dir_regex, port_regex])
-
-    if tor_service['authenticated'] and tor_service['version'] == 2:
-        auth_regex = "HiddenServiceAuthorizeClient stealth {}".format(
-                tor_service['client'])
-        assert f.contains("^{}$".format(auth_regex))
-        service_regex += "\n{}".format(auth_regex)
-
     # Check for block in file, to ensure declaration order
+    service_regex = "\n".join([dir_regex, port_regex])
     assert service_regex in f.content_string