From 18965df2dff28317acd01fc6883702ce8d35b16b Mon Sep 17 00:00:00 2001 From: juju4 Date: Sat, 28 Oct 2023 13:25:53 +0000 Subject: [PATCH] ci: add molecule/vagrant test suite --- molecule/vagrant/converge.yml | 52 ++++++++ molecule/vagrant/create.yml | 59 +++++++++ molecule/vagrant/destroy.yml | 39 ++++++ molecule/vagrant/molecule.yml | 48 +++++++ molecule/vagrant/playbook.yml | 7 + molecule/vagrant/requirements.yml | 21 +++ molecule/vagrant/verify.yml | 211 ++++++++++++++++++++++++++++++ 7 files changed, 437 insertions(+) create mode 100644 molecule/vagrant/converge.yml create mode 100644 molecule/vagrant/create.yml create mode 100644 molecule/vagrant/destroy.yml create mode 100644 molecule/vagrant/molecule.yml create mode 100644 molecule/vagrant/playbook.yml create mode 100644 molecule/vagrant/requirements.yml create mode 100644 molecule/vagrant/verify.yml diff --git a/molecule/vagrant/converge.yml b/molecule/vagrant/converge.yml new file mode 100644 index 0000000..9f5a053 --- /dev/null +++ b/molecule/vagrant/converge.yml @@ -0,0 +1,52 @@ +--- + +- name: Converge + hosts: all + environment: + http_proxy: "{{ lookup('env', 'http_proxy') }}" + https_proxy: "{{ lookup('env', 'https_proxy') }}" + no_proxy: "{{ lookup('env', 'no_proxy') }}" + remote_user: root + pre_tasks: + - name: Ubuntu | Install python3 + ansible.builtin.raw: test -e /usr/bin/python3 || (apt -y update && apt install -y python3-minimal) + register: python3 + changed_when: "'installed' in python3.stdout" + when: (ansible_distribution == "Ubuntu" and ansible_distribution_major_version | int >= 16) + - name: RedHat | Install python3 + ansible.builtin.raw: test -e /usr/bin/python3 || (yum install -y python3) + register: python3 + changed_when: "'installed' in python3.stdout" + when: (ansible_os_family == "RedHat" and ansible_distribution_major_version | int >= 8) + - name: Gather Facts + ansible.builtin.setup: + when: (ansible_distribution == "Ubuntu" and ansible_distribution_major_version | int >= 16) + - name: Ubuntu Bionic+, Redhat 8+ | Enforce python3 for ansible + ansible.builtin.set_fact: + ansible_python_interpreter: /usr/bin/python3 + when: > + (ansible_distribution == "Ubuntu" and ansible_distribution_major_version | int >= 16) or + (ansible_os_family == "RedHat" and ansible_distribution_major_version | int >= 8) + - name: 22.04 | Set fact + ansible.builtin.set_fact: + misp_modules_enable: false + when: (ansible_distribution == "Ubuntu" and ansible_distribution_major_version | int == 22) + vars: + misp_pymisp_validation_fatal: false + # need pymisp + misp_enable_feeds: [] + misp_webusers_list: [] + misp_testing: false + harden_php_allow_url_fopen: On + harden_php_disable_functions: 'pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,chown,diskfreespace,disk_free_space,disk_total_space,dl,exec,escapeshellarg,escapeshellcmd,fileinode,highlight_file,max_execution_time,passthru,pclose,phpinfo,popen,proc_get_status,proc_nice,proc_terminate,show_source,system,__construct, __destruct, __call,__wakeup' + misp_php_snuffleupagus_enable: true + harden_php7_snuffleupagus: true + harden_php7_snuffleupagus_rules_template: 'misp-snuffleupagus-rules.ini.j2' + harden_apache_php_variants: ['apache2'] + harden_apache_php74_debian_enable: true + harden_apache_php74_rhel9_enable: true + misp_no_log: false + roles: + - { role: juju4.redhat_epel, when: ansible_os_family == 'RedHat' } + - juju4.harden_apache + - juju4.misp diff --git a/molecule/vagrant/create.yml b/molecule/vagrant/create.yml new file mode 100644 index 0000000..7f0d8f6 --- /dev/null +++ b/molecule/vagrant/create.yml @@ -0,0 +1,59 @@ +--- +# https://github.com/ansible-collections/community.vagrant/ +# https://github.com/ansible-community/molecule-vagrant/blob/main/molecule_vagrant/playbooks/create.yml + +- name: Create + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Create molecule instance(s) # noqa fqcn[action] + community.vagrant.vagrant: + instance_name: "{{ item.name }}" + instance_interfaces: "{{ item.interfaces | default(omit) }}" + instance_raw_config_args: "{{ item.instance_raw_config_args | default(omit) }}" + config_options: "{{ item.config_options | default(omit) }}" + platform_box: "{{ item.box | default('debian/bullseye64') }}" + platform_box_version: "{{ item.box_version | default(omit) }}" + platform_box_url: "{{ item.box_url | default(omit) }}" + provider_name: "{{ molecule_yml.driver.provider.name | default(omit, true) }}" + provider_memory: "{{ item.memory | default(omit) }}" + provider_cpus: "{{ item.cpus | default(omit) }}" + provider_options: "{{ item.provider_options | default(omit) }}" + provider_raw_config_args: "{{ item.provider_raw_config_args | default(omit) }}" + provider_override_args: "{{ item.provider_override_args | default(omit) }}" + provision: "{{ item.provision | default(omit) }}" + state: up + register: server + with_items: "{{ molecule_yml.platforms }}" + loop_control: + label: "{{ item.name }}" + no_log: false + + # Mandatory configuration for Molecule to function. + + - name: Create molecule instances configuration + when: server is changed # noqa no-handler + block: + + - name: Populate instance config dict + ansible.builtin.set_fact: + instance_conf_dict: { + 'instance': "{{ item.Host }}", + 'address': "{{ item.HostName }}", + 'user': "{{ item.User }}", + 'port': "{{ item.Port }}", + 'identity_file': "{{ item.IdentityFile }}", } + with_items: "{{ server.results }}" + register: instance_config_dict + + - name: Convert instance config dict to a list + ansible.builtin.set_fact: + instance_conf: "{{ instance_config_dict.results | map(attribute='ansible_facts.instance_conf_dict') | list }}" + + - name: Dump instance config + ansible.builtin.copy: + content: "{{ instance_conf | to_json | from_json | to_yaml }}" + dest: "{{ molecule_instance_config }}" + mode: 0600 diff --git a/molecule/vagrant/destroy.yml b/molecule/vagrant/destroy.yml new file mode 100644 index 0000000..5d66cb5 --- /dev/null +++ b/molecule/vagrant/destroy.yml @@ -0,0 +1,39 @@ +--- +# https://github.com/ansible-collections/community.vagrant/ +# https://github.com/ansible-community/molecule-vagrant/blob/main/molecule_vagrant/playbooks/destroy.yml + +- name: Destroy + hosts: localhost + connection: local + gather_facts: false + no_log: "{{ molecule_no_log }}" + tasks: + - name: Delete molecule instance(s) # noqa fqcn[action] + community.vagrant.vagrant: + instance_name: "{{ item.name }}" + platform_box: "{{ item.box | default(omit) }}" + provider_name: "{{ molecule_yml.driver.provider.name | default(omit, true) }}" + provider_options: "{{ item.provider_options | default(omit) }}" + provider_raw_config_args: "{{ item.provider_raw_config_args | default(omit) }}" + force_stop: "{{ item.force_stop | default(true) }}" + state: destroy + register: server + with_items: "{{ molecule_yml.platforms }}" + loop_control: + label: "{{ item.name }}" + no_log: false + + # Mandatory configuration for Molecule to function. + + - name: Populate instance config + ansible.builtin.set_fact: + instance_conf: {} + + - name: Dump instance config + ansible.builtin.copy: + content: | + # Molecule managed + {{ instance_conf | to_json | from_json | to_yaml }} + dest: "{{ molecule_instance_config }}" + mode: 0600 + when: server.changed | default(false) | bool # noqa no-handler diff --git a/molecule/vagrant/molecule.yml b/molecule/vagrant/molecule.yml new file mode 100644 index 0000000..6024e32 --- /dev/null +++ b/molecule/vagrant/molecule.yml @@ -0,0 +1,48 @@ +--- +dependency: + name: galaxy +driver: + name: vagrant + provider: + name: libvirt + type: libvirt + options: + memory: 4096 # lol, ubuntu 22.04 will deadlock on boot due to lack of memory. + cpus: 1 +platforms: + - name: instance + box: ${MOLECULE_DISTRO:-generic/ubuntu2004} + interfaces: + - auto_config: true + network_name: private_network + type: static + ip: 192.168.10.10 + # env: + # http_proxy: ${http_proxy} + # https_proxy: ${https_proxy} + # no_proxy: ${no_proxy} + groups: + - mispgroup +provisioner: + name: ansible + config_options: + defaults: + verbosity: 2 + connection_options: + ansible_ssh_user: vagrant + ansible_become: true +scenario: + name: default + test_sequence: + - dependency + # - lint + - cleanup + - destroy + - syntax + - create + - prepare + - converge + # - idempotence + - verify +verifier: + name: ansible diff --git a/molecule/vagrant/playbook.yml b/molecule/vagrant/playbook.yml new file mode 100644 index 0000000..bc4aaba --- /dev/null +++ b/molecule/vagrant/playbook.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + vars: + misp_pymisp_validation_fatal: false + roles: + - juju4.misp diff --git a/molecule/vagrant/requirements.yml b/molecule/vagrant/requirements.yml new file mode 100644 index 0000000..c823274 --- /dev/null +++ b/molecule/vagrant/requirements.yml @@ -0,0 +1,21 @@ +--- + +collections: + - ansible.posix + - community.vagrant + +roles: + - src: https://github.com/juju4/ansible-redhat-epel/ + version: main + name: juju4.redhat_epel + - src: https://github.com/juju4/ansible-harden-apache/ + version: main + name: juju4.harden_apache + - src: https://github.com/juju4/ansible-harden-nginx/ + version: main + name: juju4.harden_nginx + - src: https://github.com/juju4/ansible-faup/ + version: main + name: juju4.faup + - name: geerlingguy.nginx + - name: geerlingguy.apache diff --git a/molecule/vagrant/verify.yml b/molecule/vagrant/verify.yml new file mode 100644 index 0000000..828ffd6 --- /dev/null +++ b/molecule/vagrant/verify.yml @@ -0,0 +1,211 @@ +--- + +- name: Verify MISP setup + hosts: mispgroup + gather_facts: true + vars: + misp_home: /var/www/_MISP + misp_rootdir: "{{ misp_home }}/MISP" + misp_config: "{{ misp_rootdir }}/app/Config/config.php" + ports: + # apache + - { h: 0.0.0.0, p: 443 } + # mysql + - { h: localhost, p: 3306 } + # redis + - { h: localhost, p: 6379 } + url: https://localhost + is_container: false + misp_pymisp_test: false + apache_process: apache2 + apache_user: www-data + php_pkg: php + pre_tasks: + - name: Debug | var ansible_virtualization_type + ansible.builtin.debug: + var: ansible_virtualization_type + - name: Set fact is_container + ansible.builtin.set_fact: + is_container: true + when: > + (ansible_virtualization_type is defined and + (ansible_virtualization_type == "docker" or ansible_virtualization_type == "containerd") + ) + - name: RedHat | Set fact + ansible.builtin.set_fact: + apache_process: httpd + apache_user: root + when: ansible_os_family == 'RedHat' + - name: Jammy, RHEL9 | Set fact php7.4 + ansible.builtin.set_fact: + php_pkg: php7.4 + when: > + (ansible_distribution == 'Ubuntu' and ansible_distribution_major_version | int == 22) + or + (ansible_os_family == 'RedHat' and ansible_distribution_major_version | int == 9) + tasks: + - name: Gather package facts + ansible.builtin.package_facts: + manager: auto + - name: Validate that needed packages are present + ansible.builtin.assert: + that: + - ansible_facts.packages[php_pkg] + - ansible_facts.packages['redis-server'] + - ansible_facts.packages['python3'] + + - name: Check misp config file + ansible.builtin.stat: + path: "{{ misp_config }}" + register: cfg1 + - name: Validate misp configuration file is present + ansible.builtin.assert: + that: cfg1.stat.exists and cfg1.stat.size != 0 + + - name: Ensure apache process is running + ansible.builtin.command: "pgrep -u {{ apache_user }} {{ apache_process }}" + register: ps1 + changed_when: false + failed_when: false + - name: Validate ps output + ansible.builtin.assert: + that: ps1.stdout + when: + - not is_container|bool + + - name: Check all processes + ansible.builtin.command: ps aux + changed_when: false + register: psa + - name: Debug | ps aux output + ansible.builtin.debug: + var: psa + verbosity: 1 + + - name: Ensure ports are listening + ansible.builtin.wait_for: + host: "{{ item.h }}" + port: "{{ item.p }}" + timeout: 10 + with_items: "{{ ports }}" + when: + - not is_container|bool + + - name: Fetch localhost endpoint + ansible.builtin.uri: + url: "{{ url }}" + validate_certs: no + return_content: yes + register: web + failed_when: false + - name: Validate localhost endpoint + ansible.builtin.assert: + that: > + "'Users - MISP' in web.content" + when: + - not is_container|bool + + - name: Check misp errors + ansible.builtin.command: "grep Error: {{ misp_rootdir }}/app/tmp/logs/error.log" + changed_when: false + failed_when: false + register: errors + + - name: Check misp debug + ansible.builtin.command: "cat {{ misp_rootdir }}/app/tmp/logs/debug.log" + changed_when: false + failed_when: false + register: debug + + - name: Check apache2 access + ansible.builtin.command: "tail -50 /var/log/apache2/misp.local_access.log" + changed_when: false + failed_when: false + register: apache2access + + - name: Check apache2 errors + ansible.builtin.command: "tail -50 /var/log/apache2/misp.local_error.log" + changed_when: false + failed_when: false + register: apache2error + + - name: Check redis logs + ansible.builtin.command: "tail -50 /var/log/redis/redis-server.log" + changed_when: false + failed_when: false + register: redis + + - name: Check pymisp API + ansible.builtin.command: "{{ misp_home }}/venv/bin/python {{ misp_rootdir }}/PyMISP/examples/users_list.py" + changed_when: false + failed_when: false + register: pymisp + - name: Validate pymisp users list + ansible.builtin.assert: + that: + - "'admin@admin.test' in pymisp.stdout" + when: misp_pymisp_test | bool + + - name: Check cake Admin configLint + ansible.builtin.command: "{{ misp_rootdir }}/app/Console/cake Admin configLint" + become: yes + become_user: www-data + changed_when: false + failed_when: false + register: configlint + - name: Validate config lint + ansible.builtin.assert: + that: + - not configlint.stdout|string + - not configlint.stderr|string + + - name: Check cake Admin live + ansible.builtin.command: "{{ misp_rootdir }}/app/Console/cake Admin live" + become: yes + become_user: www-data + changed_when: false + failed_when: false + register: live + - name: Validate live + ansible.builtin.assert: + that: + - "'True' in live.stdout" + - not live.stderr|string + + - name: Check cake Admin securityAudit + ansible.builtin.command: "{{ misp_rootdir }}/app/Console/cake Admin securityAudit" + become: yes + become_user: www-data + changed_when: false + failed_when: false + register: securityaudit + - name: Validate securityaudit + ansible.builtin.assert: + that: + - "'Error: ' not in securityaudit.stdout" + - not securityaudit.stderr|string + when: false + + - name: Check mysql misp tables + ansible.builtin.command: mysql -e "SHOW TABLES" misp + changed_when: false + failed_when: false + register: mysql1 + + - name: Check mysql misp users + ansible.builtin.command: mysql -e "SELECT * from users;" misp + changed_when: false + failed_when: false + register: mysql2 + + - name: Check misp-workers under supervisor # noqa command-instead-of-module + ansible.builtin.command: supervisorctl status + changed_when: false + failed_when: false + register: sup + - name: Validate supervisor status + ansible.builtin.assert: + that: + - "'misp-workers:' in sup.stdout" + - "'RUNNING' in sup.stdout" + - sup.rc == 0