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/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/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 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/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..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 @@ -1,12 +1,11 @@ --- -- 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" - 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 }}" diff --git a/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 b/install_files/securedrop-grsec-focal/DEBIAN/postinst.j2 index 6d251e9b03..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. - sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/=.*/=\"noefi\"/' /etc/default/grub + sed -i '/^GRUB_CMDLINE_LINUX_DEFAULT=/s/=.*/=\"noefi ipv6\.disable=1 quiet\"/' /etc/default/grub update-grub } 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" 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]