Files
MajorWiki/02-selfhosting/security/clamav-fleet-deployment.md
majorlinux b40e484aae Add 5 wiki articles from 2026-04-17/18 work
- ghost-smtp-mailgun-setup: two-system email config (newsletter API + transactional SMTP)
- firewalld-fleet-hardening: Fedora fleet firewall audit-and-harden pattern with Ansible
- clamav-fleet-deployment: fleet deployment with nice/ionice throttling + quarantine
- ansible-check-mode-false-positives: when: not ansible_check_mode guard for verify/assert tasks
- ghost-emailanalytics-lag-warning: submitted status, lag counter, fetchMissing skip explained
2026-04-18 11:13:39 -04:00

5.2 KiB
Raw Blame History

title, domain, category, tags, status, created, updated
title domain category tags status created updated
ClamAV Fleet Deployment with Ansible selfhosting security
clamav
antivirus
security
ansible
fleet
cron
published 2026-04-18 2026-04-18T11:13

ClamAV Fleet Deployment with Ansible

Overview

ClamAV is the standard open-source antivirus for Linux servers. For internet-facing hosts, a weekly scan with fresh definitions catches known malware, web shells, and suspicious files before they cause damage. The key operational concern is CPU impact — an unthrottled clamscan will saturate a core for hours on a busy host. The solution is nice and ionice wrappers.

This guide covers deployment to internet-facing hosts. Internal-only hosts (storage, inference, gaming) are lower priority and can be skipped.

What Gets Deployed

  • clamav + clamav-update packages (provides clamscan + freshclam)
  • freshclam service enabled for automatic definition updates
  • A quarantine directory at /var/lib/clamav/quarantine/
  • A weekly clamscan cron job, niced to background priority
  • SELinux context set on the quarantine directory (Fedora hosts)

Ansible Playbook

- name: Deploy ClamAV to internet-facing hosts
  hosts: internet_facing  # dca, majorlinux, teelia, tttpod, majortoot, majormail
  become: true

  tasks:

    - name: Install ClamAV packages
      ansible.builtin.package:
        name:
          - clamav
          - clamav-update
        state: present

    - name: Enable and start freshclam
      ansible.builtin.service:
        name: clamav-freshclam
        enabled: true
        state: started

    - name: Create quarantine directory
      ansible.builtin.file:
        path: /var/lib/clamav/quarantine
        state: directory
        owner: root
        group: root
        mode: '0700'

    - name: Set SELinux context on quarantine dir (Fedora/RHEL)
      ansible.builtin.command:
        cmd: chcon -t var_t /var/lib/clamav/quarantine
      when: ansible_os_family == "RedHat"
      changed_when: false

    - name: Deploy weekly clamscan cron job
      ansible.builtin.cron:
        name: "Weekly ClamAV scan"
        user: root
        weekday: "0"   # Sunday
        hour: "3"
        minute: "0"
        job: >-
          nice -n 19 ionice -c 3
          clamscan -r /
          --exclude-dir=^/proc
          --exclude-dir=^/sys
          --exclude-dir=^/dev
          --exclude-dir=^/run
          --move=/var/lib/clamav/quarantine
          --log=/var/log/clamav/scan.log
          --quiet
          2>&1 | logger -t clamscan

The nice/ionice Flags

Without throttling, clamscan -r / will peg a CPU core for 3090 minutes depending on disk size and file count. On production hosts this causes Netdata alerts and visible service degradation.

Flag Value Meaning
nice -n 19 Lowest CPU priority Kernel will preempt this process for anything else
ionice -c 3 Idle I/O class Disk I/O only runs when no other process needs the disk

With both flags set, clamscan becomes essentially invisible under normal load. The scan takes longer (possibly 24× on busy disks), but this is acceptable for a weekly background job.

SELinux on Fedora/Fedora: ionice may trigger AVC denials under SELinux Enforcing. If scans silently fail on Fedora hosts, check ausearch -m avc -ts recent for clamscan denials. See selinux-fail2ban-execmem-fix for the pattern.

Excluded Paths

Always exclude virtual/pseudo filesystems — scanning them wastes time and can trigger false positives or kernel errors:

--exclude-dir=^/proc   # Process info (not real files)
--exclude-dir=^/sys    # Kernel interfaces
--exclude-dir=^/dev    # Device nodes
--exclude-dir=^/run    # Runtime tmpfs

You may also want to exclude large data directories (/var/lib/docker, backup volumes, media stores) if scan time is a concern. These are lower-risk targets anyway.

Quarantine vs Delete

--move=/var/lib/clamav/quarantine moves detected files rather than deleting them. This is safer than --remove — you can inspect and restore false positives. Review the quarantine directory periodically:

ls -la /var/lib/clamav/quarantine/

If a file is a confirmed false positive, restore it and add it to /etc/clamav/whitelist.ign2.

Checking Scan Results

# View last scan log
cat /var/log/clamav/scan.log

# Summary line from the log
grep -E "^Infected|^Scanned" /var/log/clamav/scan.log | tail -5

# Check freshclam is keeping definitions current
systemctl status clamav-freshclam
freshclam --version

Verifying Deployment

Test that ClamAV can detect malware using the EICAR test file (a harmless string that all AV tools recognize as test malware):

echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
  > /tmp/eicar-test.txt
clamscan /tmp/eicar-test.txt
# Expected: /tmp/eicar-test.txt: Eicar-Signature FOUND
rm /tmp/eicar-test.txt

See Also