Clam AV - Hardening

Clam AV - Hardening
Linux

I am in the process of ditching Microsoft Windows, it's not that I don't like it - I'm neutral on that front but I am moving to Linux. There are a number of reasons

  1. Privacy - Microsoft Windows is noisy as hell - Linux is not
  2. I need to become instinctive on what I am doing with Linux - making it my daily will help
  3. Linux is transparent or I believe so - I will leave it at that.
  4. Hardware longevity - replacing a laptop every 5 to 7 years is bloody wasteful

As such I don't believe the "why do you need antivirus on Linux" line. The fact that it runs nearly all the worlds' servers and has a growing footprint in desktops and laptops means it will eventually gain somebodies attention with malicious intent - if it hasn't already done so. I mean derivatives of it run routers, just about every IOT device, you name it.

So, I went with the standard solution for Linux antivirus protection.

I have gotten around to hardening it iaw the recommendations of the AI (the smarter of the 5 I have access to - yes it costs - but I learn heaps - yes, it's smarter than me). Yes, I have been writing a lot of code lately in addition to fixing my servers. Learn something new, code rewrite, find out your assumptions are wrong, code rewrite. It's never ending but I love it.

The following script is its recommendations. I have shared it to give anyone who reads this some ideas. I DONT say it's right but it's better than the base configuration.

So here it is

#!/bin/bash
# Harden ClamAV configuration for Debian/Mint systems
# Idempotent: safely re-runnable, only applies changes if missing
# Audit-style comments included for compliance traceability
# Root check added: script must be run as root to modify system configs

set -euo pipefail

# --- Root privilege check ---
if [[ "${EUID}" -ne 0 ]]; then
    echo "❌ This script must be run as root."
    exit 1
fi

CLAMD_CONF="/etc/clamav/clamd.conf"
FRESHCLAM_CONF="/etc/clamav/freshclam.conf"
LOGFILE="/var/log/clamav/harden_clamav.log"

# Ensure logfile exists
mkdir -p "$(dirname "$LOGFILE")"
touch "$LOGFILE"

log() {
    echo "$(date '+%F %T') : $*" | tee -a "$LOGFILE"
}

# Function to ensure a config directive exists and is set correctly
ensure_config() {
    local file="$1"
    local key="$2"
    local value="$3"

    if grep -qE "^[[:space:]]*$key" "$file"; then
        # Replace existing line
        sed -i "s|^[[:space:]]*$key.*|$key $value|" "$file"
        log "Updated $key in $file to $value"
    else
        # Append if missing
        echo "$key $value" >> "$file"
        log "Added $key $value to $file"
    fi
}

log "=== Hardening ClamAV configuration ==="

# --- Socket permissions ---
ensure_config "$CLAMD_CONF" "LocalSocketMode" "660"
ensure_config "$CLAMD_CONF" "LocalSocketGroup" "clamav"

# --- Logging ---
ensure_config "$CLAMD_CONF" "LogVerbose" "yes"
ensure_config "$CLAMD_CONF" "LogSyslog" "yes"
ensure_config "$CLAMD_CONF" "LogFacility" "LOG_LOCAL6"

# --- Exclusions ---
for path in "^/proc/" "^/sys/" "^/dev/" "^/run/" "^/var/lib/docker/" "^/var/lib/containers/" "^/var/lib/mysql/" "^/var/lib/postgresql/" "^/var/cache/apt/archives/" "^/var/infected/"; do
    if ! grep -q "ExcludePath $path" "$CLAMD_CONF"; then
        echo "ExcludePath $path" >> "$CLAMD_CONF"
        log "Added ExcludePath $path"
    fi
done

# --- Alerts ---
for key in AlertEncryptedArchive AlertEncryptedDoc AlertOLE2Macros AlertPhishingSSLMismatch AlertPhishingCloak; do
    ensure_config "$CLAMD_CONF" "$key" "yes"
done

# --- Database hygiene ---
ensure_config "$CLAMD_CONF" "OfficialDatabaseOnly" "yes"
ensure_config "$CLAMD_CONF" "FailIfCvdOlderThan" "7"

# --- Freshclam checks ---
ensure_config "$FRESHCLAM_CONF" "Checks" "24"

# --- On-Access Scanning (workstation protection) ---
# Enable scanning of user home directories only
ensure_config "$CLAMD_CONF" "OnAccessIncludePath" "/home"
ensure_config "$CLAMD_CONF" "OnAccessPrevention" "yes"

# Exclude system dirs from on-access scanning
for path in "/proc" "/sys" "/dev" "/run"; do
    if ! grep -q "OnAccessExcludePath $path" "$CLAMD_CONF"; then
        echo "OnAccessExcludePath $path" >> "$CLAMD_CONF"
        log "Added OnAccessExcludePath $path"
    fi
done

log "=== Restarting ClamAV services ==="

# Restart services
systemctl restart clamav-daemon clamav-freshclam || {
    log "ERROR: Failed to restart ClamAV services"
    exit 1
}

# Verify status
if systemctl is-active --quiet clamav-daemon && systemctl is-active --quiet clamav-freshclam; then
    log "SUCCESS: ClamAV services restarted and are active"
else
    log "ERROR: One or more ClamAV services failed to start"
    systemctl status clamav-daemon clamav-freshclam | tee -a "$LOGFILE"
    exit 1
fi

log "=== Harden ClamAV script completed successfully ==="

As you can see - it didn't change too much.

What’s Included

  • Root check: prevents non‑root execution.
  • PidFile enabled: /var/run/clamav/clamd.pid for monitoring.
  • LogFileMaxSize set to 50M: prevents runaway logs.
  • DetectPUA enabled: catches adware/toolbars.
  • OnAccessMaxFileSize bumped to 10 MB: broader coverage for downloads.
  • OnAccessIncludePath /home: protects user data.
  • Exclusions: system dirs, DBs, container storage, apt cache, quarantine.
  • Alerts: encrypted archives/docs, macros, phishing mismatches/cloaks.
  • Database hygiene: official DB only, fail if older than 7 days.
  • Service restart & verification: ensures daemon and updater are active.
  • Audit log: /var/log/clamav/harden_clamav.log records all changes.

Proof it works

2025-11-17 16:48:39 : === Hardening ClamAV configuration ===
2025-11-17 16:48:39 : Updated LocalSocketMode in /etc/clamav/clamd.conf to 660
2025-11-17 16:48:39 : Updated LocalSocketGroup in /etc/clamav/clamd.conf to clamav
2025-11-17 16:48:39 : Added PidFile /var/run/clamav/clamd.pid to /etc/clamav/clamd.conf
2025-11-17 16:48:39 : Updated LogFile in /etc/clamav/clamd.conf to /var/log/clamav/clamav.log
2025-11-17 16:48:39 : Added LogFileMaxSize 50M to /etc/clamav/clamd.conf
2025-11-17 16:48:39 : Updated LogTime in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated LogVerbose in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated LogSyslog in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated LogFacility in /etc/clamav/clamd.conf to LOG_LOCAL6
2025-11-17 16:48:40 : Updated LogRotate in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated AlertEncryptedArchive in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated AlertEncryptedDoc in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated AlertOLE2Macros in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated AlertPhishingSSLMismatch in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated AlertPhishingCloak in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated OfficialDatabaseOnly in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated FailIfCvdOlderThan in /etc/clamav/clamd.conf to 7
2025-11-17 16:48:40 : Added ConcurrentDatabaseReload yes to /etc/clamav/clamd.conf
2025-11-17 16:48:40 : Updated SelfCheck in /etc/clamav/clamd.conf to 3600
2025-11-17 16:48:40 : Updated AlgorithmicDetection in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated DetectPUA in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated MaxScanSize in /etc/clamav/clamd.conf to 104857600
2025-11-17 16:48:40 : Updated MaxFileSize in /etc/clamav/clamd.conf to 26214400
2025-11-17 16:48:40 : Updated MaxRecursion in /etc/clamav/clamd.conf to 16
2025-11-17 16:48:40 : Updated MaxFiles in /etc/clamav/clamd.conf to 10000
2025-11-17 16:48:40 : Updated OnAccessMaxFileSize in /etc/clamav/clamd.conf to 10485760
2025-11-17 16:48:40 : Updated OnAccessIncludePath in /etc/clamav/clamd.conf to /home
2025-11-17 16:48:40 : Updated OnAccessPrevention in /etc/clamav/clamd.conf to yes
2025-11-17 16:48:40 : Updated Checks in /etc/clamav/freshclam.conf to 24
2025-11-17 16:48:40 : === Restarting ClamAV services ===
2025-11-17 16:48:41 : SUCCESS: ClamAV services restarted and are active
2025-11-17 16:48:41 : === Harden ClamAV script completed successfully ===

Unlike Windows Professional and above you don't have to turn ASR on and a lot of services off with or without caveats. Like I said transparent.

Next job testing the SSH key reset/rotation script that the AI and I rewrote last night whilst I should have been asleep. It's much better to break your laptop than your server 😄

One massive learning on moving to Linux is none of the major cloud storage players provide Linux code to access their storage servers - which includes Microsoft, Google, Proton to name 3 - I mean what the hell.

To be honest for most people Linux would be a much safer option.

On a side note - the docker version 29 saga continues - I cut my losses but...

https://github.com/portainer/portainer/issues/12925#issuecomment-3540552277

But what the hell do I know.

#enoughsaid