From 4bb82d4801862b38b9aa4de5f49cb490de613c4b Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Mon, 22 Feb 2021 14:03:46 -0500 Subject: [PATCH 1/6] Disables IPv6 via cmdline option for Focal Adds a Focal-only cmdline option for the boot to disable IPv6 functionality completely. Adds a config test to ensure no IPv6 addresses are assigned. Since the IPv6 stack is disabled at boot time, the associated sysctl tasks won't exist. Therefore we'll add those only on Xenial. This is the type of config that could be moved into a metapackage. --- .../roles/common/defaults/main.yml | 3 ++ .../roles/common/tasks/sysctl.yml | 13 +++++++++ .../securedrop-grsec-focal/DEBIAN/postinst.j2 | 2 +- molecule/testinfra/common/test_grsecurity.py | 12 ++++++++ molecule/testinfra/common/test_ip6tables.py | 29 ++++++++++++++++++- .../testinfra/common/test_system_hardening.py | 3 ++ 6 files changed, 60 insertions(+), 2 deletions(-) diff --git a/install_files/ansible-base/roles/common/defaults/main.yml b/install_files/ansible-base/roles/common/defaults/main.yml index 3850dd4208..d2c3ae7a38 100644 --- a/install_files/ansible-base/roles/common/defaults/main.yml +++ b/install_files/ansible-base/roles/common/defaults/main.yml @@ -38,6 +38,9 @@ sysctl_flags: value: "0" - name: "net.ipv4.conf.default.send_redirects" value: "0" + +# Store IPv6-related sysctl flags separately, for distro-specific handling +sysctl_flags_ipv6: - name: "net.ipv6.conf.all.disable_ipv6" value: "1" - name: "net.ipv6.conf.default.disable_ipv6" diff --git a/install_files/ansible-base/roles/common/tasks/sysctl.yml b/install_files/ansible-base/roles/common/tasks/sysctl.yml index e05adc5415..5c542cb4ea 100644 --- a/install_files/ansible-base/roles/common/tasks/sysctl.yml +++ b/install_files/ansible-base/roles/common/tasks/sysctl.yml @@ -12,3 +12,16 @@ tags: - sysctl - hardening + +- name: Set sysctl flags for net.ipv6 config. + sysctl: + name: "{{ item.name }}" + value: "{{ item.value }}" + sysctl_set: yes + state: present + reload: yes + with_items: "{{ sysctl_flags_ipv6 }}" + when: ansible_distribution_release == "xenial" + tags: + - sysctl + - hardening diff --git a/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 b/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 index 6d251e9b03..6d7d0d8b87 100755 --- a/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 +++ b/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 @@ -28,7 +28,7 @@ set_grub_default() { # When using CONFIG_PAX_KERNEXEC, the grsecurity team recommends the kernel # is booted with "noefi" on the kernel command line if "CONFIG_EFI" is # enabled, as EFI runtime services are necessarily mapped as RWX. - sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/=.*/=\"noefi\"/' /etc/default/grub + perl -pi -e 's|^GRUB_CMDLINE_LINUX_DEFAULT=|GRUB_CMDLINE_LINUX_DEFAULT="noefi ipv6.disable=1"|' /etc/default/grub update-grub } diff --git a/molecule/testinfra/common/test_grsecurity.py b/molecule/testinfra/common/test_grsecurity.py index 6b4af061e0..3bf05d23df 100644 --- a/molecule/testinfra/common/test_grsecurity.py +++ b/molecule/testinfra/common/test_grsecurity.py @@ -277,3 +277,15 @@ def test_mds_mitigations_and_smt_disabled(host): grub_config = host.file(grub_config_path) assert grub_config.contains("mds=full,nosmt") + + +def test_kernel_boot_options(host): + """ + Ensure command-line options for currently booted kernel are set. + """ + with host.sudo(): + f = host.file("/proc/cmdline") + boot_opts = f.content_string.split() + assert "noefi" in boot_opts + if host.system_info.codename == "focal": + assert "ipv6.disable=1" in boot_opts diff --git a/molecule/testinfra/common/test_ip6tables.py b/molecule/testinfra/common/test_ip6tables.py index 8f7497731b..ecc6e29c3f 100644 --- a/molecule/testinfra/common/test_ip6tables.py +++ b/molecule/testinfra/common/test_ip6tables.py @@ -4,11 +4,15 @@ testinfra_hosts = [test_vars.app_hostname, test_vars.monitor_hostname] -def test_ip6tables_drop_everything(host): +def test_ip6tables_drop_everything_xenial(host): """ Ensure that all IPv6 packets are dropped by default. The IPv4 rules are more complicated, and tested separately. + This test is Xenial-specific, given that on Focal we disable + IPv6 functionality completely. """ + if host.system_info.codename != "xenial": + return True desired_ip6tables_output = """ -P INPUT DROP -P FORWARD DROP @@ -18,3 +22,26 @@ def test_ip6tables_drop_everything(host): with host.sudo(): c = host.check_output("ip6tables -S") assert c == desired_ip6tables_output + + +def test_ip6tables_drop_everything_focal(host): + """ + Ensures that IPv6 firewall settings are inaccessible, + due to fully disabling IPv6 functionality at boot-time, + via boot options. + """ + if host.system_info.codename != "focal": + return True + with host.sudo(): + c = host.run("ip6tables -S") + assert c.rc != 0 + assert c.stdout == "" + + +def test_ipv6_addresses_absent(host): + """ + Ensure that no IPv6 addresses are assigned to interfaces. + """ + with host.sudo(): + c = host.check_output("ip -6 addr") + assert c == "" diff --git a/molecule/testinfra/common/test_system_hardening.py b/molecule/testinfra/common/test_system_hardening.py index 283e49a840..d7825a3bd7 100644 --- a/molecule/testinfra/common/test_system_hardening.py +++ b/molecule/testinfra/common/test_system_hardening.py @@ -33,6 +33,9 @@ def test_sysctl_options(host, sysctl_opt): due to the heavy use of Tor. """ with host.sudo(): + # For Focal, we disable IPv6 entirely, so the IPv6 sysctl options won't exist + if sysctl_opt[0].startswith("net.ipv6") and host.system_info.codename == "focal": + return True assert host.sysctl(sysctl_opt[0]) == sysctl_opt[1] From 86865b1306095294fc802994e27cbc0c7b496442 Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Tue, 23 Feb 2021 11:26:12 -0500 Subject: [PATCH 2/6] Hold securedrop-grsec package in staging Ensures that the "securedrop-grsec" package built locally for staging takes precedence, so that the version served from the apt-test.freedom.press repository doesn't win out. --- install_files/ansible-base/group_vars/all/securedrop | 3 +++ .../group_vars/securedrop_application_server.yml | 1 + .../ansible-base/group_vars/securedrop_monitor_server.yml | 1 + .../grsecurity/tasks/from_local_pkg_install_grsec.yml | 7 +++---- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/install_files/ansible-base/group_vars/all/securedrop b/install_files/ansible-base/group_vars/all/securedrop index 218baf1f8f..a9be5850b9 100644 --- a/install_files/ansible-base/group_vars/all/securedrop +++ b/install_files/ansible-base/group_vars/all/securedrop @@ -45,3 +45,6 @@ securedrop_pkg_grsec_xenial: securedrop_pkg_grsec_focal: ver: "5.4.97" depends: "linux-image-5.4.97-grsec-securedrop,intel-microcode" + +# Mostly useful for local package installation +grsec_version: "{{ securedrop_pkg_grsec_xenial.ver if securedrop_target_distribution == 'xenial' else securedrop_pkg_grsec_focal.ver }}" diff --git a/install_files/ansible-base/group_vars/securedrop_application_server.yml b/install_files/ansible-base/group_vars/securedrop_application_server.yml index e81244a649..c5f9c6b196 100644 --- a/install_files/ansible-base/group_vars/securedrop_application_server.yml +++ b/install_files/ansible-base/group_vars/securedrop_application_server.yml @@ -9,6 +9,7 @@ local_deb_packages: - "securedrop-keyring-0.1.4+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" - "securedrop-config-0.1.4+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" - "securedrop-ossec-agent-3.6.0+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" + - securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb - "{{ securedrop_app_code_deb }}.deb" - "ossec-agent-3.6.0+{{ securedrop_target_distribution }}-amd64.deb" diff --git a/install_files/ansible-base/group_vars/securedrop_monitor_server.yml b/install_files/ansible-base/group_vars/securedrop_monitor_server.yml index ed4a28eeb7..73ab530daa 100644 --- a/install_files/ansible-base/group_vars/securedrop_monitor_server.yml +++ b/install_files/ansible-base/group_vars/securedrop_monitor_server.yml @@ -9,6 +9,7 @@ local_deb_packages: - "securedrop-keyring-0.1.4+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" - "securedrop-config-0.1.4+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" - "securedrop-ossec-server-3.6.0+{{ securedrop_version }}+{{ securedrop_target_distribution }}-amd64.deb" + - securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb - ossec-server-3.6.0+{{ securedrop_target_distribution }}-amd64.deb # Configure the tor onion services. The Monitor server has only one, diff --git a/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml b/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml index adfe2e5b70..d81f0ee637 100644 --- a/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml +++ b/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml @@ -1,8 +1,4 @@ --- -- name: Get the grsec version of the current scenario - set_fact: - grsec_version: "{% if ansible_distribution_release == 'xenial' %}{{ securedrop_pkg_grsec_xenial.ver }}{% else %}{{ securedrop_pkg_grsec_focal.ver }}{% endif %}" - - name: Copy locally built securedrop-grsec metapackage copy: src: "../../build/{{ securedrop_target_distribution }}/securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb" @@ -10,3 +6,6 @@ - name: Install locally built securedrop-grsec metapackage command: apt-get install -y -f /root/securedrop-grsec.deb + +- name: Mark package as held, so it doesn't update to apt-test version + command: apt-mark hold securedrop-grsec From d1a50ccbe57943f7d5b708219fddca084bb0ca6e Mon Sep 17 00:00:00 2001 From: Kushal Das Date: Wed, 24 Feb 2021 14:17:20 +0530 Subject: [PATCH 3/6] Adds back sed command for grub Adding the old style sed command instead of the perl command. This makes sure that we have only one value within double quotes in the correct location in /etc/default/grub. --- install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 b/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 index 6d7d0d8b87..88c20a1172 100755 --- a/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 +++ b/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 @@ -28,7 +28,7 @@ set_grub_default() { # When using CONFIG_PAX_KERNEXEC, the grsecurity team recommends the kernel # is booted with "noefi" on the kernel command line if "CONFIG_EFI" is # enabled, as EFI runtime services are necessarily mapped as RWX. - perl -pi -e 's|^GRUB_CMDLINE_LINUX_DEFAULT=|GRUB_CMDLINE_LINUX_DEFAULT="noefi ipv6.disable=1"|' /etc/default/grub + sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/=.*/=\"noefi ipv6\.disable=1 quiet\"/' /etc/default/grub update-grub } From 0b828192587e68cc26cc289e28347967ffcec214 Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Wed, 24 Feb 2021 09:45:42 -0500 Subject: [PATCH 4/6] Uses same grsec pkg name for staging --- .../roles/grsecurity/tasks/from_local_pkg_install_grsec.yml | 4 ++-- .../roles/install-local-packages/tasks/hold_debs.yml | 6 +----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml b/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml index d81f0ee637..71bf11f9fa 100644 --- a/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml +++ b/install_files/ansible-base/roles/grsecurity/tasks/from_local_pkg_install_grsec.yml @@ -2,10 +2,10 @@ - name: Copy locally built securedrop-grsec metapackage copy: src: "../../build/{{ securedrop_target_distribution }}/securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb" - dest: /root/securedrop-grsec.deb + dest: "/root/securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb" - name: Install locally built securedrop-grsec metapackage - command: apt-get install -y -f /root/securedrop-grsec.deb + command: apt-get install -y -f "/root/securedrop-grsec-{{ grsec_version }}+{{ securedrop_target_distribution }}-amd64.deb" - name: Mark package as held, so it doesn't update to apt-test version command: apt-mark hold securedrop-grsec diff --git a/install_files/ansible-base/roles/install-local-packages/tasks/hold_debs.yml b/install_files/ansible-base/roles/install-local-packages/tasks/hold_debs.yml index 063f0411ed..1a1e604c77 100644 --- a/install_files/ansible-base/roles/install-local-packages/tasks/hold_debs.yml +++ b/install_files/ansible-base/roles/install-local-packages/tasks/hold_debs.yml @@ -13,9 +13,5 @@ - name: Mark packages as held, so they aren't upgraded automatically (via apt). command: apt-mark hold {{ item.stdout }} register: apt_mark_hold_result - # The packages will have the "hold" state cleared during the previous task - # for `dpkg -i `. Therefore let's determine changed state by comparing - # to the value prior to installation. - changed_when: item.stdout not in apt_mark_showhold_result.stdout_lines - when: item.stdout not in apt_mark_showhold_result.stdout_lines + changed_when: not apt_mark_showhold_result.stdout.endswith("was already set on hold") with_items: "{{ local_deb_packages_name_check.results }}" From a6f7e02e36fa51600e10943d98ae2104004098a7 Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Wed, 24 Feb 2021 10:18:59 -0500 Subject: [PATCH 5/6] Makes vbox service disabling fail gracefully The VirtualBox services are CI/dev-specific, and won't exist on hardware. Don't fail if the services aren't found. --- .../tasks/post_ubuntu_install_checks.yml | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/install_files/ansible-base/roles/common/tasks/post_ubuntu_install_checks.yml b/install_files/ansible-base/roles/common/tasks/post_ubuntu_install_checks.yml index e562304c03..d2b82eddd2 100644 --- a/install_files/ansible-base/roles/common/tasks/post_ubuntu_install_checks.yml +++ b/install_files/ansible-base/roles/common/tasks/post_ubuntu_install_checks.yml @@ -53,22 +53,16 @@ tags: - ntp -- name: Disable VirtualBox service vboxadd to avoid conflict with systemd-timesyncd. +- name: Disable VirtualBox services to avoid conflict with systemd-timesyncd. systemd: - name: vboxadd - enabled: no - state: stopped - when: ansible_distribution_release == "focal" - become: yes - tags: - - ntp - -- name: Disable VirtualBox service vboxadd-service to avoid conflict with systemd-timesyncd. - systemd: - name: vboxadd-service + name: "{{ item }}" enabled: no state: stopped when: ansible_distribution_release == "focal" + failed_when: false + with_items: + - vboxadd + - vboxadd-service become: yes tags: - ntp From 600fcfbd167adc1f0bc14ad2cf477c5662a88630 Mon Sep 17 00:00:00 2001 From: Conor Schaefer Date: Wed, 24 Feb 2021 10:21:15 -0500 Subject: [PATCH 6/6] Makes unattended-upgrades test read-only Adds the "dry-run" flag so that system state is not changed during the test. --- molecule/testinfra/common/test_automatic_updates.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/molecule/testinfra/common/test_automatic_updates.py b/molecule/testinfra/common/test_automatic_updates.py index 63746bcf4b..fd4d016ec4 100644 --- a/molecule/testinfra/common/test_automatic_updates.py +++ b/molecule/testinfra/common/test_automatic_updates.py @@ -243,7 +243,7 @@ def test_unattended_upgrades_functional(host): are up-to-date. """ if host.system_info.codename != "xenial": - c = host.run('sudo unattended-upgrades -d') + c = host.run('sudo unattended-upgrades --dry-run --debug') assert c.rc == 0 expected_origins = ( "Allowed origins are: origin=Ubuntu,archive=focal, origin=Ubuntu,archive=focal-security"