Repository: freedomofpress/securedrop

Issue:
Investigate running test_infra tests against production HW
## Description

Currently, QA testing involves a "basic server testing" phase that duplicates some checks already present in the test_infra tests while missing a whole bunch of others. It would be useful to be able to automate this testing phase and do it in more detail using the test_infra suite. Failing that, a script that could be run from the Admin Workstation to do basic server testing would be a useful thing.

## User Stories

As a SecureDrop Tester, I want to be able to automatically run basic server tests against production servers.


Discussion:
Tentatively adding to 1.4.0 milestone per today's sprint retrospective; please don't hesitate to nominate other issues on the QA automation front that should be higher priority or also added.

Resolving patch:
diff --git a/admin/bootstrap.py b/admin/bootstrap.py
--- a/admin/bootstrap.py
+++ b/admin/bootstrap.py
@@ -197,7 +197,18 @@ def envsetup(args, virtualenv_dir=VENV_DIR):
     else:
         sdlog.info("Virtualenv already exists, not creating")
 
-    install_pip_dependencies(args)
+    if args.t:
+        install_pip_dependencies(args, pip_install_cmd=[
+            os.path.join(VENV_DIR, 'bin', 'pip3'),
+            'install',
+            '--no-deps',
+            '-r', os.path.join(DIR, 'requirements-testinfra.txt'),
+            '--require-hashes',
+            '-U', '--upgrade-strategy', 'only-if-needed', ],
+            desc="dependencies with verification support")
+    else:
+        install_pip_dependencies(args)
+
     if os.path.exists(os.path.join(DIR, 'setup.py')):
         install_pip_self(args)
 
@@ -226,33 +237,36 @@ def install_pip_dependencies(args, pip_install_cmd=[
         '-r', os.path.join(DIR, 'requirements.txt'),
         '--require-hashes',
         # Make sure to upgrade packages only if necessary.
-        '-U', '--upgrade-strategy', 'only-if-needed',
-]):
+        '-U', '--upgrade-strategy', 'only-if-needed', ],
+        desc="Python dependencies"
+):
     """
     Install Python dependencies via pip into virtualenv.
     """
 
-    sdlog.info("Checking Python dependencies for securedrop-admin")
+    sdlog.info("Checking {} for securedrop-admin".format(desc))
     try:
         pip_output = subprocess.check_output(maybe_torify() + pip_install_cmd,
                                              stderr=subprocess.STDOUT)
     except subprocess.CalledProcessError as e:
         sdlog.debug(e.output)
-        sdlog.error(("Failed to install pip dependencies. Check network"
-                     " connection and try again."))
+        sdlog.error(("Failed to install {}. Check network"
+                     " connection and try again.".format(desc)))
         raise
 
     sdlog.debug(pip_output)
     if "Successfully installed" in str(pip_output):
-        sdlog.info("Python dependencies for securedrop-admin upgraded")
+        sdlog.info("{} for securedrop-admin upgraded".format(desc))
     else:
-        sdlog.info("Python dependencies for securedrop-admin are up-to-date")
+        sdlog.info("{} for securedrop-admin are up-to-date".format(desc))
 
 
 def parse_argv(argv):
     parser = argparse.ArgumentParser()
     parser.add_argument('-v', action='store_true', default=False,
                         help="Increase verbosity on output")
+    parser.add_argument('-t', action='store_true', default=False,
+                        help="Install additional test dependencies")
     parser.set_defaults(func=envsetup)
 
     subparsers = parser.add_subparsers()
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
@@ -753,6 +753,15 @@ def install_securedrop(args):
                                  cwd=args.ansible_path)
 
 
+def verify_install(args):
+    """Run configuration tests against SecureDrop servers"""
+
+    sdlog.info("Running configuration tests: ")
+    testinfra_cmd = ["./devops/scripts/run_prod_testinfra"]
+    return subprocess.check_call(testinfra_cmd,
+                                 cwd=os.getcwd())
+
+
 def backup_securedrop(args):
     """Perform backup of the SecureDrop Application Server.
     Creates a tarball of submissions and server config, and fetches
@@ -1050,6 +1059,10 @@ class ArgParseFormatterCombo(argparse.ArgumentDefaultsHelpFormatter,
                                             help=reset_admin_access.__doc__)
     parse_reset_ssh.set_defaults(func=reset_admin_access)
 
+    parse_verify = subparsers.add_parser('verify',
+                                         help=verify_install.__doc__)
+    parse_verify.set_defaults(func=verify_install)
+
     args = parser.parse_args(argv)
     if getattr(args, 'func', None) is None:
         print('Please specify an operation.\n')
diff --git a/molecule/testinfra/staging/conftest.py b/molecule/testinfra/conftest.py
similarity index 100%
rename from molecule/testinfra/staging/conftest.py
rename to molecule/testinfra/conftest.py


Test patch:
diff --git a/molecule/testinfra/staging/app-code/test_haveged.py b/molecule/testinfra/app-code/test_haveged.py
similarity index 92%
rename from molecule/testinfra/staging/app-code/test_haveged.py
rename to molecule/testinfra/app-code/test_haveged.py
--- a/molecule/testinfra/staging/app-code/test_haveged.py
+++ b/molecule/testinfra/app-code/test_haveged.py
@@ -1,4 +1,7 @@
-testinfra_hosts = ["app-staging"]
+import pytest
+
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_haveged_config(host):
diff --git a/molecule/testinfra/staging/app-code/test_securedrop_app_code.py b/molecule/testinfra/app-code/test_securedrop_app_code.py
similarity index 97%
rename from molecule/testinfra/staging/app-code/test_securedrop_app_code.py
rename to molecule/testinfra/app-code/test_securedrop_app_code.py
--- a/molecule/testinfra/staging/app-code/test_securedrop_app_code.py
+++ b/molecule/testinfra/app-code/test_securedrop_app_code.py
@@ -1,8 +1,8 @@
 import pytest
 
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
 def test_apache_default_docroot_is_absent(host):
@@ -38,6 +38,7 @@ def test_securedrop_application_apt_dependencies(host, package):
     assert host.package(package).is_installed
 
 
+@pytest.mark.skip_in_prod
 def test_securedrop_application_test_locale(host):
     """
     Ensure both SecureDrop DEFAULT_LOCALE and SUPPORTED_LOCALES are present.
@@ -52,6 +53,7 @@ def test_securedrop_application_test_locale(host):
         assert "\nSUPPORTED_LOCALES = ['el', 'ar', 'en_US']\n" in securedrop_config.content_string
 
 
+@pytest.mark.skip_in_prod
 def test_securedrop_application_test_journalist_key(host):
     """
     Ensure the SecureDrop Application GPG public key file is present.
diff --git a/molecule/testinfra/staging/app-code/test_securedrop_rqrequeue.py b/molecule/testinfra/app-code/test_securedrop_rqrequeue.py
similarity index 95%
rename from molecule/testinfra/staging/app-code/test_securedrop_rqrequeue.py
rename to molecule/testinfra/app-code/test_securedrop_rqrequeue.py
--- a/molecule/testinfra/staging/app-code/test_securedrop_rqrequeue.py
+++ b/molecule/testinfra/app-code/test_securedrop_rqrequeue.py
@@ -1,7 +1,7 @@
 import pytest
 
-
-testinfra_hosts = ["app-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_securedrop_rqrequeue_service(host):
diff --git a/molecule/testinfra/staging/app-code/test_securedrop_rqworker.py b/molecule/testinfra/app-code/test_securedrop_rqworker.py
similarity index 95%
rename from molecule/testinfra/staging/app-code/test_securedrop_rqworker.py
rename to molecule/testinfra/app-code/test_securedrop_rqworker.py
--- a/molecule/testinfra/staging/app-code/test_securedrop_rqworker.py
+++ b/molecule/testinfra/app-code/test_securedrop_rqworker.py
@@ -1,7 +1,7 @@
 import pytest
 
-
-testinfra_hosts = ["app-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_securedrop_rqworker_service(host):
diff --git a/molecule/testinfra/staging/app-code/test_securedrop_shredder_configuration.py b/molecule/testinfra/app-code/test_securedrop_shredder_configuration.py
similarity index 95%
rename from molecule/testinfra/staging/app-code/test_securedrop_shredder_configuration.py
rename to molecule/testinfra/app-code/test_securedrop_shredder_configuration.py
--- a/molecule/testinfra/staging/app-code/test_securedrop_shredder_configuration.py
+++ b/molecule/testinfra/app-code/test_securedrop_shredder_configuration.py
@@ -1,7 +1,7 @@
 import pytest
 
-
-testinfra_hosts = ["app-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_securedrop_shredder_service(host):
diff --git a/molecule/testinfra/staging/app-code/test_securedrop_source_deleter_configuration.py b/molecule/testinfra/app-code/test_securedrop_source_deleter_configuration.py
similarity index 95%
rename from molecule/testinfra/staging/app-code/test_securedrop_source_deleter_configuration.py
rename to molecule/testinfra/app-code/test_securedrop_source_deleter_configuration.py
--- a/molecule/testinfra/staging/app-code/test_securedrop_source_deleter_configuration.py
+++ b/molecule/testinfra/app-code/test_securedrop_source_deleter_configuration.py
@@ -1,7 +1,7 @@
 import pytest
 
-
-testinfra_hosts = ["app-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_securedrop_source_deleter_service(host):
diff --git a/molecule/testinfra/staging/app/apache/test_apache_journalist_interface.py b/molecule/testinfra/app/apache/test_apache_journalist_interface.py
similarity index 99%
rename from molecule/testinfra/staging/app/apache/test_apache_journalist_interface.py
rename to molecule/testinfra/app/apache/test_apache_journalist_interface.py
--- a/molecule/testinfra/staging/app/apache/test_apache_journalist_interface.py
+++ b/molecule/testinfra/app/apache/test_apache_journalist_interface.py
@@ -2,8 +2,8 @@
 import re
 
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 # Setting once so it can be reused in multiple tests.
 wanted_apache_headers = [
@@ -28,6 +28,7 @@
   'Header set Referrer-Policy "no-referrer"',
 ]
 
+
 # Test is not DRY; haven't figured out how to parametrize on
 # multiple inputs, so explicitly redeclaring test logic.
 @pytest.mark.parametrize("header", wanted_apache_headers)
diff --git a/molecule/testinfra/staging/app/apache/test_apache_service.py b/molecule/testinfra/app/apache/test_apache_service.py
similarity index 97%
rename from molecule/testinfra/staging/app/apache/test_apache_service.py
rename to molecule/testinfra/app/apache/test_apache_service.py
--- a/molecule/testinfra/staging/app/apache/test_apache_service.py
+++ b/molecule/testinfra/app/apache/test_apache_service.py
@@ -1,8 +1,8 @@
 import pytest
 
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
 @pytest.mark.parametrize("apache_site", [
diff --git a/molecule/testinfra/staging/app/apache/test_apache_source_interface.py b/molecule/testinfra/app/apache/test_apache_source_interface.py
similarity index 97%
rename from molecule/testinfra/staging/app/apache/test_apache_source_interface.py
rename to molecule/testinfra/app/apache/test_apache_source_interface.py
--- a/molecule/testinfra/staging/app/apache/test_apache_source_interface.py
+++ b/molecule/testinfra/app/apache/test_apache_source_interface.py
@@ -1,8 +1,8 @@
 import pytest
 import re
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
 @pytest.mark.parametrize("header", securedrop_test_vars.wanted_apache_headers)
diff --git a/molecule/testinfra/staging/app/apache/test_apache_system_config.py b/molecule/testinfra/app/apache/test_apache_system_config.py
similarity index 94%
rename from molecule/testinfra/staging/app/apache/test_apache_system_config.py
rename to molecule/testinfra/app/apache/test_apache_system_config.py
--- a/molecule/testinfra/staging/app/apache/test_apache_system_config.py
+++ b/molecule/testinfra/app/apache/test_apache_system_config.py
@@ -1,8 +1,8 @@
 import pytest
 import re
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
 @pytest.mark.parametrize("package", [
@@ -158,9 +158,11 @@ def test_apache_logfiles_no_extras(host):
     `test_apache_logfiles_present` config test. Here, we confirm that the
     total number of Apache logfiles exactly matches the number permitted
     on the Application Server, whether staging or prod.
+    Long-running instances may have rotated and gzipped logfiles, so this
+    test should only look for files ending in '.log'.
     """
     # We need elevated privileges to read files inside /var/log/apache2
     with host.sudo():
-        c = host.run("find /var/log/apache2 -mindepth 1 | wc -l")
+        c = host.run("find /var/log/apache2 -mindepth 1 -name '*.log' | wc -l")
         assert int(c.stdout) == \
             len(securedrop_test_vars.allowed_apache_logfiles)
diff --git a/molecule/testinfra/staging/app/test_app_network.py b/molecule/testinfra/app/test_app_network.py
similarity index 95%
rename from molecule/testinfra/staging/app/test_app_network.py
rename to molecule/testinfra/app/test_app_network.py
--- a/molecule/testinfra/staging/app/test_app_network.py
+++ b/molecule/testinfra/app/test_app_network.py
@@ -5,10 +5,11 @@
 from jinja2 import Template
 
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
+@pytest.mark.skip_in_prod
 def test_app_iptables_rules(host):
 
     # Build a dict of variables to pass to jinja for iptables comparison
diff --git a/molecule/testinfra/staging/app/test_apparmor.py b/molecule/testinfra/app/test_apparmor.py
similarity index 99%
rename from molecule/testinfra/staging/app/test_apparmor.py
rename to molecule/testinfra/app/test_apparmor.py
--- a/molecule/testinfra/staging/app/test_apparmor.py
+++ b/molecule/testinfra/app/test_apparmor.py
@@ -1,8 +1,8 @@
 import pytest
 
 
-testinfra_hosts = ["app-staging"]
 sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 @pytest.mark.parametrize('pkg', ['apparmor', 'apparmor-utils'])
diff --git a/molecule/testinfra/staging/app/test_appenv.py b/molecule/testinfra/app/test_appenv.py
similarity index 96%
rename from molecule/testinfra/staging/app/test_appenv.py
rename to molecule/testinfra/app/test_appenv.py
--- a/molecule/testinfra/staging/app/test_appenv.py
+++ b/molecule/testinfra/app/test_appenv.py
@@ -1,8 +1,8 @@
 import os.path
 import pytest
 
-testinfra_hosts = ["app-staging"]
 sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 @pytest.mark.parametrize('exp_pip_pkg', sdvars.pip_deps)
@@ -12,6 +12,7 @@ def test_app_pip_deps(host, exp_pip_pkg):
     assert pip[exp_pip_pkg['name']]['version'] == exp_pip_pkg['version']
 
 
+@pytest.mark.skip_in_prod
 def test_app_wsgi(host):
     """ ensure logging is enabled for source interface in staging """
     f = host.file("/var/www/source.wsgi")
@@ -63,6 +64,7 @@ def test_supervisor_not_installed(host):
     assert host.package("supervisor").is_installed is False
 
 
+@pytest.mark.skip_in_prod
 def test_gpg_key_in_keyring(host):
     """ ensure test gpg key is present in app keyring """
     with host.sudo(sdvars.securedrop_user):
diff --git a/molecule/testinfra/staging/app/test_ossec_agent.py b/molecule/testinfra/app/test_ossec_agent.py
similarity index 97%
rename from molecule/testinfra/staging/app/test_ossec_agent.py
rename to molecule/testinfra/app/test_ossec_agent.py
--- a/molecule/testinfra/staging/app/test_ossec_agent.py
+++ b/molecule/testinfra/app/test_ossec_agent.py
@@ -3,7 +3,7 @@
 import pytest
 
 sdvars = pytest.securedrop_test_vars
-testinfra_hosts = ["app", "app-staging"]
+testinfra_hosts = [sdvars.app_hostname]
 
 
 def test_hosts_files(host):
diff --git a/molecule/testinfra/staging/app/test_paxctld.py b/molecule/testinfra/app/test_paxctld.py
similarity index 94%
rename from molecule/testinfra/staging/app/test_paxctld.py
rename to molecule/testinfra/app/test_paxctld.py
--- a/molecule/testinfra/staging/app/test_paxctld.py
+++ b/molecule/testinfra/app/test_paxctld.py
@@ -2,8 +2,8 @@
 import re
 
 
-testinfra_hosts = ["app-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.app_hostname]
 
 
 def test_paxctld_installed(host):
diff --git a/molecule/testinfra/staging/app/test_tor_config.py b/molecule/testinfra/app/test_tor_config.py
similarity index 96%
rename from molecule/testinfra/staging/app/test_tor_config.py
rename to molecule/testinfra/app/test_tor_config.py
--- a/molecule/testinfra/staging/app/test_tor_config.py
+++ b/molecule/testinfra/app/test_tor_config.py
@@ -1,8 +1,8 @@
 import pytest
 import re
 
-testinfra_hosts = ["app-staging"]
 sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
 
 @pytest.mark.parametrize('package', [
@@ -61,6 +61,7 @@ def test_tor_torrc_sandbox(host):
     assert not f.contains("^.*Sandbox.*$")
 
 
+@pytest.mark.skip_in_prod
 def test_tor_v2_onion_url_readable_by_app(host):
     v2_url_filepath = "/var/lib/securedrop/source_v2_url"
     with host.sudo():
@@ -71,6 +72,7 @@ def test_tor_v2_onion_url_readable_by_app(host):
         assert re.search(r"^[a-z0-9]{16}\.onion$", f.content_string)
 
 
+@pytest.mark.skip_in_prod
 def test_tor_v3_onion_url_readable_by_app(host):
     v3_url_filepath = "/var/lib/securedrop/source_v3_url"
     with host.sudo():
diff --git a/molecule/testinfra/staging/app/test_tor_hidden_services.py b/molecule/testinfra/app/test_tor_hidden_services.py
similarity index 95%
rename from molecule/testinfra/staging/app/test_tor_hidden_services.py
rename to molecule/testinfra/app/test_tor_hidden_services.py
--- a/molecule/testinfra/staging/app/test_tor_hidden_services.py
+++ b/molecule/testinfra/app/test_tor_hidden_services.py
@@ -2,10 +2,12 @@
 import re
 
 
-testinfra_hosts = ["app-staging"]
 sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname]
 
-
+# Prod Tor services may have unexpected configs
+# TODO: read from admin workstation site-specific file if available
+@pytest.mark.skip_in_prod
 @pytest.mark.parametrize('tor_service', sdvars.tor_services)
 def test_tor_service_directories(host, tor_service):
     """
@@ -19,6 +21,7 @@ def test_tor_service_directories(host, tor_service):
         assert f.group == "debian-tor"
 
 
+@pytest.mark.skip_in_prod
 @pytest.mark.parametrize('tor_service', sdvars.tor_services)
 def test_tor_service_hostnames(host, tor_service):
     """
@@ -62,6 +65,7 @@ def test_tor_service_hostnames(host, tor_service):
             assert re.search("^{}$".format(ths_hostname_regex_v3), f.content_string)
 
 
+@pytest.mark.skip_in_prod
 @pytest.mark.parametrize('tor_service', sdvars.tor_services)
 def test_tor_services_config(host, tor_service):
     """
diff --git a/molecule/testinfra/staging/common/test_cron_apt.py b/molecule/testinfra/common/test_cron_apt.py
similarity index 98%
rename from molecule/testinfra/staging/common/test_cron_apt.py
rename to molecule/testinfra/common/test_cron_apt.py
--- a/molecule/testinfra/staging/common/test_cron_apt.py
+++ b/molecule/testinfra/common/test_cron_apt.py
@@ -3,6 +3,7 @@
 
 
 test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
 
 
 @pytest.mark.parametrize('dependency', [
diff --git a/molecule/testinfra/staging/common/test_fpf_apt_repo.py b/molecule/testinfra/common/test_fpf_apt_repo.py
similarity index 97%
rename from molecule/testinfra/staging/common/test_fpf_apt_repo.py
rename to molecule/testinfra/common/test_fpf_apt_repo.py
--- a/molecule/testinfra/staging/common/test_fpf_apt_repo.py
+++ b/molecule/testinfra/common/test_fpf_apt_repo.py
@@ -3,6 +3,7 @@
 
 
 test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
 
 
 def test_fpf_apt_repo_present(host):
diff --git a/molecule/testinfra/staging/common/test_grsecurity.py b/molecule/testinfra/common/test_grsecurity.py
similarity index 97%
rename from molecule/testinfra/staging/common/test_grsecurity.py
rename to molecule/testinfra/common/test_grsecurity.py
--- a/molecule/testinfra/staging/common/test_grsecurity.py
+++ b/molecule/testinfra/common/test_grsecurity.py
@@ -2,7 +2,9 @@
 import re
 
 
-KERNEL_VERSION = pytest.securedrop_test_vars.grsec_version
+sdvars = pytest.securedrop_test_vars
+KERNEL_VERSION = sdvars.grsec_version
+testinfra_hosts = [sdvars.app_hostname, sdvars.monitor_hostname]
 
 
 def test_ssh_motd_disabled(host):
@@ -88,6 +90,7 @@ def test_grsecurity_sysctl_options(host, sysctl_opt):
         assert host.sysctl(sysctl_opt[0]) == sysctl_opt[1]
 
 
+@pytest.mark.skip_in_prod
 @pytest.mark.parametrize('paxtest_check', [
   "Executable anonymous mapping",
   "Executable bss",
@@ -122,6 +125,7 @@ def test_grsecurity_paxtest(host, paxtest_check):
             assert re.search(regex, c.stdout)
 
 
+@pytest.mark.skip_in_prod
 def test_grub_pc_marked_manual(host):
     """
     Ensure the `grub-pc` packaged is marked as manually installed.
diff --git a/molecule/testinfra/staging/common/test_ip6tables.py b/molecule/testinfra/common/test_ip6tables.py
similarity index 75%
rename from molecule/testinfra/staging/common/test_ip6tables.py
rename to molecule/testinfra/common/test_ip6tables.py
--- a/molecule/testinfra/staging/common/test_ip6tables.py
+++ b/molecule/testinfra/common/test_ip6tables.py
@@ -1,3 +1,9 @@
+import pytest
+
+test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
+
+
 def test_ip6tables_drop_everything(host):
     """
     Ensure that all IPv6 packets are dropped by default.
diff --git a/molecule/testinfra/staging/common/test_platform.py b/molecule/testinfra/common/test_platform.py
similarity index 86%
rename from molecule/testinfra/staging/common/test_platform.py
rename to molecule/testinfra/common/test_platform.py
--- a/molecule/testinfra/staging/common/test_platform.py
+++ b/molecule/testinfra/common/test_platform.py
@@ -1,3 +1,8 @@
+import pytest
+
+test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
+
 # We expect Ubuntu Xenial
 SUPPORTED_CODENAMES = ('xenial')
 SUPPORTED_RELEASES = ('16.04')
diff --git a/molecule/testinfra/staging/common/test_release_upgrades.py b/molecule/testinfra/common/test_release_upgrades.py
similarity index 86%
rename from molecule/testinfra/staging/common/test_release_upgrades.py
rename to molecule/testinfra/common/test_release_upgrades.py
--- a/molecule/testinfra/staging/common/test_release_upgrades.py
+++ b/molecule/testinfra/common/test_release_upgrades.py
@@ -1,3 +1,10 @@
+import pytest
+
+
+test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
+
+
 def test_release_manager_upgrade_channel(host):
     """
     Ensures that the `do-release-upgrade` command will not
diff --git a/molecule/testinfra/staging/common/test_system_hardening.py b/molecule/testinfra/common/test_system_hardening.py
similarity index 97%
rename from molecule/testinfra/staging/common/test_system_hardening.py
rename to molecule/testinfra/common/test_system_hardening.py
--- a/molecule/testinfra/staging/common/test_system_hardening.py
+++ b/molecule/testinfra/common/test_system_hardening.py
@@ -1,7 +1,8 @@
 import pytest
 import re
 
-testinfra_hosts = ["app", "app-staging", "mon", "mon-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname, sdvars.monitor_hostname]
 
 
 @pytest.mark.parametrize('sysctl_opt', [
diff --git a/molecule/testinfra/staging/common/test_tor_mirror.py b/molecule/testinfra/common/test_tor_mirror.py
similarity index 95%
rename from molecule/testinfra/staging/common/test_tor_mirror.py
rename to molecule/testinfra/common/test_tor_mirror.py
--- a/molecule/testinfra/staging/common/test_tor_mirror.py
+++ b/molecule/testinfra/common/test_tor_mirror.py
@@ -1,5 +1,8 @@
 import pytest
 
+test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname]
+
 
 @pytest.mark.parametrize('repo_file', [
     "/etc/apt/sources.list.d/deb_torproject_org_torproject_org.list",
diff --git a/molecule/testinfra/staging/common/test_user_config.py b/molecule/testinfra/common/test_user_config.py
similarity index 95%
rename from molecule/testinfra/staging/common/test_user_config.py
rename to molecule/testinfra/common/test_user_config.py
--- a/molecule/testinfra/staging/common/test_user_config.py
+++ b/molecule/testinfra/common/test_user_config.py
@@ -1,5 +1,10 @@
 import re
 import textwrap
+import pytest
+
+
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.app_hostname, sdvars.monitor_hostname]
 
 
 def test_sudoers_config(host):
@@ -79,6 +84,7 @@ def test_tmux_installed(host):
     assert host.package("tmux").is_installed
 
 
+@pytest.mark.skip_in_prod
 def test_sudoers_tmux_env_deprecated(host):
     """
     Previous version of the Ansible config set the tmux config
diff --git a/molecule/testinfra/staging/mon/test_mon_network.py b/molecule/testinfra/mon/test_mon_network.py
similarity index 96%
rename from molecule/testinfra/staging/mon/test_mon_network.py
rename to molecule/testinfra/mon/test_mon_network.py
--- a/molecule/testinfra/staging/mon/test_mon_network.py
+++ b/molecule/testinfra/mon/test_mon_network.py
@@ -5,10 +5,11 @@
 from jinja2 import Template
 
 
-testinfra_hosts = ["mon-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.monitor_hostname]
 
 
+@pytest.mark.skip_in_prod
 def test_mon_iptables_rules(host):
 
     # Build a dict of variables to pass to jinja for iptables comparison
@@ -45,6 +46,7 @@ def test_mon_iptables_rules(host):
         assert iptables_expected == iptables
 
 
+@pytest.mark.skip_in_prod
 @pytest.mark.parametrize('ossec_service', [
     dict(host="0.0.0.0", proto="tcp", port=22, listening=True),
     dict(host="0.0.0.0", proto="udp", port=1514, listening=True),
diff --git a/molecule/testinfra/staging/mon/test_ossec_ruleset.py b/molecule/testinfra/mon/test_ossec_ruleset.py
similarity index 96%
rename from molecule/testinfra/staging/mon/test_ossec_ruleset.py
rename to molecule/testinfra/mon/test_ossec_ruleset.py
--- a/molecule/testinfra/staging/mon/test_ossec_ruleset.py
+++ b/molecule/testinfra/mon/test_ossec_ruleset.py
@@ -1,10 +1,10 @@
 import pytest
 import re
 
-testinfra_hosts = ["mon-staging"]
+sdvars = pytest.securedrop_test_vars
+testinfra_hosts = [sdvars.monitor_hostname]
 alert_level_regex = re.compile(r"Level: '(\d+)'")
 rule_id_regex = re.compile(r"Rule id: '(\d+)'")
-sdvars = pytest.securedrop_test_vars
 
 
 @pytest.mark.parametrize('log_event',
diff --git a/molecule/testinfra/staging/mon/test_ossec_server.py b/molecule/testinfra/mon/test_ossec_server.py
similarity index 98%
rename from molecule/testinfra/staging/mon/test_ossec_server.py
rename to molecule/testinfra/mon/test_ossec_server.py
--- a/molecule/testinfra/staging/mon/test_ossec_server.py
+++ b/molecule/testinfra/mon/test_ossec_server.py
@@ -2,8 +2,8 @@
 import pytest
 
 
-testinfra_hosts = ["mon-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.monitor_hostname]
 
 
 def test_ossec_connectivity(host):
diff --git a/molecule/testinfra/staging/mon/test_postfix.py b/molecule/testinfra/mon/test_postfix.py
similarity index 97%
rename from molecule/testinfra/staging/mon/test_postfix.py
rename to molecule/testinfra/mon/test_postfix.py
--- a/molecule/testinfra/staging/mon/test_postfix.py
+++ b/molecule/testinfra/mon/test_postfix.py
@@ -2,8 +2,8 @@
 import pytest
 
 
-testinfra_hosts = ["mon-staging"]
 securedrop_test_vars = pytest.securedrop_test_vars
+testinfra_hosts = [securedrop_test_vars.monitor_hostname]
 
 
 @pytest.mark.parametrize('header', [
diff --git a/molecule/testinfra/staging/ossec/test_journalist_mail.py b/molecule/testinfra/ossec/test_journalist_mail.py
similarity index 100%
rename from molecule/testinfra/staging/ossec/test_journalist_mail.py
rename to molecule/testinfra/ossec/test_journalist_mail.py