Repository: freedomofpress/securedrop

Issue:
Swapfile not really disabled
The Ansible config tries to disable swapfile on the Application and Monitor Servers, via `swapoff -a`. This works, but only for the current boot cycle. If a machine is configured with a swapfile in `/etc/fstab`, that swapfile will be restored on a subsequent reboot. Since the machines reboot nightly, the `swapoff -a` approach is close to useless.

In order to disable swap effectively, the first-run Ansible config should ensure that no swap entries exist in fstab, removing them if found. 


Discussion:
This may effectively undermine the efforts of https://github.com/freedomofpress/securedrop/commit/1dbab3c26f0c6a2275a28d24c287705694ca2108 to minimize the duration of plaintext in memory, and should be resolved in the 0.4 release. Last time I checked it is the default to include a swapfile in `fstab` when using "guided partitioning" in the Ubuntu installer.
# Next steps

- [x] Verify that swap is re-enabled after reboot on our test instance.
- [ ] Announce intent to release 0.3.12 on https://securedrop.org/news ahead of release, so admins have some prior warning.

# Goals

1. Ensure swap is disabled now and forever on all instances after 0.3.12 is installed.
2. Securely erase any existing swap that may have been populated with sensitive data prior to the 0.3.12 release.
3. This update should be automatic and not require intervention from the admins. Therefore, we need to investigate how best to achieve these goals within the environment of the `securedrop-app-code` Debian package and its postinstall script.
Have a working implementation for detecting active swap, disabling it, shredding it, and removing the fstab entry. The changes are idempotent. Will place them in the `preinst` script in the `securedrop-app-code` package and begin testing in VMs and hardware.

Resolving patch:
diff --git a/docs/conf.py b/docs/conf.py
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -59,9 +59,9 @@
 # built documents.
 #
 # The short X.Y version.
-version = '0.3.11'
+version = '0.3.12'
 # The full version, including alpha/beta/rc tags.
-release = '0.3.11'
+release = '0.3.12'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
diff --git a/securedrop/version.py b/securedrop/version.py
--- a/securedrop/version.py
+++ b/securedrop/version.py
@@ -1 +1 @@
-__version__ = '0.3.11'
+__version__ = '0.3.12'


Test patch:
diff --git a/testinfra/common/test_system_hardening.py b/testinfra/common/test_system_hardening.py
--- a/testinfra/common/test_system_hardening.py
+++ b/testinfra/common/test_system_hardening.py
@@ -1,6 +1,9 @@
+import os
 import pytest
 import re
 
+hostenv = os.environ['SECUREDROP_TESTINFRA_TARGET_HOST']
+
 
 @pytest.mark.parametrize('sysctl_opt', [
   ('net.ipv4.conf.all.accept_redirects', 0),
@@ -60,9 +63,8 @@ def test_blacklisted_kernel_modules(Command, File, Sudo, kernel_module):
     assert f.contains("^blacklist {}$".format(kernel_module))
 
 
-# Expecting failure here, since the Ansible config doesn't actually
-# disable swap, as intended. (It doesn't manage /etc/fstab.)
-@pytest.mark.xfail
+@pytest.mark.skipif(hostenv.startswith('mon'),
+        reason="Monitor Server does not have swap disabled yet.")
 def test_swap_disabled(Command):
     """
     Ensure swap space is disabled. Prohibit writing memory to swapfiles
@@ -73,4 +75,4 @@ def test_swap_disabled(Command):
     # A leading slash will indicate full path to a swapfile.
     assert not re.search("^/", c.stdout, re.M)
     # Expect that ONLY the headers will be present in the output.
-    assert c.stdout == "Filename\t\t\t\tType\t\tSize\tUsed\tPriority\n"
+    assert c.stdout == "Filename\t\t\t\tType\t\tSize\tUsed\tPriority"
diff --git a/testinfra/common/test_user_config.py b/testinfra/common/test_user_config.py
--- a/testinfra/common/test_user_config.py
+++ b/testinfra/common/test_user_config.py
@@ -1,8 +1,10 @@
-import re
 import os
+import pytest
+import re
 
 hostenv = os.environ['SECUREDROP_TESTINFRA_TARGET_HOST']
 
+
 def test_sudoers_config(File, Sudo):
     """
     Check sudoers config for passwordless sudo via group membership,