From f1a4cab61eb802d76618aeef97b73d24f685e729 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=B6rg=20Sachse?= <joerg.sachse@slub-dresden.de>
Date: Wed, 15 Nov 2023 12:22:25 +0100
Subject: [PATCH] feat: resolve ND-2709 'Bereinigung clamd-Services
 Rosetta-Server'

---
 handlers/main.yml                        |   8 +-
 molecule/centos7/molecule.yml            |  42 +++++++
 molecule/resources/playbooks/prepare.yml |  68 ++++++++---
 tasks/install_clamav.yml                 | 149 +++++++++++++++--------
 vars/clamav.yml                          |   2 +-
 5 files changed, 197 insertions(+), 72 deletions(-)
 create mode 100644 molecule/centos7/molecule.yml

diff --git a/handlers/main.yml b/handlers/main.yml
index 90600c2..5fd9a4c 100644
--- a/handlers/main.yml
+++ b/handlers/main.yml
@@ -76,6 +76,12 @@
 
 - name: restart clamd service
   ansible.builtin.service:
-    name: "clamd@{{ ansible_hostname }}.service"
+    name: "clamd@scan.service"
+    state: restarted
+  when: ansible_os_family == "RedHat"
+
+- name: restart freshclam
+  ansible.builtin.systemd:
+    name: "clamav-freshclam"
     state: restarted
   when: ansible_os_family == "RedHat"
diff --git a/molecule/centos7/molecule.yml b/molecule/centos7/molecule.yml
new file mode 100644
index 0000000..5f43804
--- /dev/null
+++ b/molecule/centos7/molecule.yml
@@ -0,0 +1,42 @@
+---
+dependency:
+  name: galaxy
+  enabled: false
+driver:
+  name: vagrant
+lint: |
+  set -e
+  yamllint .
+  ansible-lint -x no-loop-var-prefix,command-instead-of-module,package-latest
+platforms:
+  # Check out the documentation at
+  # https://github.com/ansible-community/molecule-vagrant#documentation
+  # for more platform parameters.
+  - name: vm-runner
+    box: centos/7
+    memory: 1024
+    # List of raw Vagrant `config` options.
+    # provider_raw_config_args:
+    #   - "customize [ 'modifyvm', :id, '--natdnshostresolver1', 'on' ]"
+    # Dictionary of `config` options.
+    config_options:
+      ssh.keep_alive: yes
+      ssh.remote_user: "lza"
+provisioner:
+  name: ansible
+  log: true
+  config_options:
+    defaults:
+      # https://stackoverflow.com/questions/57435811/ansible-molecule-pass-multiple-vault-ids
+      #vault_identity_list: "@$HOME/.ansible/roles/lza_install_common.pass, @$HOME/.ansible/roles/passfile_1.pass"
+      #vault_identity_list: "${MOLECULE_PROJECT_DIRECTORY}/../../lza_server_hardening.pass"
+      vault_identity_list: "../lza_server_hardening.pass, ../../../lza_server_hardening.pass"
+  vvv: false
+  playbooks:
+    # create: ../resources/playbooks/create.yml
+    # destroy: ../resources/playbooks/destroy.yml
+    converge: ../resources/playbooks/converge.yml
+    prepare: ../resources/playbooks/prepare.yml
+    verify: ../resources/playbooks/verify.yml
+verifier:
+  name: ansible
diff --git a/molecule/resources/playbooks/prepare.yml b/molecule/resources/playbooks/prepare.yml
index eb1e2ce..76fc5eb 100644
--- a/molecule/resources/playbooks/prepare.yml
+++ b/molecule/resources/playbooks/prepare.yml
@@ -1,25 +1,55 @@
 ---
 - name: Prepare
   hosts: "*"
-  tasks:
-    - name: install GPG
-      ansible.builtin.apt:
-        name: "gnupg"
-        state: latest
-        update_cache: true
-      become: true
-    - name: add GPG key for SLUB Debian repository
-      ansible.builtin.apt_key:
-        url: "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
-        state: present
-      become: true
-    - name: add repo URL to sources.list
-      ansible.builtin.apt_repository:
-        repo: "deb https://sdvdebianrepo.slub-dresden.de/deb-repository bullseye main"
-        state: present
-        update_cache: true
-        mode: "0644"
-      become: true
+  pre_tasks:
+    - name: configure additional package repositories for Debian
+      block:
+      - name: install GPG
+        ansible.builtin.apt:
+          name: "gnupg"
+          state: latest
+          update_cache: true
+        become: true
+      - name: add GPG key for SLUB Debian repository
+        ansible.builtin.apt_key:
+          url: "https://sdvdebianrepo.slub-dresden.de/deb-repository/pub.gpg.key"
+          state: present
+        become: true
+      - name: add repo URL to sources.list
+        ansible.builtin.apt_repository:
+          repo: "deb https://sdvdebianrepo.slub-dresden.de/deb-repository bookworm main"
+          state: present
+          update_cache: true
+          mode: "0644"
+        become: true
+      when: ansible_os_family == "Debian"
+
+    - name: configure additional package repositories for RedHat
+      block:
+      - name: add custom repositories
+        ansible.builtin.yum_repository:
+          name: "{{ item.name }}"
+          description: "{{ item.description }}"
+          baseurl: "{{ item.baseurl }}"
+          gpgcheck: "{{ item.gpgcheck | default('true') }}"
+          gpgkey: "{{ item.gpgkey | default(omit) }}"
+        loop:
+          - name: "epel"
+            description: EPEL YUM repo
+            baseurl: "https://download.fedoraproject.org/pub/epel/{{ ansible_distribution_major_version }}/x86_64/"
+            gpgkey: "https://dl.fedoraproject.org/pub/epel/RPM-GPG-KEY-EPEL-{{ ansible_distribution_major_version }}"
+          - name: "slub"
+            description: SLUB YUM repo
+            baseurl: "https://sdvrhelrepo.slub-dresden.de/"
+            gpgcheck: "false"
+        become: true
+      - name: remove legacy repo configuration to avoid double configuration for SLUB repo
+        ansible.builtin.file:
+          path: "/etc/yum.repos.d/SLUB.repo"
+          state: absent
+        become: true
+      when: ansible_os_family == "RedHat"
+
     # This Ansible role installs a multitude of firewall rules, some of which
     # will lock us out of our Molecule test VM if we don't take precautions.
     # As Molecule itself uses SSH just like Ansible, we need to open port
diff --git a/tasks/install_clamav.yml b/tasks/install_clamav.yml
index 4b84472..70e6f96 100644
--- a/tasks/install_clamav.yml
+++ b/tasks/install_clamav.yml
@@ -14,6 +14,14 @@
   when: ansible_os_family == "Debian"
   tags: [apt]
 
+- name: install EPEL so we have access to the ClamAV packages hosted there
+  ansible.builtin.yum:
+    name: "epel-release.noarch"
+    state: latest
+    update_cache: true
+  when: ansible_os_family == "RedHat"
+  tags: [yum]
+
 - name: install clamav packages (RedHat)
   ansible.builtin.yum:
     name: [
@@ -28,6 +36,7 @@
       "clamd",
     ]
     state: present
+    update_cache: true
   when: ansible_os_family == "RedHat"
   tags: [yum]
 
@@ -82,54 +91,58 @@
       DatabaseMirror db.de.clamav.net
       DatabaseMirror database.clamav.net
       OnUpdateExecute "/usr/local/bin/refresh_rkhunter.sh"
+  notify: restart freshclam
 
+- name: remove legacy config
+  ansible.builtin.file:
+    path: "{{ clamav_cfg_path }}/{{ ansible_hostname }}.conf"
+    state: absent
+
+# Config paths according to manpage/systemd-unit:
+#   - Debian: "/etc/clamav/clamd.conf"
+#   - RedHat: "/etc/clamd.d/scan.conf"
 - name: configure ClamD
   ansible.builtin.blockinfile:
-    name: "{{ clamav_cfg_path }}/{{ 'clamd' if ansible_os_family == 'Debian' else ansible_hostname }}.conf"
-    mode: "0444"
-    owner: "{{ 'clamav' if ansible_os_family == 'Debian' else 'clamscan' }}"
-    group: "adm"
-    create: true
-    insertafter: EOF
-    marker: "# {mark} ANSIBLE MANAGED BLOCK - CLAMD SCAN SETTINGS"
-    block: |
-      LogFileMaxSize 0
-      LogTime yes
-      LogVerbose yes
-      TemporaryDirectory /var/tmp
-      DatabaseDirectory /var/lib/clamav
-      FixStaleSocket yes
-      TCPSocket 3310
-      TCPAddr 127.0.0.1
-      MaxConnectionQueueLength 200
-      StreamMaxLength 4000M
-      # AllowSupplementaryGroups yes            # DEPRECATED
-      ScanPE yes
-      ScanELF yes
-      # DetectBrokenExecutables yes             # DEPRECATED
-      ScanOLE2 yes
-      ScanMail yes
-      ScanArchive yes
-      ArchiveBlockEncrypted no
-      OnAccessExcludeUname root
-      OnAccessIncludePath /
-  notify: restart clamd service
-
-- name: configure ClamD exclude paths
-  ansible.builtin.blockinfile:
-    name: "{{ clamav_cfg_path }}/{{ 'clamd' if ansible_os_family == 'Debian' else ansible_hostname }}.conf"
+    name: "{{ clamav_cfg_path }}/{{ 'clamd' if ansible_os_family == 'Debian' else 'scan' }}.conf"
     mode: "0444"
     owner: "{{ 'clamav' if ansible_os_family == 'Debian' else 'clamscan' }}"
     group: "adm"
     create: true
     insertafter: EOF
-    marker: "### {mark} ANSIBLE MANAGED BLOCK - CLAMD FILE WHITELIST"
-    block: |
-      # Exclude paths from being checked. Use 'man regex' to get more information about REGEX format (clamav uses the regex.c library).
-      # Default: ExcludePath REGEX
-      ExcludePath "/mnt/*"
-      # Default: disabled
-      OnAccessExcludePath "/mnt/*"
+    marker: "{{ item.marker }}"
+    block: "{{ item.block }}"
+  loop:
+    # configure general settings
+    - marker: "# {mark} ANSIBLE MANAGED BLOCK - CLAMD SCAN SETTINGS"
+      block: |
+        LogFileMaxSize 0
+        LogTime yes
+        LogVerbose yes
+        TemporaryDirectory /var/tmp
+        DatabaseDirectory /var/lib/clamav
+        FixStaleSocket yes
+        TCPSocket 3310
+        TCPAddr 127.0.0.1
+        MaxConnectionQueueLength 200
+        StreamMaxLength 4000M
+        # AllowSupplementaryGroups yes            # DEPRECATED
+        ScanPE yes
+        ScanELF yes
+        # DetectBrokenExecutables yes             # DEPRECATED
+        ScanOLE2 yes
+        ScanMail yes
+        ScanArchive yes
+        ArchiveBlockEncrypted no
+        OnAccessExcludeUname root
+        OnAccessIncludePath /
+    # configure ClamD exclude paths
+    - marker: "### {mark} ANSIBLE MANAGED BLOCK - CLAMD FILE WHITELIST"
+      block: |
+        # Exclude paths from being checked. Use 'man regex' to get more information about REGEX format (clamav uses the regex.c library).
+        # Default: ExcludePath REGEX
+        ExcludePath "/mnt/*"
+        # Default: disabled
+        OnAccessExcludePath "/mnt/*"
   notify:
     - restart clamav-daemon service
     - restart clamd service
@@ -150,20 +163,54 @@
           /usr/bin/rkhunter --propupd --nolog
       fi
 
-- name: copy systemd service
-  ansible.builtin.copy:
-   src: "/usr/lib/systemd/system/clamd@.service"
-   dest: "/etc/systemd/system/"
-   mode: "0644"
-   remote_src: true
+- name: enable Freshclam systemd service now to make sure we have signature databases on the system 
+  ansible.builtin.systemd:
+    service: "clamav-freshclam.service"
+    enabled: true
+    state: "started"
+  when: ansible_os_family == "RedHat"
+
+- name: wait for signature file to appear
+  ansible.builtin.wait_for:
+    path: "/var/lib/clamav/daily.cld"
   when: ansible_os_family == "RedHat"
 
-- name: enable ClamD & Freshclam systemd services
+- name: find out if unnecessary systemd service exists
+  ansible.builtin.stat:
+    path: "/etc/systemd/system/multi-user.target.wants/clamd@{{ ansible_hostname }}.service"
+  register: clamd_unit
+
+- name: remove unnecessary systemd services
+  ansible.builtin.systemd:
+    service: "clamd@{{ ansible_hostname }}.service"
+    state: stopped
+    enabled: false
+  loop:
+    - "clamd@{{ ansible_hostname }}.service"
+    - "clamd@.service"
+  when: 
+    - ( ansible_os_family == "RedHat" )
+    - ( clamd_unit.stat.exists )
+
+- name: remove custom clamd service
+  ansible.builtin.file:
+    path: "/etc/systemd/system/clamd@.service"
+    state: absent
+  when: 
+    - ( ansible_os_family == "RedHat" )
+    - ( clamd_unit.stat.exists )
+
+#- name: copy systemd service
+#  ansible.builtin.copy:
+#   src: "/usr/lib/systemd/system/clamd@.service"
+#   dest: "/etc/systemd/system/"
+#   mode: "0644"
+#   remote_src: true
+#  when: ansible_os_family == "RedHat"
+
+- name: enable ClamD systemd service
   ansible.builtin.systemd:
-    service: "{{ item }}.service"
+    service: "clamd@scan.service"
     enabled: true
     state: "started"
-  loop:
-    - "clamd@{{ ansible_hostname }}"
-    - "clamav-freshclam"
   when: ansible_os_family == "RedHat"
diff --git a/vars/clamav.yml b/vars/clamav.yml
index 1d5c455..13803f6 100644
--- a/vars/clamav.yml
+++ b/vars/clamav.yml
@@ -1,2 +1,2 @@
 ---
-clamav_cfg_path: "{{ '/etc/clamd.d/' if ansible_distribution == 'RedHat' else '/etc/clamav/' | default('/etc/') }}"
+clamav_cfg_path: "{{ '/etc/clamd.d/' if ansible_os_family == 'RedHat' else '/etc/clamav/' | default('/etc/') }}"
-- 
GitLab