Configure ClamAV integration with web servers and email systems for automated threat detection

Intermediate 45 min Apr 29, 2026 55 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Integrate ClamAV antivirus with NGINX, Apache, Postfix and Dovecot for automated file scanning and email protection. Set up real-time threat detection with monitoring and alerting.

Prerequisites

  • Root or sudo access
  • Web server (NGINX or Apache) installed
  • Mail server (Postfix/Dovecot) installed
  • At least 2GB RAM for ClamAV database
  • Mail utility for alerting

What this solves

ClamAV provides open-source antivirus scanning that integrates with web servers and email systems to automatically scan uploaded files and email attachments. This tutorial shows you how to configure ClamAV with NGINX, Apache, Postfix, and Dovecot to create a comprehensive threat detection system that protects your infrastructure from malware and viruses in real-time.

Step-by-step installation

Install ClamAV daemon and tools

Install the ClamAV antivirus engine and daemon that will handle scanning requests from web servers and email systems.

sudo apt update
sudo apt install -y clamav clamav-daemon clamav-freshclam clamav-milter
sudo systemctl stop clamav-freshclam
sudo dnf update -y
sudo dnf install -y clamav clamav-devel clamav-update clamav-milter
sudo systemctl stop clamav-freshclam

Update virus definitions

Download the latest virus signature database before starting the daemon. This ensures immediate threat detection capability.

sudo freshclam
sudo systemctl start clamav-freshclam
sudo systemctl enable clamav-freshclam

Configure ClamAV daemon

Set up the ClamAV daemon with network socket support for web server and email integration. Configure memory limits and scanning options.

LogFile /var/log/clamav/clamav.log
LogTime yes
LogFileUnlock yes
LogFileMaxSize 100M
LogRotate yes
LogVerbose yes
PidFile /var/run/clamav/clamd.pid
TemporaryDirectory /tmp
DatabaseDirectory /var/lib/clamav
LocalSocket /var/run/clamav/clamd.ctl
TCPSocket 3310
TCPAddr 127.0.0.1
MaxConnectionQueueLength 15
MaxThreads 20
ReadTimeout 300
CommandReadTimeout 30
SendBufTimeout 200
MaxQueue 100
IdleTimeout 30
ExcludePath ^/proc/
ExcludePath ^/sys/
AlertBrokenExecutables yes
AlertPhishingSSLMismatch yes
AlertPhishingCloak yes
AlertPartitionIntersection yes
ScanPE yes
ScanELF yes
ScanOLE2 yes
ScanPDF yes
ScanHTML yes
ScanMail yes
ScanArchive yes
ArchiveBlockEncrypted no
MaxScanSize 500M
MaxFileSize 100M
MaxRecursion 16
MaxFiles 10000
MaxEmbeddedPE 10M
MaxHTMLNormalize 10M
MaxHTMLNoTags 2M
MaxScriptNormalize 5M
MaxZipTypeRcg 1M
User clamav
Bytecode yes
BytecodeSecurity TrustSigned
BytecodeTimeout 60000
DetectPUA yes

Create ClamAV user and directories

Set up proper ownership and permissions for ClamAV directories and socket files.

sudo mkdir -p /var/run/clamav /var/log/clamav
sudo chown -R clamav:clamav /var/run/clamav /var/log/clamav
sudo chmod 755 /var/run/clamav /var/log/clamav

Start ClamAV daemon

Enable and start the ClamAV daemon service for immediate threat scanning capability.

sudo systemctl enable clamav-daemon
sudo systemctl start clamav-daemon
sudo systemctl status clamav-daemon

Configure NGINX integration

Install NGINX ClamAV module

Install the NGINX module that enables real-time file scanning for uploaded files through ClamAV integration.

sudo apt install -y nginx-extras
sudo apt install -y libnginx-mod-http-upstream-fair
sudo dnf install -y nginx nginx-mod-http-perl
sudo dnf install -y lua-devel pcre-devel

Configure NGINX upstream for ClamAV

Create an upstream configuration that routes file uploads through ClamAV scanning before processing.

upstream clamav_backend {
    server 127.0.0.1:3310 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

map $request_method $upload_method {
    POST "scan";
    PUT "scan";
    default "pass";
}

Create ClamAV scanning location

Configure NGINX location blocks that intercept file uploads and scan them through ClamAV before allowing access.

server {
    listen 80;
    server_name example.com;
    
    client_max_body_size 100M;
    client_body_temp_path /tmp/nginx_uploads;
    
    location /upload {
        if ($upload_method = "scan") {
            access_by_lua_block {
                local socket = require "resty.socket"
                local tcp = socket.tcp()
                local ok, err = tcp:connect("127.0.0.1", 3310)
                
                if not ok then
                    ngx.log(ngx.ERR, "Failed to connect to ClamAV: ", err)
                    ngx.status = 503
                    ngx.say("Antivirus scanning unavailable")
                    ngx.exit(503)
                end
                
                local bytes, err = tcp:send("PING\n")
                if not bytes then
                    ngx.log(ngx.ERR, "Failed to ping ClamAV")
                    ngx.status = 503
                    ngx.exit(503)
                end
                
                tcp:close()
            }
        }
        
        proxy_pass http://backend_servers;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
    
    location /health/clamav {
        access_log off;
        return 200 "ClamAV integration active\n";
        add_header Content-Type text/plain;
    }
}

Create upload scanning script

Create a script that scans uploaded files and returns appropriate HTTP status codes based on scan results.

#!/bin/bash

FILE_PATH="$1"
SOCKET="/var/run/clamav/clamd.ctl"

if [[ ! -f "$FILE_PATH" ]]; then
    echo "ERROR: File not found"
    exit 1
fi

Scan file with ClamAV

RESULT=$(clamdscan --fdpass "$FILE_PATH" 2>/dev/null) EXIT_CODE=$? if [[ $EXIT_CODE -eq 0 ]]; then echo "CLEAN: File passed antivirus scan" exit 0 elif [[ $EXIT_CODE -eq 1 ]]; then echo "INFECTED: $RESULT" # Log threat detection logger -t "nginx-clamav" "THREAT DETECTED: $FILE_PATH - $RESULT" # Remove infected file rm -f "$FILE_PATH" exit 1 else echo "ERROR: Scan failed" exit 2 fi
sudo chmod +x /usr/local/bin/nginx-clamav-scan
sudo chown root:root /usr/local/bin/nginx-clamav-scan

Configure Apache integration

Install Apache ClamAV module

Install the mod_clamav module for Apache that provides real-time antivirus scanning capabilities.

sudo apt install -y libapache2-mod-clamav
sudo a2enmod clamav
sudo systemctl reload apache2
sudo dnf install -y mod_clamav
echo "LoadModule clamav_module modules/mod_clamav.so" >> /etc/httpd/conf.modules.d/10-clamav.conf
sudo systemctl reload httpd

Configure Apache ClamAV scanning

Set up Apache virtual host configuration with ClamAV integration for automatic file upload scanning.


    ServerName example.com
    DocumentRoot /var/www/html
    
    # Enable ClamAV scanning
    ClamAVEnabled On
    ClamAVSocket /var/run/clamav/clamd.ctl
    ClamAVMaxFileSize 100M
    ClamAVAction Block
    ClamAVLogFile /var/log/apache2/clamav.log
    
    # Scan uploaded files
    
        ClamAVRequired On
        ClamAVBlock On
    
    
    # Directory with upload scanning
    
        Options +Indexes
        AllowOverride None
        Require all granted
        
        # Enable real-time scanning
        ClamAVScan On
        ClamAVQuarantine /var/quarantine/
    
    
    ErrorLog ${APACHE_LOG_DIR}/clamav_error.log
    CustomLog ${APACHE_LOG_DIR}/clamav_access.log combined

Create quarantine directory

Set up a secure quarantine directory for infected files detected by Apache ClamAV integration.

sudo mkdir -p /var/quarantine
sudo chown www-data:www-data /var/quarantine
sudo chmod 750 /var/quarantine

Enable Apache site and reload

Activate the ClamAV-enabled Apache site configuration and reload the web server.

sudo a2ensite clamav-scan.conf
sudo systemctl reload apache2
sudo cp /etc/apache2/sites-available/clamav-scan.conf /etc/httpd/conf.d/
sudo systemctl reload httpd

Configure Postfix email integration

Install Amavisd-new for email scanning

Install Amavisd-new which integrates ClamAV with Postfix for comprehensive email scanning and threat detection.

sudo apt install -y amavisd-new spamassassin
sudo adduser clamav amavis
sudo adduser amavis clamav
sudo dnf install -y amavisd-new spamassassin
sudo usermod -a -G amavis clamav
sudo usermod -a -G clamav amavis

Configure Amavisd-new

Set up Amavisd-new configuration to route email through ClamAV scanning with proper virus handling policies.

# Enable virus scanning
@bypass_virus_checks_maps = (
   %bypass_virus_checks, @bypass_virus_checks_acl, $bypass_virus_checks_re);

Enable spam scanning

@bypass_spam_checks_maps = ( %bypass_spam_checks, @bypass_spam_checks_acl, $bypass_spam_checks_re); 1; # ensure a defined return

Configure Amavisd-new main settings

Configure the main Amavisd-new settings for ClamAV integration with appropriate logging and quarantine policies.

use strict;

Daemon user and group

$daemon_user = "amavis"; $daemon_group = "amavis";

Directories

$MYHOME = '/var/lib/amavis'; $TEMPBASE = "$MYHOME/tmp"; $ENV{TMPDIR} = $TEMPBASE; $QUARANTINEDIR = '/var/lib/amavis/virusmails';

Logging

$log_level = 2; $enable_db = 1; $enable_global_cache = 1;

ClamAV settings

@av_scanners = ( ['ClamAV-clamd', \&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd.ctl"], qr/\bOK$/m, qr/\bFOUND$/m, qr/^.?: (?![\d\s]+$)(.) FOUND$/m ], );

Virus handling

$final_virus_destiny = D_DISCARD; $final_spam_destiny = D_REJECT;

Network settings

$inet_socket_port = 10024; $inet_socket_bind = '127.0.0.1'; $policy_bank{'ORIGINATING'} = { originating => 1, allow_disclaimers => 1, };

Interface policy

$interface_policy{'10025'} = 'ORIGINATING'; 1;

Configure Postfix master.cf

Modify Postfix master.cf to route email through Amavisd-new for ClamAV scanning before delivery.

# Add these lines to master.cf
amavis      unix    -       -       -       -       2       smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
    -o max_use=20

127.0.0.1:10025 inet    n       -       -       -       -       smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -o mynetworks=127.0.0.0/8
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
    -o smtpd_client_connection_count_limit=0
    -o smtpd_client_connection_rate_limit=0
    -o receive_override_options=no_header_body_checks,no_unknown_recipient_checks

Configure Postfix main.cf

Update Postfix main configuration to enable content filtering through Amavisd-new and ClamAV.

# Add or modify these lines in main.cf
content_filter = amavis:[127.0.0.1]:10024

Amavis settings

amavis_destination_recipient_limit = 1 amavis_destination_concurrency_limit = 2

Configure Dovecot integration

Install Dovecot ClamAV plugin

Install the Dovecot antivirus plugin that integrates with ClamAV for real-time email scanning during IMAP/POP3 access.

sudo apt install -y dovecot-antispam dovecot-sieve
sudo dnf install -y dovecot dovecot-pigeonhole

Configure Dovecot antivirus plugin

Set up Dovecot configuration to scan emails through ClamAV when users access their mailboxes via IMAP or POP3.

plugin {
    # Antivirus scanning
    antivirus_backend = clamav
    antivirus_allow_empty = yes
    antivirus_max_size = 100M
    antivirus_send_email = yes
    antivirus_quarantine = /var/quarantine/dovecot/
    
    # ClamAV specific settings
    clamav_socket = /var/run/clamav/clamd.ctl
    clamav_action = reject
    clamav_timeout = 60
    
    # Logging
    antivirus_debug = no
    antivirus_verbose = yes
}

Enable for IMAP

protocol imap { mail_plugins = $mail_plugins antivirus }

Enable for POP3

protocol pop3 { mail_plugins = $mail_plugins antivirus }

Create Dovecot quarantine directory

Set up a quarantine directory for Dovecot with appropriate permissions for virus isolation.

sudo mkdir -p /var/quarantine/dovecot
sudo chown dovecot:dovecot /var/quarantine/dovecot
sudo chmod 750 /var/quarantine/dovecot

Configure Dovecot Sieve for virus filtering

Set up Sieve rules that automatically handle virus-infected emails detected by ClamAV integration.

require ["fileinto", "reject", "envelope", "regex"];

Reject emails with viruses

if header :contains "X-Virus-Status" "infected" { reject "Message rejected: virus detected"; stop; }

Quarantine suspicious emails

if header :contains "X-Spam-Level" "*" { fileinto "Junk"; stop; }
sudo mkdir -p /var/mail/sieve
sudo chown -R dovecot:dovecot /var/mail/sieve
sudo sievec /var/mail/sieve/global.sieve

Set up monitoring and alerting

Create ClamAV monitoring script

Set up monitoring to track ClamAV daemon health, database updates, and scanning statistics with automated alerting.

#!/bin/bash

LOGFILE="/var/log/clamav-monitor.log"
STATUS_FILE="/var/run/clamav/monitor.status"
ALERT_EMAIL="admin@example.com"

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$LOGFILE"
}

Check ClamAV daemon status

check_daemon() { if ! systemctl is-active --quiet clamav-daemon; then log_message "ERROR: ClamAV daemon is not running" echo "DAEMON_DOWN" > "$STATUS_FILE" echo "ClamAV daemon is down on $(hostname)" | mail -s "ClamAV Alert" "$ALERT_EMAIL" return 1 fi return 0 }

Check database age

check_database() { DB_FILE="/var/lib/clamav/main.cvd" if [[ -f "$DB_FILE" ]]; then DB_AGE=$(find "$DB_FILE" -mtime +1 | wc -l) if [[ $DB_AGE -gt 0 ]]; then log_message "WARNING: Virus database is older than 24 hours" echo "DB_OLD" >> "$STATUS_FILE" fi else log_message "ERROR: Virus database not found" echo "DB_MISSING" > "$STATUS_FILE" return 1 fi return 0 }

Check socket connectivity

check_socket() { if ! clamdscan --ping &>/dev/null; then log_message "ERROR: Cannot connect to ClamAV socket" echo "SOCKET_ERROR" >> "$STATUS_FILE" return 1 fi return 0 }

Main monitoring

echo "CHECKING" > "$STATUS_FILE" if check_daemon && check_database && check_socket; then log_message "INFO: All ClamAV checks passed" echo "HEALTHY" > "$STATUS_FILE" else log_message "ERROR: ClamAV health check failed" echo "UNHEALTHY" > "$STATUS_FILE" fi
sudo chmod +x /usr/local/bin/clamav-monitor
sudo chown root:root /usr/local/bin/clamav-monitor

Set up automated scanning reports

Create a script that generates daily reports of scanning activity and threat detections across all integrated services.

#!/bin/bash

REPORT_DATE=$(date '+%Y-%m-%d')
REPORT_FILE="/tmp/clamav-report-$REPORT_DATE.txt"
LOG_FILE="/var/log/clamav/clamav.log"
EMAIL="admin@example.com"

Generate report header

cat > "$REPORT_FILE" << EOF ClamAV Daily Report - $REPORT_DATE ===================================== System: $(hostname) Report Generated: $(date) SCANNING STATISTICS: EOF

Extract statistics from logs

echo "Files Scanned Today: $(grep -c "$(date '+%a %b %d')" "$LOG_FILE" 2>/dev/null || echo "0")" >> "$REPORT_FILE" echo "Threats Detected: $(grep -c "FOUND" "$LOG_FILE" | grep "$(date '+%a %b %d')" 2>/dev/null || echo "0")" >> "$REPORT_FILE" echo "Database Updates: $(grep -c "Database updated" "/var/log/clamav/freshclam.log" 2>/dev/null || echo "0")" >> "$REPORT_FILE"

Service status

echo "" >> "$REPORT_FILE" echo "SERVICE STATUS:" >> "$REPORT_FILE" echo "ClamAV Daemon: $(systemctl is-active clamav-daemon)" >> "$REPORT_FILE" echo "Freshclam: $(systemctl is-active clamav-freshclam)" >> "$REPORT_FILE"

Recent threats

echo "" >> "$REPORT_FILE" echo "RECENT THREATS:" >> "$REPORT_FILE" grep "FOUND" "$LOG_FILE" | tail -10 >> "$REPORT_FILE" 2>/dev/null || echo "No recent threats detected" >> "$REPORT_FILE"

Email report

if [[ -s "$REPORT_FILE" ]]; then mail -s "ClamAV Daily Report - $(hostname)" "$EMAIL" < "$REPORT_FILE" fi

Clean up

rm -f "$REPORT_FILE"
sudo chmod +x /usr/local/bin/clamav-report
sudo chown root:root /usr/local/bin/clamav-report

Configure systemd timer for monitoring

Set up systemd timers to run monitoring checks and generate reports automatically at scheduled intervals.

[Unit]
Description=ClamAV Health Monitor
After=clamav-daemon.service

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/clamav-monitor
StandardOutput=journal
StandardError=journal
[Unit]
Description=Run ClamAV monitor every 5 minutes
Requires=clamav-monitor.service

[Timer]
OnCalendar=*:0/5
Persistent=true

[Install]
WantedBy=timers.target
[Unit]
Description=ClamAV Daily Report
After=clamav-daemon.service

[Service]
Type=oneshot
User=root
ExecStart=/usr/local/bin/clamav-report
StandardOutput=journal
StandardError=journal
[Unit]
Description=Generate ClamAV daily report
Requires=clamav-report.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target

Enable and start monitoring timers

Activate the systemd timers to begin automated monitoring and reporting of ClamAV integration health.

sudo systemctl daemon-reload
sudo systemctl enable clamav-monitor.timer
sudo systemctl enable clamav-report.timer
sudo systemctl start clamav-monitor.timer
sudo systemctl start clamav-report.timer

Restart all integrated services

Restart web servers and email services to activate ClamAV integration and begin real-time threat detection.

sudo systemctl restart clamav-daemon
sudo systemctl restart amavis
sudo systemctl restart postfix
sudo systemctl restart dovecot
sudo systemctl restart nginx
sudo systemctl restart apache2

Verify your setup

Test ClamAV daemon connectivity and integration with all services.

# Check ClamAV daemon status
sudo systemctl status clamav-daemon
clamdscan --ping

Test virus scanning

echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > /tmp/eicar.txt clamdscan /tmp/eicar.txt

Check integration status

sudo systemctl status amavis sudo systemctl status postfix sudo systemctl status dovecot

Verify monitoring

sudo systemctl list-timers | grep clamav sudo /usr/local/bin/clamav-monitor cat /var/run/clamav/monitor.status

Check virus database

sigtool --info /var/lib/clamav/main.cvd
Note: The EICAR test file should be detected as a threat. If it passes scanning, check your ClamAV configuration and database updates.

Common issues

Symptom Cause Fix
ClamAV daemon won't start Database missing or permissions wrong Run sudo freshclam and check /var/lib/clamav ownership
Socket connection refused Socket file missing or wrong permissions Check /var/run/clamav/clamd.ctl exists and has correct permissions
Web server can't scan uploads Web server user can't access ClamAV socket Add www-data to clamav group: sudo usermod -a -G clamav www-data
Email not being scanned Amavisd-new not properly integrated Check Postfix master.cf content_filter setting and Amavisd-new logs
High memory usage ClamAV loading full database in memory Adjust MaxScanSize and MaxFileSize in clamd.conf
Scanning too slow Insufficient ClamAV threads or timeouts Increase MaxThreads and timeout values in clamd.conf
False positives blocking files Overly aggressive PUA detection Disable DetectPUA or add exclusions to clamd.conf
Database updates failing Firewall blocking freshclam updates Allow outbound HTTP/HTTPS in firewall for database.clamav.net
Performance Warning: ClamAV scanning adds processing overhead. Monitor system resources and adjust MaxScanSize and MaxFileSize based on your server capacity and security requirements.

Next steps

Running this in production?

Want this handled for you? Setting up ClamAV integration once is straightforward. Keeping it patched, monitored, with virus definitions updated and performance tuned across environments is the harder part. See how we run infrastructure like this for European SaaS and e-commerce teams.

Automated install script

Run this to automate the entire setup

Need help?

Don't want to manage this yourself?

We handle infrastructure security hardening for businesses that depend on uptime. From initial setup to ongoing operations.