diff --git a/install_files/ansible-base/roles/common/handlers/main.yml b/install_files/ansible-base/roles/common/handlers/main.yml index 33e589e653..6386935323 100644 --- a/install_files/ansible-base/roles/common/handlers/main.yml +++ b/install_files/ansible-base/roles/common/handlers/main.yml @@ -6,3 +6,7 @@ # as necessary. - name: reboot include: "{{ role_path }}/../../tasks/reboot.yml" + +- name: update apt cache + apt: + update_cache: yes diff --git a/install_files/ansible-base/roles/common/tasks/apt_sources.yml b/install_files/ansible-base/roles/common/tasks/apt_sources.yml index 9ee323ccc9..93cd6784c8 100644 --- a/install_files/ansible-base/roles/common/tasks/apt_sources.yml +++ b/install_files/ansible-base/roles/common/tasks/apt_sources.yml @@ -4,5 +4,10 @@ dest: /etc/apt/sources.list mode: "0644" owner: root + notify: update apt cache tags: - apt + +# Ensure apt cache is updated before proceeding, otherwise +# packages may fail to install. +- meta: flush_handlers diff --git a/molecule/qubes-staging-focal/molecule.yml b/molecule/qubes-staging-focal/molecule.yml index 5481e0950f..5bb3873cf1 100644 --- a/molecule/qubes-staging-focal/molecule.yml +++ b/molecule/qubes-staging-focal/molecule.yml @@ -49,9 +49,11 @@ scenario: - converge verifier: name: testinfra - lint: + lint: | flake8 directory: ../testinfra options: n: auto v: 2 + env: + SECUREDROP_TESTINFRA_TARGET_HOST: qubes-staging diff --git a/molecule/qubes-staging-focal/qubes-vars.yml b/molecule/qubes-staging-focal/qubes-vars.yml index b09ef15bf0..3cfe31ec3b 100644 --- a/molecule/qubes-staging-focal/qubes-vars.yml +++ b/molecule/qubes-staging-focal/qubes-vars.yml @@ -7,21 +7,6 @@ monitor_ip: "{{ hostvars['mon-staging']['ansible_default_ipv4'].address }}" # Use hardcoded username from the manual VM provisioning step. ssh_users: sdadmin -# Override the default logic to determine remote host connection info. -# Since we're using the "delegated" driver in Molecule, there's no inventory -# file in play for the connection, only the "instance config" file. -# Molecule will try to connect to the hostname, e.g. "app-staging". -# Let's look up the IP address already written to the instance config file, -# and wait for that address when the VMs are rebooting. -remote_host_ref: >- - {{ lookup('file', lookup('env', 'MOLECULE_INSTANCE_CONFIG')) - | from_yaml - | selectattr('instance', 'eq', ansible_host) - | map(attribute='address') - | first - | default (ansible_host) - }} - securedrop_target_distribution: focal # Inform the Ansible logic we're targeting Qubes staging VMs, diff --git a/molecule/qubes-staging-xenial/molecule.yml b/molecule/qubes-staging-xenial/molecule.yml index 82a21fffcf..bca55f0537 100644 --- a/molecule/qubes-staging-xenial/molecule.yml +++ b/molecule/qubes-staging-xenial/molecule.yml @@ -26,8 +26,8 @@ platforms: provisioner: name: ansible - lint: - name: ansible-lint + lint: | + ansible-lint config_options: defaults: callback_whitelist: "profile_tasks, timer" @@ -49,9 +49,11 @@ scenario: - converge verifier: name: testinfra - lint: - name: flake8 + lint: | + flake8 directory: ../testinfra options: n: auto v: 2 + env: + SECUREDROP_TESTINFRA_TARGET_HOST: qubes-staging diff --git a/molecule/qubes-staging-xenial/qubes-vars.yml b/molecule/qubes-staging-xenial/qubes-vars.yml index 1a34dc82ad..88d04e3154 100644 --- a/molecule/qubes-staging-xenial/qubes-vars.yml +++ b/molecule/qubes-staging-xenial/qubes-vars.yml @@ -7,21 +7,6 @@ monitor_ip: "{{ hostvars['mon-staging']['ansible_default_ipv4'].address }}" # Use hardcoded username from the manual VM provisioning step. ssh_users: sdadmin -# Override the default logic to determine remote host connection info. -# Since we're using the "delegated" driver in Molecule, there's no inventory -# file in play for the connection, only the "instance config" file. -# Molecule will try to connect to the hostname, e.g. "app-staging". -# Let's look up the IP address already written to the instance config file, -# and wait for that address when the VMs are rebooting. -remote_host_ref: >- - {{ lookup('file', lookup('env', 'MOLECULE_INSTANCE_CONFIG')) - | from_yaml - | selectattr('instance', 'eq', ansible_host) - | map(attribute='address') - | first - | default (ansible_host) - }} - securedrop_target_distribution: xenial # Inform the Ansible logic we're targeting Qubes staging VMs, diff --git a/molecule/testinfra/app/iptables-app-qubes-staging.j2 b/molecule/testinfra/app/iptables-app-qubes-staging.j2 new file mode 100644 index 0000000000..12a4628a46 --- /dev/null +++ b/molecule/testinfra/app/iptables-app-qubes-staging.j2 @@ -0,0 +1,44 @@ +*filter +:INPUT DROP +:FORWARD DROP +:OUTPUT DROP +:LOGNDROP - +-A INPUT -p tcp -m state --state RELATED,ESTABLISHED -m comment --comment "Allow traffic back for tor" -j ACCEPT +-A INPUT -i lo -p tcp -m tcp --dport 80 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow tor connection from local loopback to connect to source int" -j ACCEPT +-A INPUT -i lo -p tcp -m tcp --dport 8080 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow tor connection from local loopback to connect to journalist int" -j ACCEPT +-A INPUT -s 127.0.0.1/32 -d 127.0.0.1/32 -i lo -p tcp -m state --state RELATED,ESTABLISHED -m comment --comment "for redis worker all application user local loopback user" -j ACCEPT +{% for address in dns_server -%} +-A INPUT -s {{ address }}/32 -p tcp -m tcp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +-A INPUT -s {{ address }}/32 -p udp -m udp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +{% endfor -%} +-A INPUT -p udp -m udp --sport 123 --dport 123 -m state --state RELATED,ESTABLISHED -m comment --comment ntp -j ACCEPT +-A INPUT -p tcp -m multiport --sports 80,8080,443 -m state --state RELATED,ESTABLISHED -m comment --comment "apt updates" -j ACCEPT +-A INPUT -s {{ mon_ip }}/32 -p udp -m udp --sport 1514 -m state --state RELATED,ESTABLISHED -m comment --comment "OSSEC server agent" -j ACCEPT +-A INPUT -s {{ mon_ip }}/32 -p tcp -m tcp --dport 22 -m comment --comment "Block explicitly SSH from the adjacent SD component" -j DROP +-A INPUT -s {{ ssh_ip }}/32 -i {{ default_interface }} -p tcp -m tcp --dport 22 -m state --state NEW -m limit --limit 3/min --limit-burst 3 -m comment --comment "Rate limit incoming ssh traffic" -j ACCEPT +-A INPUT -s {{ ssh_ip }}/32 -i {{ default_interface }} -p tcp -m tcp --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT +-A INPUT -i lo -m comment --comment "Allow lo to lo traffic all protocols" -j ACCEPT +-A INPUT -p tcp -m state --state INVALID -m comment --comment "drop but do not log inbound invalid state packets" -j DROP +-A INPUT -m comment --comment "Drop and log all other incoming traffic" -j LOGNDROP +-A OUTPUT -p tcp -m owner --uid-owner {{ tor_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow tor outbound" -j ACCEPT +-A OUTPUT -m owner --uid-owner {{ tor_user_id }} -m comment --comment "Drop all other traffic for tor" -j LOGNDROP +-A OUTPUT -o lo -p tcp -m tcp --sport 80 -m owner --uid-owner {{ securedrop_user_id }} -m state --state RELATED,ESTABLISHED -m comment --comment "Restrict the apache user outbound connections" -j ACCEPT +-A OUTPUT -o lo -p tcp -m tcp --sport 8080 -m owner --uid-owner {{ securedrop_user_id }} -m state --state RELATED,ESTABLISHED -m comment --comment "Restrict the apache user outbound connections" -j ACCEPT +-A OUTPUT -s 127.0.0.1/32 -d 127.0.0.1/32 -o lo -p tcp -m owner --uid-owner {{ securedrop_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "for redis worker all application user local loopback user" -j ACCEPT +-A OUTPUT -m owner --uid-owner {{ securedrop_user_id }} -m comment --comment "Drop all other traffic by the securedrop user" -j LOGNDROP +-A OUTPUT -m owner --gid-owner {{ ssh_group_gid }} -m comment --comment "Drop all other outbound traffic for ssh user" -j LOGNDROP +{% for address in dns_server -%} +-A OUTPUT -d {{ address }}/32 -p tcp -m tcp --dport 53 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +-A OUTPUT -d {{ address }}/32 -p udp -m udp --dport 53 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +{% endfor -%} +-A OUTPUT -p udp -m udp --sport 123 --dport 123 -m owner --uid-owner 0 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment ntp -j ACCEPT +-A OUTPUT -p tcp -m multiport --dports 80,8080,443 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "apt updates" -j ACCEPT +-A OUTPUT -d {{ mon_ip }}/32 -p udp -m udp --dport 1514 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "OSSEC server agent" -j ACCEPT +-A OUTPUT -o eth0 -p tcp -m owner --uid-owner 0 -m tcp --sport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT +-A OUTPUT -o lo -m comment --comment "Allow lo to lo traffic all protocols" -j ACCEPT +-A OUTPUT -m comment --comment "Drop all other outgoing traffic" -j DROP +-A LOGNDROP -p tcp -m limit --limit 5/min -j LOG --log-tcp-options --log-ip-options --log-uid +-A LOGNDROP -p udp -m limit --limit 5/min -j LOG --log-ip-options --log-uid +-A LOGNDROP -p icmp -m limit --limit 5/min -j LOG --log-ip-options --log-uid +-A LOGNDROP -j DROP +COMMIT diff --git a/molecule/testinfra/app/test_app_network.py b/molecule/testinfra/app/test_app_network.py index 54bad13572..099c7e2c86 100644 --- a/molecule/testinfra/app/test_app_network.py +++ b/molecule/testinfra/app/test_app_network.py @@ -14,6 +14,8 @@ @pytest.mark.skip_in_prod def test_app_iptables_rules(host): + local = host.get_host("local://") + # Build a dict of variables to pass to jinja for iptables comparison kwargs = dict( mon_ip=os.environ.get('MON_IP', securedrop_test_vars.mon_ip), @@ -24,9 +26,13 @@ def test_app_iptables_rules(host): ssh_group_gid=host.check_output("getent group ssh | cut -d: -f3"), dns_server=securedrop_test_vars.dns_server) + # Required for testing under Qubes. + if local.interface("eth0").exists: + kwargs["ssh_ip"] = local.interface("eth0").addresses[0] + # Build iptables scrape cmd, purge comments + counters iptables = r"iptables-save | sed 's/ \[[0-9]*\:[0-9]*\]//g' | egrep -v '^#'" - environment = os.environ.get("CI_SD_ENV", "staging") + environment = os.environ.get("SECUREDROP_TESTINFRA_TARGET_HOST", "staging") iptables_file = "{}/iptables-app-{}.j2".format( os.path.dirname(os.path.abspath(__file__)), environment) diff --git a/molecule/testinfra/app/test_apparmor.py b/molecule/testinfra/app/test_apparmor.py index 0a4c78b06b..f119eb409a 100644 --- a/molecule/testinfra/app/test_apparmor.py +++ b/molecule/testinfra/app/test_apparmor.py @@ -59,12 +59,7 @@ def test_apparmor_tor_exact_capabilities(host): assert str(len(tor_capabilities)) == c -@pytest.mark.parametrize('profile', [ - 'ntpd', - 'apache2', - 'tcpdump', - 'tor', -]) +@pytest.mark.parametrize('profile', sdvars.apparmor_enforce) def test_apparmor_ensure_not_disabled(host, profile): """ Explicitly check that enforced profiles are NOT in /etc/apparmor.d/disable @@ -106,11 +101,10 @@ def test_apparmor_total_profiles(host): """ Ensure number of total profiles is sum of enforced and complaining profiles """ with host.sudo(): - total_expected = str(len(sdvars.apparmor_enforce) - + len(sdvars.apparmor_complain)) + total_expected = len(sdvars.apparmor_enforce) + len(sdvars.apparmor_complain) # Xenial about ~20 profiles, so let's expect # *at least* the sum. - assert host.check_output("aa-status --profiled") >= total_expected + assert int(host.check_output("aa-status --profiled")) >= total_expected def test_aastatus_unconfined(host): diff --git a/molecule/testinfra/common/test_user_config.py b/molecule/testinfra/common/test_user_config.py index 307c1d1edb..d4bd45afd1 100644 --- a/molecule/testinfra/common/test_user_config.py +++ b/molecule/testinfra/common/test_user_config.py @@ -96,7 +96,5 @@ def test_sudoers_tmux_env_deprecated(host): old setting isn't still active. """ - admin_user = "vagrant" - - f = host.file("/home/{}/.bashrc".format(admin_user)) + f = host.file("/home/{}/.bashrc".format(sdvars.admin_user)) assert not f.contains(r"^. \/etc\/bashrc\.securedrop_additions$") diff --git a/molecule/testinfra/mon/iptables-mon-qubes-staging.j2 b/molecule/testinfra/mon/iptables-mon-qubes-staging.j2 new file mode 100644 index 0000000000..d5bb5d8fd6 --- /dev/null +++ b/molecule/testinfra/mon/iptables-mon-qubes-staging.j2 @@ -0,0 +1,47 @@ +*filter +:INPUT DROP +:FORWARD DROP +:OUTPUT DROP +:LOGNDROP - +-A INPUT -p tcp -m state --state RELATED,ESTABLISHED -m comment --comment "Allow traffic back for tor" -j ACCEPT +{% for address in dns_server -%} +-A INPUT -s {{ address }}/32 -p tcp -m tcp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +-A INPUT -s {{ address }}/32 -p udp -m udp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +{% endfor -%} +-A INPUT -p udp -m udp --sport 123 --dport 123 -m state --state RELATED,ESTABLISHED -m comment --comment ntp -j ACCEPT +-A INPUT -p tcp -m multiport --sports 80,8080,443 -m state --state RELATED,ESTABLISHED -m comment --comment "apt updates" -j ACCEPT +-A INPUT -s {{ app_ip }}/32 -p udp -m udp --dport 1514 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow OSSEC agent to monitor" -j ACCEPT +{% for address in dns_server -%} +-A INPUT -s {{ address }}/32 -p tcp -m tcp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +-A INPUT -s {{ address }}/32 -p udp -m udp --sport 53 -m state --state RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +{% endfor -%} +-A INPUT -p tcp -m tcp --sport 587 -m state --state RELATED,ESTABLISHED -m comment --comment "Allow ossec email alerts out" -j ACCEPT +-A INPUT -s {{ app_ip }}/32 -p tcp -m tcp --dport 22 -m comment --comment "Block explicitly SSH from the adjacent SD component" -j DROP +-A INPUT -s {{ ssh_ip }}/32 -i {{ default_interface }} -p tcp -m tcp --dport 22 -m state --state NEW -m limit --limit 3/min --limit-burst 3 -m comment --comment "Rate limit incoming ssh traffic" -j ACCEPT +-A INPUT -s {{ ssh_ip }}/32 -i {{ default_interface }} -p tcp -m tcp --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT +-A INPUT -i lo -m comment --comment "Allow lo to lo traffic all protocols" -j ACCEPT +-A INPUT -p tcp -m state --state INVALID -m comment --comment "drop but do not log inbound invalid state packets" -j DROP +-A INPUT -m comment --comment "Drop and log all other incoming traffic" -j LOGNDROP +-A OUTPUT -p tcp -m owner --uid-owner {{ tor_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow tor outbound" -j ACCEPT +-A OUTPUT -m owner --uid-owner {{ tor_user_id }} -m comment --comment "Drop all other traffic for tor" -j LOGNDROP +-A OUTPUT -m owner --gid-owner {{ ssh_group_gid }} -m comment --comment "Drop all other outbound traffic for ssh user" -j LOGNDROP +{% for address in dns_server -%} +-A OUTPUT -d {{ address }}/32 -p tcp -m tcp --dport 53 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +-A OUTPUT -d {{ address }}/32 -p udp -m udp --dport 53 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "tcp/udp dns" -j ACCEPT +{% endfor -%} +-A OUTPUT -p udp -m udp --sport 123 --dport 123 -m owner --uid-owner 0 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment ntp -j ACCEPT +-A OUTPUT -p tcp -m multiport --dports 80,8080,443 -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "apt updates" -j ACCEPT +-A OUTPUT -d {{ app_ip }}/32 -p udp -m udp --sport 1514 -m state --state RELATED,ESTABLISHED -m comment --comment "Allow OSSEC agent to monitor" -j ACCEPT +{% for address in dns_server -%} +-A OUTPUT -d {{ address }}/32 -p tcp -m tcp --dport 53 -m owner --uid-owner {{ postfix_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "postfix dns rule" -j ACCEPT +-A OUTPUT -d {{ address }}/32 -p udp -m udp --dport 53 -m owner --uid-owner {{ postfix_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "postfix dns rule" -j ACCEPT +{% endfor -%} +-A OUTPUT -p tcp -m tcp --dport 587 -m owner --uid-owner {{ postfix_user_id }} -m state --state NEW,RELATED,ESTABLISHED -m comment --comment "Allow ossec email alerts out" -j ACCEPT +-A OUTPUT -o {{ default_interface }} -p tcp -m owner --uid-owner 0 -m tcp --sport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT +-A OUTPUT -o lo -m comment --comment "Allow lo to lo traffic all protocols" -j ACCEPT +-A OUTPUT -m comment --comment "Drop all other outgoing traffic" -j DROP +-A LOGNDROP -p tcp -m limit --limit 5/min -j LOG --log-tcp-options --log-ip-options --log-uid +-A LOGNDROP -p udp -m limit --limit 5/min -j LOG --log-ip-options --log-uid +-A LOGNDROP -p icmp -m limit --limit 5/min -j LOG --log-ip-options --log-uid +-A LOGNDROP -j DROP +COMMIT diff --git a/molecule/testinfra/mon/test_mon_network.py b/molecule/testinfra/mon/test_mon_network.py index cf3b1f2bab..5856206a53 100644 --- a/molecule/testinfra/mon/test_mon_network.py +++ b/molecule/testinfra/mon/test_mon_network.py @@ -13,6 +13,8 @@ @pytest.mark.skip_in_prod def test_mon_iptables_rules(host): + local = host.get_host("local://") + # Build a dict of variables to pass to jinja for iptables comparison kwargs = dict( app_ip=os.environ.get('APP_IP', securedrop_test_vars.app_ip), @@ -23,9 +25,13 @@ def test_mon_iptables_rules(host): postfix_user_id=host.check_output("id -u postfix"), dns_server=securedrop_test_vars.dns_server) + # Required for testing under Qubes. + if local.interface("eth0").exists: + kwargs["ssh_ip"] = local.interface("eth0").addresses[0] + # Build iptables scrape cmd, purge comments + counters iptables = r"iptables-save | sed 's/ \[[0-9]*\:[0-9]*\]//g' | egrep -v '^#'" - environment = os.environ.get("CI_SD_ENV", "staging") + environment = os.environ.get("SECUREDROP_TESTINFRA_TARGET_HOST", "staging") iptables_file = "{}/iptables-mon-{}.j2".format( os.path.dirname(os.path.abspath(__file__)), environment) diff --git a/molecule/testinfra/vars/app-qubes-staging.yml b/molecule/testinfra/vars/app-qubes-staging.yml new file mode 100644 index 0000000000..5eaddeae0d --- /dev/null +++ b/molecule/testinfra/vars/app-qubes-staging.yml @@ -0,0 +1,83 @@ +--- +# Testinfra vars file for app-staigng. +wanted_apache_headers: + - 'Header edit Set-Cookie ^(.*)$ $1;HttpOnly' + - 'Header always append X-Frame-Options: DENY' + - 'Header set Referrer-Policy "same-origin"' + - 'Header set X-XSS-Protection: "1; mode=block"' + - 'Header set X-Content-Type-Options: nosniff' + - 'Header set X-Download-Options: noopen' + - "Header set X-Content-Security-Policy: \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';\"" + - "Header set Content-Security-Policy: \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';\"" + - 'Header unset Etag' + +securedrop_venv: /opt/venvs/securedrop-app-code +securedrop_venv_bin: "{{ securedrop_venv }}/bin" +securedrop_venv_site_packages: "{{ securedrop_venv }}/lib/python3.5/site-packages" +securedrop_code: /var/www/securedrop +securedrop_data: /var/lib/securedrop +securedrop_user: www-data + +app_hostname: app-staging +monitor_hostname: mon-staging + +apache_listening_address: 127.0.0.1 +apache_source_log: /var/log/apache2/source-error.log +apache_allow_from: 127.0.0.1 + +dns_server: + - 8.8.8.8 + - 8.8.4.4 +mon_ip: 10.137.0.51 +app_ip: 10.137.0.50 + +pip_deps: + - name: 'Flask' + version: '1.0.2' + +apparmor_complain: [] + +app_directories: + - /var/www/securedrop + - /var/lib/securedrop + - /var/lib/securedrop/store + - /var/lib/securedrop/keys + - /var/lib/securedrop/tmp + +tor_services: + - name: source + ports: + - "80" + authenticated: no + version: 2 + + - name: journalist + ports: + - "80" + - "8080" + authenticated: yes + client: journalist + version: 2 + + - name: journalistv3 + ports: + - "80" + authenticated: yes + version: 3 + + - name: sourcev3 + ports: + - "80" + authenticated: no + version: 3 + +# Staging permits presence of "source-error.log". +allowed_apache_logfiles: + - /var/log/apache2/access.log + - /var/log/apache2/error.log + - /var/log/apache2/journalist-access.log + - /var/log/apache2/journalist-error.log + - /var/log/apache2/other_vhosts_access.log + - /var/log/apache2/source-error.log + +fpf_apt_repo_url: "https://apt-test.freedom.press" diff --git a/molecule/testinfra/vars/mon-qubes-staging.yml b/molecule/testinfra/vars/mon-qubes-staging.yml new file mode 100644 index 0000000000..799f714fe9 --- /dev/null +++ b/molecule/testinfra/vars/mon-qubes-staging.yml @@ -0,0 +1,105 @@ +--- +dns_server: + - 8.8.8.8 + - 8.8.4.4 + +# Hardcoding the IPv4 addresses will ONLY work with local testing in Vagrant. +app_ip: 10.0.137.50 +app_hostname: app-staging +tor_services: + - name: ssh + ports: + - "22" # assumes remote and local ports are identical + authenticated: yes # value will automatically be coerced to boolean + client: admin + +# Disable Postfix in staging, since we don't have valid credentials. +postfix_enabled: False + +# Log events for OSSEC alerts we suppress +log_events_without_ossec_alerts: + # Check that using an overloaded guard does not produce an OSSEC alert + - name: test_overloaded_tor_guard_does_not_produce_alert + alert: > + Aug 16 21:54:44 app-staging Tor[26695]: [warn] Your Guard + () is failing a very large amount of + circuits. Most likely this means the Tor network is + overloaded, but it could also mean an attack against you + or potentially the guard itself. + + # Check that OSSEC keep alive messages sent to the OSSEC manager + # do not produce OSSEC alerts. + # + # For more information see: + # https://github.com/ossec/ossec-hids/issues/466 + # http://ossec-docs.readthedocs.io/en/latest/faq/alerts.html + # + # Example alert is from: + # https://groups.google.com/forum/#!msg/ossec-list/dE3klm84JMU/kGZkRdSl3ZkJ + - name: test_ossec_keep_alive_mark_does_not_produce_alert + alert: > + Dec 02 09:48:40 app-staging ossec-keepalive: --MARK--: + &pQSW__BPa5S?%tyDTJ3-iCG2lz2dU))r(F%6tjp8wqpf=]IKFT%ND2k + P]ua/W)3-6'eHduX$;$Axqq7Vr.dVZ1SUDSaH)4xTXCIieaEKv47LD-b + U)SXMnXO/jPGKn3.!NGBR_5]jD2UoSV9)h%z8G%7.xhI;s)267.rV214 + O@t2#w)Z(k'UQp9]MyDERrOrG[-,e?iS@B3Rg/kGiR[g6mc0K)/]S]0' + +?+'/.[r$fqBR^7iAjoPv4j6SWjeRsLGr%$3#p+buf&u_RC3i/mE3vS3* + jp&B1qSJM431TmEg,YJ][ge;6-dJI69?-TB?!BI4?Uza63V3vMY3ake6a + hj-%A-m_5lgab!OVR,!pR+;L]eLgilU + + +# Log events we expect an OSSEC alert to occur for +log_events_with_ossec_alerts: + # Check that a denied RWX mmaping would produce an OSSEC alert + - name: test_grsec_denied_rwx_mapping_produces_alert + alert: > + Feb 10 23:34:40 app kernel: [ 124.188641] grsec: denied + RWX mmap of by /usr/sbin/apache2 + [apache2:1328] uid/euid:33/33 gid/egid:33/33, parent + /usr/sbin/apache2[apache2:1309] uid/euid:0/0 gid/egid:0/0 + level: "7" # Level 7 alert should be triggered by rule 100101 + rule_id: "100101" + + # When Ansible playbooks are run, an informative alert should be triggered + - name: test_ansible_playbook_triggers_alert + alert: > + Jul 22 17:06:41 app ansible-apt_key: Invoked with file=None + keyserver=None url=None data=-----BEGIN PGP PUBLIC KEY BLOCK + -----#012Version: GnuPG + v1#012#012mQENBEqg7GsBCACsef8koRT8UyZxiv1Irke5nVpte54TDtTl1 + za1tOKfthmHbs2I#0124DHWG3qrwGayw+6yb5mMFe0h9Ap9IbilA5a1IdRs + dDgViyQQ3kvdfoavFHRxvGON#012tknIyk5Goa36GMBl84gQceRs/4Zx3kx + qCV+JYXE9CmdkpkVrh2K3j5+ysDWfD/kO#012dTzwu3WHaAwL8d5MJAGQn2 + i6bTw4UHytrYemS1DdG/0EThCCyAnPmmb8iBkZlSW8#0126MzVqTrN37yvY + WTXk6MwKH50twaX5hzZAlSh9eqRjZLq51DDomO7EumXP90rS5mT#012QrS+ + wiYfGQttoZfbh3wl5ZjejgEjx+qrnOH7ABEBAAG0JmRlYi50b3Jwcm9qZWN + 0#012Lm9yZyBhcmNoaXZlIHNpZ25pbmcga2V5iEYEEBECAAYFAkqqojIACg + kQ61qJaiiY#012i/WmOgCfTyf3NJ7wHTBckwAeE4MSt5ZtXVsAn0XDq8PWW + nk4nK6TlevqK/VoWItF#012iEYEEBECAAYFAkqsYDUACgkQO50JPzGwl0vo + JwCcCSokiJSNY+yIr3nBPN/LJldb#012xekAmwfU60GeaWFwz7hqwVFL23x + eTpyniEYEEBECAAYFAkt9ndgACgkQYhWWT1sX#012KrI5TACfcBPbsaPA1A + UVVXXPv0KeWFYgVaIAoMr3jwd1NYVD6Te3D+yJhGzzCD6P#012iEYEEBECA + AYFAkt+li8ACgkQTlMAGaGhvAU4FwCfX3H4Ggm/x0yIAvmt4CW8AP9F#012 + 5D8AoKapuwbjsGncT3UdNFiHminAaq1tiEYEEBECAAYFAky6mjsACgkQhfc + mMSeh#012yJpL+gCggxs4C5o+Oznk7WmFrPQ3lbnfDKIAni4p20aRuwx6QW + GH8holjzTSmm5F#012iEYEEBECAAYFAlMI0FEACgkQhEMxewZV94DLagCcD + G5SR00+00VHzBVE6fDg027e#012N2sAnjNLOYbRSBxBnELUDKC7Vjaz/sAM + iEwEExECAAwFAkqg7nQFgwll/3cACgkQ#0123nqvbpTAnH+GJA + level: "13" + rule_id: "400001" + + # Override and Log (as to not send email of messages pertaining to pssec + # server and agent starting up after each reboot. + - name: test_ossec_server_started_does_not_produce_email + alert: > + ossec: Ossec started. + level: "1" + rule_id: "400502" + + - name: test_ossec_agent_started_does_not_produce_email + alert: > + ossec: Agent started + level: "1" + rule_id: "400503" + +fpf_apt_repo_url: "https://apt-test.freedom.press" diff --git a/molecule/testinfra/vars/qubes-staging.yml b/molecule/testinfra/vars/qubes-staging.yml new file mode 100644 index 0000000000..707a03e69a --- /dev/null +++ b/molecule/testinfra/vars/qubes-staging.yml @@ -0,0 +1,203 @@ +--- +# Testinfra vars file for app-staigng. +wanted_apache_headers: + - 'Header edit Set-Cookie ^(.*)$ $1;HttpOnly' + - 'Header always append X-Frame-Options: DENY' + - 'Header set Referrer-Policy "same-origin"' + - 'Header set X-XSS-Protection: "1; mode=block"' + - 'Header set X-Content-Type-Options: nosniff' + - 'Header set X-Download-Options: noopen' + - "Header set X-Content-Security-Policy: \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';\"" + - "Header set Content-Security-Policy: \"default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; font-src 'self';\"" + - 'Header unset Etag' + +securedrop_venv: /opt/venvs/securedrop-app-code +securedrop_venv_bin: "/opt/venvs/securedrop-app-code/bin" +securedrop_venv_site_packages: /opt/venvs/securedrop-app-code/lib/python{}/site-packages +securedrop_code: /var/www/securedrop +securedrop_data: /var/lib/securedrop +securedrop_user: www-data +admin_user: sdadmin + +app_hostname: app-staging +monitor_hostname: mon-staging + +apache_listening_address: 127.0.0.1 +apache_source_log: /var/log/apache2/source-error.log +apache_allow_from: 127.0.0.1 + +dns_server: + - 8.8.8.8 + - 8.8.4.4 +mon_ip: 10.137.0.51 +app_ip: 10.137.0.50 + +pip_deps: + - name: 'Flask' + version: '1.0.2' + +apparmor_complain: [] + +apparmor_enforce: + - "/sbin/dhclient" + - "/usr/lib/NetworkManager/nm-dhcp-client.action" + - "/usr/lib/connman/scripts/dhclient-script" + - "/usr/sbin/ntpd" + - "system_tor" + - "/usr/sbin/apache2" + - "/usr/sbin/apache2//DEFAULT_URI" + - "/usr/sbin/apache2//HANDLING_UNTRUSTED_INPUT" + - "/usr/sbin/tor" + +app_directories: + - /var/www/securedrop + - /var/lib/securedrop + - /var/lib/securedrop/store + - /var/lib/securedrop/keys + - /var/lib/securedrop/tmp + +tor_services: + - name: source + ports: + - "80" + authenticated: no + version: 2 + + - name: journalist + ports: + - "80" + - "8080" + authenticated: yes + client: journalist + version: 2 + + - name: journalistv3 + ports: + - "80" + authenticated: yes + version: 3 + + - name: sourcev3 + ports: + - "80" + authenticated: no + version: 3 + +# Staging permits presence of "source-error.log". +allowed_apache_logfiles: + - /var/log/apache2/access.log + - /var/log/apache2/error.log + - /var/log/apache2/journalist-access.log + - /var/log/apache2/journalist-error.log + - /var/log/apache2/other_vhosts_access.log + - /var/log/apache2/source-error.log + +# Disable Postfix in staging, since we don't have valid credentials. +postfix_enabled: False + +# Log events for OSSEC alerts we suppress +log_events_without_ossec_alerts: + # Check that using an overloaded guard does not produce an OSSEC alert + - name: test_overloaded_tor_guard_does_not_produce_alert + alert: > + Aug 16 21:54:44 app-staging Tor[26695]: [warn] Your Guard + () is failing a very large amount of + circuits. Most likely this means the Tor network is + overloaded, but it could also mean an attack against you + or potentially the guard itself. + + # Check that OSSEC keep alive messages sent to the OSSEC manager + # do not produce OSSEC alerts. + # + # For more information see: + # https://github.com/ossec/ossec-hids/issues/466 + # http://ossec-docs.readthedocs.io/en/latest/faq/alerts.html + # + # Example alert is from: + # https://groups.google.com/forum/#!msg/ossec-list/dE3klm84JMU/kGZkRdSl3ZkJ + - name: test_ossec_keep_alive_mark_does_not_produce_alert + alert: > + Dec 02 09:48:40 app-staging ossec-keepalive: --MARK--: + &pQSW__BPa5S?%tyDTJ3-iCG2lz2dU))r(F%6tjp8wqpf=]IKFT%ND2k + P]ua/W)3-6'eHduX$;$Axqq7Vr.dVZ1SUDSaH)4xTXCIieaEKv47LD-b + U)SXMnXO/jPGKn3.!NGBR_5]jD2UoSV9)h%z8G%7.xhI;s)267.rV214 + O@t2#w)Z(k'UQp9]MyDERrOrG[-,e?iS@B3Rg/kGiR[g6mc0K)/]S]0' + +?+'/.[r$fqBR^7iAjoPv4j6SWjeRsLGr%$3#p+buf&u_RC3i/mE3vS3* + jp&B1qSJM431TmEg,YJ][ge;6-dJI69?-TB?!BI4?Uza63V3vMY3ake6a + hj-%A-m_5lgab!OVR,!pR+;L]eLgilU + + +# Log events we expect an OSSEC alert to occur for +log_events_with_ossec_alerts: + # Check that a denied RWX mmaping would produce an OSSEC alert + - name: test_grsec_denied_rwx_mapping_produces_alert + alert: > + Feb 10 23:34:40 app kernel: [ 124.188641] grsec: denied + RWX mmap of by /usr/sbin/apache2 + [apache2:1328] uid/euid:33/33 gid/egid:33/33, parent + /usr/sbin/apache2[apache2:1309] uid/euid:0/0 gid/egid:0/0 + level: "7" # Level 7 alert should be triggered by rule 100101 + rule_id: "100101" + + # When Ansible playbooks are run, an informative alert should be triggered + - name: test_ansible_playbook_triggers_alert + alert: > + Jul 22 17:06:41 app ansible-apt_key: Invoked with file=None + keyserver=None url=None data=-----BEGIN PGP PUBLIC KEY BLOCK + -----#012Version: GnuPG + v1#012#012mQENBEqg7GsBCACsef8koRT8UyZxiv1Irke5nVpte54TDtTl1 + za1tOKfthmHbs2I#0124DHWG3qrwGayw+6yb5mMFe0h9Ap9IbilA5a1IdRs + dDgViyQQ3kvdfoavFHRxvGON#012tknIyk5Goa36GMBl84gQceRs/4Zx3kx + qCV+JYXE9CmdkpkVrh2K3j5+ysDWfD/kO#012dTzwu3WHaAwL8d5MJAGQn2 + i6bTw4UHytrYemS1DdG/0EThCCyAnPmmb8iBkZlSW8#0126MzVqTrN37yvY + WTXk6MwKH50twaX5hzZAlSh9eqRjZLq51DDomO7EumXP90rS5mT#012QrS+ + wiYfGQttoZfbh3wl5ZjejgEjx+qrnOH7ABEBAAG0JmRlYi50b3Jwcm9qZWN + 0#012Lm9yZyBhcmNoaXZlIHNpZ25pbmcga2V5iEYEEBECAAYFAkqqojIACg + kQ61qJaiiY#012i/WmOgCfTyf3NJ7wHTBckwAeE4MSt5ZtXVsAn0XDq8PWW + nk4nK6TlevqK/VoWItF#012iEYEEBECAAYFAkqsYDUACgkQO50JPzGwl0vo + JwCcCSokiJSNY+yIr3nBPN/LJldb#012xekAmwfU60GeaWFwz7hqwVFL23x + eTpyniEYEEBECAAYFAkt9ndgACgkQYhWWT1sX#012KrI5TACfcBPbsaPA1A + UVVXXPv0KeWFYgVaIAoMr3jwd1NYVD6Te3D+yJhGzzCD6P#012iEYEEBECA + AYFAkt+li8ACgkQTlMAGaGhvAU4FwCfX3H4Ggm/x0yIAvmt4CW8AP9F#012 + 5D8AoKapuwbjsGncT3UdNFiHminAaq1tiEYEEBECAAYFAky6mjsACgkQhfc + mMSeh#012yJpL+gCggxs4C5o+Oznk7WmFrPQ3lbnfDKIAni4p20aRuwx6QW + GH8holjzTSmm5F#012iEYEEBECAAYFAlMI0FEACgkQhEMxewZV94DLagCcD + G5SR00+00VHzBVE6fDg027e#012N2sAnjNLOYbRSBxBnELUDKC7Vjaz/sAM + iEwEExECAAwFAkqg7nQFgwll/3cACgkQ#0123nqvbpTAnH+GJA + level: "13" + rule_id: "400001" + + # Override and Log (as to not send email of messages pertaining to pssec + # server and agent starting up after each reboot. + - name: test_ossec_server_started_does_not_produce_email + alert: > + ossec: Ossec started. + level: "1" + rule_id: "400502" + + - name: test_ossec_agent_started_does_not_produce_email + alert: > + ossec: Agent started + level: "1" + rule_id: "400503" + + - name: test_ossec_server_apache_error_log_alert + alert: > + [Fri Apr 12 14:39:25.596318 2019] [wsgi:error] + [pid 1480:tid 4201987876608] ERROR:flask.app:Login for 'user' failed: + invalid username 'user' + level: "7" + rule_id: "400700" + + - name: test_ossec_server_test_notification_alert + alert: > + [Fri Apr 12 15:45:05.310796 2019] [wsgi:error] + [pid 1479:tid 4201988093696] ERROR:flask.app:This is a test OSSEC alert + level: "7" + rule_id: "400700" + +fpf_apt_repo_url: "https://apt-test.freedom.press" + +grsec_version_xenial: "4.14.188" +grsec_version_focal: "5.4.88" + diff --git a/molecule/testinfra/vars/staging.yml b/molecule/testinfra/vars/staging.yml index 6a26c292e7..8f876ea27d 100644 --- a/molecule/testinfra/vars/staging.yml +++ b/molecule/testinfra/vars/staging.yml @@ -17,6 +17,7 @@ securedrop_venv_site_packages: /opt/venvs/securedrop-app-code/lib/python{}/site- securedrop_code: /var/www/securedrop securedrop_data: /var/lib/securedrop securedrop_user: www-data +admin_user: vagrant app_hostname: app-staging monitor_hostname: mon-staging