From e742330a41bebd74860bb19491c486ca8b51c915 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sat, 9 Jul 2022 00:52:58 +0200 Subject: [PATCH] add testing of os_hardning on vm Signed-off-by: Martin Schurz --- .github/workflows/os_hardening_vm.yml | 44 ++++++++++ molecule/os_hardening_vm/INSTALL.rst | 22 +++++ molecule/os_hardening_vm/converge.yml | 38 +++++++++ molecule/os_hardening_vm/molecule.yml | 51 ++++++++++++ molecule/os_hardening_vm/prepare.yml | 60 ++++++++++++++ .../os_hardening_vm/prepare_tasks/yum.yml | 16 ++++ molecule/os_hardening_vm/requirements.yml | 3 + molecule/os_hardening_vm/verify.yml | 83 +++++++++++++++++++ molecule/os_hardening_vm/verify_tasks/pam.yml | 59 +++++++++++++ molecule/os_hardening_vm/verify_tasks/yum.yml | 8 ++ 10 files changed, 384 insertions(+) create mode 100644 .github/workflows/os_hardening_vm.yml create mode 100644 molecule/os_hardening_vm/INSTALL.rst create mode 100644 molecule/os_hardening_vm/converge.yml create mode 100644 molecule/os_hardening_vm/molecule.yml create mode 100644 molecule/os_hardening_vm/prepare.yml create mode 100644 molecule/os_hardening_vm/prepare_tasks/yum.yml create mode 100644 molecule/os_hardening_vm/requirements.yml create mode 100644 molecule/os_hardening_vm/verify.yml create mode 100644 molecule/os_hardening_vm/verify_tasks/pam.yml create mode 100644 molecule/os_hardening_vm/verify_tasks/yum.yml diff --git a/.github/workflows/os_hardening_vm.yml b/.github/workflows/os_hardening_vm.yml new file mode 100644 index 00000000..21bd5065 --- /dev/null +++ b/.github/workflows/os_hardening_vm.yml @@ -0,0 +1,44 @@ +--- +name: "devsec.os_hardening" +on: # yamllint disable-line rule:truthy + workflow_dispatch: + push: + paths: + - 'roles/os_hardening/**' + - 'molecule/os_hardening_vm/**' + - '.github/workflows/os_hardening_vm.yml' + pull_request: + paths: + - 'roles/os_hardening/**' + - 'molecule/os_hardening_vm/**' + - '.github/workflows/os_hardening_vm.yml' +jobs: + build: + runs-on: self-hosted + env: + PY_COLORS: 1 + ANSIBLE_FORCE_COLOR: 1 + strategy: + fail-fast: false + matrix: + molecule_distro: + - ubuntu1804 + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + path: ansible_collections/devsec/hardening + submodules: true + + - name: Create default collection path symlink + run: | + mkdir -p /home/runner/.ansible + ln -s /home/runner/work/ansible-os-hardening/ansible-os-hardening /home/runner/.ansible/collections + + - name: Test with molecule + run: | + molecule --version + molecule test -s os_hardening + env: + MOLECULE_DISTRO: ${{ matrix.molecule_distro }} + working-directory: ansible_collections/devsec/hardening diff --git a/molecule/os_hardening_vm/INSTALL.rst b/molecule/os_hardening_vm/INSTALL.rst new file mode 100644 index 00000000..d926ca25 --- /dev/null +++ b/molecule/os_hardening_vm/INSTALL.rst @@ -0,0 +1,22 @@ +******* +Docker driver installation guide +******* + +Requirements +============ + +* Docker Engine + +Install +======= + +Please refer to the `Virtual environment`_ documentation for installation best +practices. If not using a virtual environment, please consider passing the +widely recommended `'--user' flag`_ when invoking ``pip``. + +.. _Virtual environment: https://virtualenv.pypa.io/en/latest/ +.. _'--user' flag: https://packaging.python.org/tutorials/installing-packages/#installing-to-the-user-site + +.. code-block:: bash + + $ python3 -m pip install 'molecule[docker]' diff --git a/molecule/os_hardening_vm/converge.yml b/molecule/os_hardening_vm/converge.yml new file mode 100644 index 00000000..4cfc0546 --- /dev/null +++ b/molecule/os_hardening_vm/converge.yml @@ -0,0 +1,38 @@ +--- +- name: wrapper playbook for kitchen testing "ansible-os-hardening" with custom vars for testing + hosts: all + become: true + environment: + http_proxy: "{{ lookup('env', 'http_proxy') | default(omit) }}" + https_proxy: "{{ lookup('env', 'https_proxy') | default(omit) }}" + no_proxy: "{{ lookup('env', 'no_proxy') | default(omit) }}" + collections: + - devsec.hardening + tasks: + - include_role: + name: os_hardening + vars: + os_auth_pam_passwdqc_enable: false + os_auth_lockout_time: 15 + os_yum_repo_file_whitelist: ['foo.repo'] + +# - name: wrapper playbook for kitchen testing "ansible-os-hardening" +# hosts: all +# become: true +# collections: +# - devsec.hardening +# vars: +# os_auditd_enabled: false +# tasks: +# - name: set ansible_python_interpreter to "/usr/bin/python3" on fedora +# set_fact: +# ansible_python_interpreter: "/usr/bin/python3" +# when: ansible_facts.distribution == 'Fedora' +# +# - name: Run the equivalent of "apt-get update" as a separate step +# apt: +# update_cache: yes +# when: ansible_facts.os_family == 'Debian' +# +# - include_role: +# name: os_hardening diff --git a/molecule/os_hardening_vm/molecule.yml b/molecule/os_hardening_vm/molecule.yml new file mode 100644 index 00000000..06fc87f7 --- /dev/null +++ b/molecule/os_hardening_vm/molecule.yml @@ -0,0 +1,51 @@ +--- +dependency: + name: galaxy + options: + role-file: molecule/os_hardening/requirements.yml +driver: + name: vagrant + provider: + name: libvirt +platforms: + - name: instance + image: "generic/${MOLECULE_DISTRO}" +provisioner: + name: ansible + config_options: + defaults: + interpreter_python: auto_silent + callback_whitelist: profile_tasks, timer, yaml +verifier: + name: ansible + +scenario: + create_sequence: + - dependency + - create + - prepare + check_sequence: + - dependency + - destroy + - create + - prepare + - converge + - check + - destroy + converge_sequence: + - dependency + - create + - prepare + - converge + destroy_sequence: + - destroy + test_sequence: + - dependency + - destroy + - syntax + - create + - prepare + - converge + - idempotence + - verify + - destroy diff --git a/molecule/os_hardening_vm/prepare.yml b/molecule/os_hardening_vm/prepare.yml new file mode 100644 index 00000000..2fdcbe9e --- /dev/null +++ b/molecule/os_hardening_vm/prepare.yml @@ -0,0 +1,60 @@ +--- +- name: wrapper playbook for kitchen testing "ansible-os-hardening" with custom vars for testing + hosts: all + become: true + collections: + - devsec.hardening + environment: + http_proxy: "{{ lookup('env', 'http_proxy') | default(omit) }}" + https_proxy: "{{ lookup('env', 'https_proxy') | default(omit) }}" + no_proxy: "{{ lookup('env', 'no_proxy') | default(omit) }}" + tasks: + - name: set ansible_python_interpreter to "/usr/bin/python3" on fedora + set_fact: + ansible_python_interpreter: "/usr/bin/python3" + when: ansible_facts.distribution == 'Fedora' + + - name: Run the equivalent of "apt-get update && apt-get upgrade" + apt: + name: "*" + state: latest + update_cache: true + when: ansible_os_family == 'Debian' + + - name: install required tools on SuSE + # cannot use zypper module, since it depends on python-xml + shell: "zypper -n install python-xml" + when: ansible_facts.os_family == 'Suse' + + - name: install required tools on fedora + dnf: + name: + - python + - findutils + - procps-ng + when: ansible_facts.distribution == 'Fedora' + + - name: install required tools on Arch + community.general.pacman: + name: + - awk + state: present + update_cache: true + when: ansible_facts.os_family == 'Archlinux' + + - name: install required tools on RHEL # noqa ignore-errors + yum: + name: + - openssh-clients + - openssh + state: present + update_cache: true + ignore_errors: true + + - name: create recursing symlink to test minimize access + shell: "rm -f /usr/bin/zzz && ln -s /usr/bin /usr/bin/zzz" + changed_when: false + + - name: include YUM prepare tasks + include_tasks: prepare_tasks/yum.yml + when: ansible_facts.os_family == 'RedHat' diff --git a/molecule/os_hardening_vm/prepare_tasks/yum.yml b/molecule/os_hardening_vm/prepare_tasks/yum.yml new file mode 100644 index 00000000..4d4bb92d --- /dev/null +++ b/molecule/os_hardening_vm/prepare_tasks/yum.yml @@ -0,0 +1,16 @@ +--- +- name: create 'foo' repository + ansible.builtin.yum_repository: + name: foo + description: mandatory description + baseurl: file:///mandatory-url + enabled: false + gpgcheck: false + +- name: create 'bar' repository + ansible.builtin.yum_repository: + name: bar + description: mandatory description + baseurl: file:///mandatory-url + enabled: false + gpgcheck: false diff --git a/molecule/os_hardening_vm/requirements.yml b/molecule/os_hardening_vm/requirements.yml new file mode 100644 index 00000000..53fa9b49 --- /dev/null +++ b/molecule/os_hardening_vm/requirements.yml @@ -0,0 +1,3 @@ +--- +roles: + - geerlingguy.git diff --git a/molecule/os_hardening_vm/verify.yml b/molecule/os_hardening_vm/verify.yml new file mode 100644 index 00000000..670718ad --- /dev/null +++ b/molecule/os_hardening_vm/verify.yml @@ -0,0 +1,83 @@ +--- +- name: Verify + hosts: all + become: true + environment: + http_proxy: "{{ lookup('env', 'http_proxy') | default(omit) }}" + https_proxy: "{{ lookup('env', 'https_proxy') | default(omit) }}" + no_proxy: "{{ lookup('env', 'no_proxy') | default(omit) }}" + roles: + - geerlingguy.git + tasks: + - name: install fake SuSE-release for cinc compatibility + copy: + content: | + openSUSE Faked Enterprise 2020 (x86_64) + VERSION = 2020 + CODENAME = Faked Feature + dest: /etc/SuSE-release + owner: root + group: root + mode: '0444' + when: ansible_facts.os_family == 'Suse' + + - name: install git for SuSE since geerlinguy.git does not support it + zypper: + name: git + state: present + when: ansible_facts.os_family == 'Suse' + + - name: Run the equivalent of "apt-get update" as a separate step + apt: + update_cache: true + when: ansible_facts.os_family == 'Debian' + + - name: install required tools on debian + apt: + name: procps + when: ansible_facts.os_family == 'Debian' + + - name: include PAM tests + include_tasks: verify_tasks/pam.yml + when: ansible_facts.distribution in ['Debian', 'Ubuntu'] or ansible_facts.os_family == 'RedHat' + + - name: include YUM tests + include_tasks: verify_tasks/yum.yml + when: ansible_facts.os_family == 'RedHat' + + - name: download cinc-auditor + get_url: + url: https://omnitruck.cinc.sh/install.sh + dest: /tmp/install.sh + mode: '0775' + + - name: install cinc-auditor + shell: "bash /tmp/install.sh -s -- -P cinc-auditor -v 4" + + - name: Execute cinc-auditor tests # noqa ignore-errors + command: "/opt/cinc-auditor/bin/cinc-auditor exec --no-show-progress --no-color --no-distinct-exit --waiver-file waivers.yaml https://github.com/dev-sec/linux-baseline/archive/refs/heads/master.zip" + register: test_results + changed_when: false + ignore_errors: true + + - name: Display details about the cinc-auditor results + debug: + msg: "{{ test_results.stdout_lines }}" + + - name: Fail when tests fail + fail: + msg: "Inspec failed to validate" + when: test_results.rc != 0 + + # test if variable can be overridden + - name: workaround for https://github.com/ansible/ansible/issues/66304 + set_fact: + os_env_umask: "027 #override" + + - include_role: + name: os_hardening + + - name: verify os_env_umask + shell: + cmd: "grep '027 #override' /etc/login.defs" + changed_when: false diff --git a/molecule/os_hardening_vm/verify_tasks/pam.yml b/molecule/os_hardening_vm/verify_tasks/pam.yml new file mode 100644 index 00000000..90fa6db3 --- /dev/null +++ b/molecule/os_hardening_vm/verify_tasks/pam.yml @@ -0,0 +1,59 @@ +--- +- name: download pam-tester + get_url: + url: https://github.com/schurzi/pam-tester/releases/download/latest/pam-tester + dest: /bin/pam-tester + mode: 0555 + +- name: set password for test + set_fact: + test_pw: "myTest!pw" + +- name: set locale for test + set_fact: + locale: "en_US.UTF-8" + when: + - ansible_facts.os_family == 'RedHat' + - ansible_facts.distribution_major_version < '8' + +- name: create testuser + user: + name: testuser + password: "{{ test_pw | password_hash('sha512') }}" + +- name: check successfull login with correct password + shell: + cmd: "pam-tester --user testuser --password {{ test_pw }}" + environment: + TMPDIR: /var/tmp + LC_ALL: "{{ locale | default('C.UTF-8') }}" + LANG: "{{ locale | default('C.UTF-8') }}" + +- name: check unsuccessfull login with incorrect password + shell: + cmd: "pam-tester --user testuser --password {{ test_pw }}fail --expectfail" + environment: + TMPDIR: /var/tmp + LC_ALL: "{{ locale | default('C.UTF-8') }}" + LANG: "{{ locale | default('C.UTF-8') }}" + with_sequence: count=6 + +- name: check unsuccessfull login, with correct password (lockout) + shell: + cmd: "pam-tester --user testuser --password {{ test_pw }} --expectfail" + environment: + TMPDIR: /var/tmp + LC_ALL: "{{ locale | default('C.UTF-8') }}" + LANG: "{{ locale | default('C.UTF-8') }}" + +- name: wait for account to unlock + pause: + seconds: 20 + +- name: check successfull login + shell: + cmd: "pam-tester --user testuser --password {{ test_pw }}" + environment: + TMPDIR: /var/tmp + LC_ALL: "{{ locale | default('C.UTF-8') }}" + LANG: "{{ locale | default('C.UTF-8') }}" diff --git a/molecule/os_hardening_vm/verify_tasks/yum.yml b/molecule/os_hardening_vm/verify_tasks/yum.yml new file mode 100644 index 00000000..15afbbf2 --- /dev/null +++ b/molecule/os_hardening_vm/verify_tasks/yum.yml @@ -0,0 +1,8 @@ +--- +- name: verify 'gpgcheck' was not enabled for 'foo' repository (in whitelist) + command: grep -e 'gpgcheck\s*=\s*0' /etc/yum.repos.d/foo.repo + changed_when: false + +- name: verify 'gpgcheck' was enabled for 'bar' repository (not in whitelist) + command: grep -e 'gpgcheck\s*=\s*1' /etc/yum.repos.d/bar.repo + changed_when: false