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
This commit is contained in:
154
02-selfhosting/security/clamav-fleet-deployment.md
Normal file
154
02-selfhosting/security/clamav-fleet-deployment.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
title: ClamAV Fleet Deployment with Ansible
|
||||
domain: selfhosting
|
||||
category: security
|
||||
tags:
|
||||
- clamav
|
||||
- antivirus
|
||||
- security
|
||||
- ansible
|
||||
- fleet
|
||||
- cron
|
||||
status: published
|
||||
created: 2026-04-18
|
||||
updated: 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
|
||||
|
||||
```yaml
|
||||
- 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 30–90 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 2–4× 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](../../05-troubleshooting/selinux-fail2ban-execmem-fix.md) 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:
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
```bash
|
||||
# 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):
|
||||
|
||||
```bash
|
||||
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
|
||||
|
||||
- [clamscan-cpu-spike-nice-ionice](../../05-troubleshooting/security/clamscan-cpu-spike-nice-ionice.md) — troubleshooting CPU spikes from unthrottled scans
|
||||
- [linux-server-hardening-checklist](linux-server-hardening-checklist.md)
|
||||
- [ssh-hardening-ansible-fleet](ssh-hardening-ansible-fleet.md)
|
||||
Reference in New Issue
Block a user