Set up a secure ClamAV cluster with SSL/TLS encryption, certificate-based authentication, and load balancing for enterprise-grade antivirus scanning. This tutorial covers SSL certificate generation, encrypted inter-node communication, and HAProxy SSL termination.
Prerequisites
- Root or sudo access
- Multiple servers for cluster setup
- Basic understanding of SSL certificates
- Familiarity with ClamAV antivirus system
- Knowledge of load balancing concepts
What this solves
Enterprise environments require secure, scalable antivirus scanning that protects sensitive data during transmission and ensures authenticated access between cluster nodes. This tutorial configures a ClamAV cluster with SSL encryption for all communications, certificate-based authentication, and load balancing with SSL termination for high availability scanning operations.
Step-by-step configuration
Update system packages
Start by updating your package manager to ensure you have the latest security patches.
sudo apt update && sudo apt upgrade -y
Install ClamAV and dependencies
Install ClamAV daemon, client tools, and OpenSSL for certificate management.
sudo apt install -y clamav clamav-daemon clamav-freshclam openssl haproxy
Create SSL certificate authority
Generate a private Certificate Authority for signing cluster node certificates. This ensures only authorized nodes can join the cluster.
sudo mkdir -p /etc/clamav/ssl
sudo cd /etc/clamav/ssl
sudo openssl genrsa -out ca-key.pem 4096
sudo openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem -subj "/CN=ClamAV-CA/O=Internal/C=US"
Generate server certificates for each node
Create individual SSL certificates for each ClamAV cluster node. Replace NODE1_IP with your actual server IP addresses.
export NODE1_IP="203.0.113.10"
export NODE2_IP="203.0.113.11"
export NODE3_IP="203.0.113.12"
sudo openssl genrsa -out node1-key.pem 2048
sudo openssl req -new -key node1-key.pem -out node1-csr.pem -subj "/CN=${NODE1_IP}/O=ClamAV-Node/C=US"
sudo openssl x509 -req -days 365 -in node1-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out node1-cert.pem
sudo openssl genrsa -out node2-key.pem 2048
sudo openssl req -new -key node2-key.pem -out node2-csr.pem -subj "/CN=${NODE2_IP}/O=ClamAV-Node/C=US"
sudo openssl x509 -req -days 365 -in node2-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out node2-cert.pem
sudo openssl genrsa -out node3-key.pem 2048
sudo openssl req -new -key node3-key.pem -out node3-csr.pem -subj "/CN=${NODE3_IP}/O=ClamAV-Node/C=US"
sudo openssl x509 -req -days 365 -in node3-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out node3-cert.pem
Set proper certificate permissions
Configure secure permissions for SSL certificates. The clamav user needs read access to certificates, but private keys must be protected from other users.
sudo chown -R root:clamav /etc/clamav/ssl
sudo chmod 750 /etc/clamav/ssl
sudo chmod 640 /etc/clamav/ssl/*.pem
sudo chmod 600 /etc/clamav/ssl/*-key.pem
Configure ClamAV daemon with SSL on Node 1
Configure the first node's ClamAV daemon to use SSL encryption and bind to the network interface.
# Remove or comment out LocalSocket
LocalSocket /run/clamav/clamd.ctl
Network configuration with SSL
TCPSocket 3310
TCPAddr 203.0.113.10
MaxConnectionQueueLength 200
MaxThreads 20
SSL Configuration
SSL yes
SSLCertificate /etc/clamav/ssl/node1-cert.pem
SSLKey /etc/clamav/ssl/node1-key.pem
SSLCACertificate /etc/clamav/ssl/ca-cert.pem
SSLVerifyClient yes
Security settings
User clamav
LogFile /var/log/clamav/clamav.log
LogTime yes
LogClean yes
LogSyslog yes
LogRotate yes
ExtendedDetectionInfo yes
Performance tuning
ReadTimeout 300
CommandReadTimeout 30
SendBufTimeout 500
MaxQueue 1000
IdleTimeout 120
ExcludePath ^/proc/
ExcludePath ^/sys/
Configure ClamAV daemon with SSL on Node 2
Configure the second node with its specific IP address and certificate.
# Network configuration with SSL
TCPSocket 3310
TCPAddr 203.0.113.11
MaxConnectionQueueLength 200
MaxThreads 20
SSL Configuration
SSL yes
SSLCertificate /etc/clamav/ssl/node2-cert.pem
SSLKey /etc/clamav/ssl/node2-key.pem
SSLCACertificate /etc/clamav/ssl/ca-cert.pem
SSLVerifyClient yes
Security settings
User clamav
LogFile /var/log/clamav/clamav.log
LogTime yes
LogClean yes
LogSyslog yes
LogRotate yes
ExtendedDetectionInfo yes
Performance tuning
ReadTimeout 300
CommandReadTimeout 30
SendBufTimeout 500
MaxQueue 1000
IdleTimeout 120
ExcludePath ^/proc/
ExcludePath ^/sys/
Configure ClamAV daemon with SSL on Node 3
Configure the third node with its specific IP address and certificate.
# Network configuration with SSL
TCPSocket 3310
TCPAddr 203.0.113.12
MaxConnectionQueueLength 200
MaxThreads 20
SSL Configuration
SSL yes
SSLCertificate /etc/clamav/ssl/node3-cert.pem
SSLKey /etc/clamav/ssl/node3-key.pem
SSLCACertificate /etc/clamav/ssl/ca-cert.pem
SSLVerifyClient yes
Security settings
User clamav
LogFile /var/log/clamav/clamav.log
LogTime yes
LogClean yes
LogSyslog yes
LogRotate yes
ExtendedDetectionInfo yes
Performance tuning
ReadTimeout 300
CommandReadTimeout 30
SendBufTimeout 500
MaxQueue 1000
IdleTimeout 120
ExcludePath ^/proc/
ExcludePath ^/sys/
Configure FreshClam for signature updates
Configure automatic virus signature updates. This ensures all cluster nodes have the latest threat definitions.
DatabaseOwner clamav
UpdateLogFile /var/log/clamav/freshclam.log
LogTime yes
LogSyslog yes
LogRotate yes
DatabaseDirectory /var/lib/clamav
DNSDatabaseInfo current.cvd.clamav.net
DatabaseMirror db.local.clamav.net
DatabaseMirror database.clamav.net
MaxAttempts 5
Checks 24
CompressLocalDatabase yes
NotifyClamd /etc/clamav/clamd.conf
Create client authentication certificates
Generate client certificates for applications that will connect to the ClamAV cluster.
sudo openssl genrsa -out client-key.pem 2048
sudo openssl req -new -key client-key.pem -out client-csr.pem -subj "/CN=ClamAV-Client/O=ClamAV-Clients/C=US"
sudo openssl x509 -req -days 365 -in client-csr.pem -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out client-cert.pem
sudo chmod 644 /etc/clamav/ssl/client-cert.pem
sudo chmod 600 /etc/clamav/ssl/client-key.pem
Configure HAProxy for SSL termination and load balancing
Set up HAProxy to provide SSL termination and distribute scanning requests across cluster nodes with health checks.
global
log stdout local0 info
chroot /var/lib/haproxy
stats socket /run/haproxy/admin.sock mode 660 level admin
stats timeout 30s
user haproxy
group haproxy
daemon
ssl-default-bind-ciphers ECDHE+AESGCM:ECDHE+CHACHA20:RSA+AESGCM:RSA+AES:!aNULL:!MD5:!DSS
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
mode tcp
log global
option tcplog
option dontlognull
option log-health-checks
timeout connect 5000
timeout client 300000
timeout server 300000
timeout check 5000
retries 3
frontend clamav_ssl_frontend
bind *:3311 ssl crt /etc/ssl/certs/haproxy-combined.pem ca-file /etc/clamav/ssl/ca-cert.pem verify required
mode tcp
default_backend clamav_cluster
frontend clamav_stats
bind *:8404
mode http
stats enable
stats uri /stats
stats refresh 30s
stats admin if TRUE
backend clamav_cluster
mode tcp
balance roundrobin
option tcp-check
tcp-check send "PING\n"
tcp-check expect string "PONG"
server node1 203.0.113.10:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem crt /etc/clamav/ssl/client-cert.pem
server node2 203.0.113.11:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem crt /etc/clamav/ssl/client-cert.pem
server node3 203.0.113.12:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem crt /etc/clamav/ssl/client-cert.pem
Create HAProxy SSL certificate bundle
HAProxy requires a combined certificate file containing both the certificate and private key.
sudo openssl genrsa -out haproxy-key.pem 2048
sudo openssl req -new -key haproxy-key.pem -out haproxy-csr.pem -subj "/CN=clamav-cluster.example.com/O=HAProxy/C=US"
sudo openssl x509 -req -days 365 -in haproxy-csr.pem -CA /etc/clamav/ssl/ca-cert.pem -CAkey /etc/clamav/ssl/ca-key.pem -CAcreateserial -out haproxy-cert.pem
sudo cat haproxy-cert.pem haproxy-key.pem > /etc/ssl/certs/haproxy-combined.pem
sudo chmod 600 /etc/ssl/certs/haproxy-combined.pem
sudo chown haproxy:haproxy /etc/ssl/certs/haproxy-combined.pem
Configure firewall rules
Open necessary ports for the ClamAV cluster and HAProxy load balancer.
sudo ufw allow 3310/tcp comment "ClamAV SSL"
sudo ufw allow 3311/tcp comment "HAProxy ClamAV SSL Frontend"
sudo ufw allow 8404/tcp comment "HAProxy Stats"
sudo ufw reload
Start and enable services
Start all ClamAV daemons and HAProxy on each respective server.
sudo systemctl enable --now clamav-freshclam
sudo systemctl enable --now clamav-daemon
sudo systemctl enable --now haproxy
Create SSL client connection script
Create a helper script for testing encrypted connections to the ClamAV cluster.
#!/bin/bash
ClamAV SSL Client Connection Script
CLAMAV_HOST="203.0.113.10"
CLAMAV_PORT="3311"
CLIENT_CERT="/etc/clamav/ssl/client-cert.pem"
CLIENT_KEY="/etc/clamav/ssl/client-key.pem"
CA_CERT="/etc/clamav/ssl/ca-cert.pem"
if [ $# -eq 0 ]; then
echo "Usage: $0 "
exit 1
fi
FILE_TO_SCAN="$1"
if [ ! -f "$FILE_TO_SCAN" ]; then
echo "Error: File '$FILE_TO_SCAN' not found"
exit 1
fi
Use clamdscan with SSL options
clamdscan --config-file=/dev/null \
--stream \
--fdpass \
--multiscan \
"$FILE_TO_SCAN"
echo "Scan completed via encrypted connection"
sudo chmod 755 /usr/local/bin/clamav-ssl-scan
Verify your setup
Test SSL connectivity, certificate authentication, and load balancer functionality.
# Check ClamAV daemon status on each node
sudo systemctl status clamav-daemon
sudo systemctl status clamav-freshclam
# Test SSL connection to individual nodes
openssl s_client -connect 203.0.113.10:3310 -cert /etc/clamav/ssl/client-cert.pem -key /etc/clamav/ssl/client-key.pem -CAfile /etc/clamav/ssl/ca-cert.pem -verify_return_error
# Test HAProxy SSL termination
curl -k https://203.0.113.10:8404/stats
# Test encrypted scanning
echo "X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" > /tmp/eicar.txt
/usr/local/bin/clamav-ssl-scan /tmp/eicar.txt
# Check cluster health through HAProxy
echo "PING" | openssl s_client -connect 203.0.113.10:3311 -cert /etc/clamav/ssl/client-cert.pem -key /etc/clamav/ssl/client-key.pem -CAfile /etc/clamav/ssl/ca-cert.pem -quiet
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| SSL handshake failure | Certificate verification failed | Check certificate CN matches server IP, verify CA chain |
| Connection refused on port 3310 | ClamAV not binding to network interface | Check TCPAddr setting in clamd.conf, verify firewall rules |
| HAProxy backend servers marked as down | SSL client certificate not configured | Verify client certificate path in HAProxy backend configuration |
| Permission denied accessing certificates | Wrong file ownership or permissions | sudo chown -R root:clamav /etc/clamav/ssl && sudo chmod 640 *.pem |
| ClamAV daemon won't start | SSL certificate file not found | Verify certificate paths in clamd.conf, check file permissions |
| Client authentication failed | Client certificate not trusted by CA | Regenerate client certificate with correct CA signing |
Next steps
- Scale ClamAV cluster with additional nodes and monitoring
- Monitor ClamAV cluster performance with Elasticsearch and Kibana
- Set up NGINX reverse proxy with SSL termination for web applications
- Automate ClamAV cluster deployment with Ansible playbooks
- Implement backup and disaster recovery for ClamAV cluster configuration
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Default configuration
DEFAULT_NODE_IPS="192.168.1.10,192.168.1.11,192.168.1.12"
NODE_IPS="${1:-$DEFAULT_NODE_IPS}"
CURRENT_NODE="${2:-1}"
usage() {
echo "Usage: $0 [NODE_IPS] [CURRENT_NODE]"
echo " NODE_IPS: Comma-separated list of cluster node IPs (default: $DEFAULT_NODE_IPS)"
echo " CURRENT_NODE: Current node number 1-3 (default: 1)"
echo "Example: $0 '10.0.1.10,10.0.1.11,10.0.1.12' 1"
exit 1
}
error_exit() {
echo -e "${RED}ERROR: $1${NC}" >&2
exit 1
}
warn() {
echo -e "${YELLOW}WARNING: $1${NC}"
}
success() {
echo -e "${GREEN}SUCCESS: $1${NC}"
}
# Validate arguments
if [[ ! "$NODE_IPS" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+,[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+,[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
error_exit "Invalid NODE_IPS format. Use comma-separated IPs like: 192.168.1.10,192.168.1.11,192.168.1.12"
fi
if [[ ! "$CURRENT_NODE" =~ ^[1-3]$ ]]; then
error_exit "CURRENT_NODE must be 1, 2, or 3"
fi
# Parse node IPs
IFS=',' read -ra NODE_ARRAY <<< "$NODE_IPS"
NODE1_IP="${NODE_ARRAY[0]}"
NODE2_IP="${NODE_ARRAY[1]}"
NODE3_IP="${NODE_ARRAY[2]}"
# Get current node IP
case $CURRENT_NODE in
1) CURRENT_IP="$NODE1_IP" ;;
2) CURRENT_IP="$NODE2_IP" ;;
3) CURRENT_IP="$NODE3_IP" ;;
esac
# Check if running as root
if [[ $EUID -ne 0 ]]; then
error_exit "This script must be run as root"
fi
# Cleanup function for rollback
cleanup() {
warn "Script failed. Cleaning up..."
systemctl stop clamav-daemon haproxy 2>/dev/null || true
rm -rf /etc/clamav/ssl 2>/dev/null || true
}
trap cleanup ERR
# Detect distribution
echo "[1/10] Detecting operating system..."
if [ -f /etc/os-release ]; then
. /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_INSTALL="apt install -y"
SERVICE_CMD="systemctl"
CLAMAV_CONF="/etc/clamav/clamd.conf"
FRESHCLAM_CONF="/etc/clamav/freshclam.conf"
HAPROXY_CONF="/etc/haproxy/haproxy.cfg"
;;
almalinux|rocky|centos|rhel|ol|fedora)
PKG_MGR="dnf"
PKG_INSTALL="dnf install -y"
SERVICE_CMD="systemctl"
CLAMAV_CONF="/etc/clamd.d/scan.conf"
FRESHCLAM_CONF="/etc/freshclam.conf"
HAPROXY_CONF="/etc/haproxy/haproxy.cfg"
;;
amzn)
PKG_MGR="yum"
PKG_INSTALL="yum install -y"
SERVICE_CMD="systemctl"
CLAMAV_CONF="/etc/clamd.d/scan.conf"
FRESHCLAM_CONF="/etc/freshclam.conf"
HAPROXY_CONF="/etc/haproxy/haproxy.cfg"
;;
*)
error_exit "Unsupported distribution: $ID"
;;
esac
success "Detected $PRETTY_NAME"
else
error_exit "Cannot detect operating system"
fi
# Update system packages
echo "[2/10] Updating system packages..."
case $PKG_MGR in
apt) apt update && apt upgrade -y ;;
*) $PKG_INSTALL update -y ;;
esac
# Install packages
echo "[3/10] Installing ClamAV and dependencies..."
case $ID in
ubuntu|debian)
$PKG_INSTALL clamav clamav-daemon clamav-freshclam openssl haproxy
;;
*)
$PKG_INSTALL clamav clamd clamav-update openssl haproxy
# Enable EPEL for additional packages if needed
if command -v dnf >/dev/null 2>&1; then
dnf install -y epel-release 2>/dev/null || true
fi
;;
esac
# Create SSL directory and certificates
echo "[4/10] Creating SSL Certificate Authority..."
mkdir -p /etc/clamav/ssl
cd /etc/clamav/ssl
# Generate CA
openssl genrsa -out ca-key.pem 4096
openssl req -new -x509 -days 3650 -key ca-key.pem -out ca-cert.pem -subj "/CN=ClamAV-CA/O=Internal/C=US"
echo "[5/10] Generating node certificates..."
# Generate certificates for all nodes
for i in {1..3}; do
node_ip="${NODE_ARRAY[$((i-1))]}"
openssl genrsa -out "node${i}-key.pem" 2048
openssl req -new -key "node${i}-key.pem" -out "node${i}-csr.pem" -subj "/CN=${node_ip}/O=ClamAV-Node/C=US"
openssl x509 -req -days 365 -in "node${i}-csr.pem" -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out "node${i}-cert.pem"
rm "node${i}-csr.pem"
done
echo "[6/10] Setting SSL certificate permissions..."
chown -R root:clamav /etc/clamav/ssl
chmod 750 /etc/clamav/ssl
chmod 640 /etc/clamav/ssl/*.pem
chmod 600 /etc/clamav/ssl/*-key.pem
# Configure ClamAV daemon
echo "[7/10] Configuring ClamAV daemon..."
# Create clamav user if it doesn't exist
if ! id clamav >/dev/null 2>&1; then
useradd -r -s /bin/false clamav
fi
# Create log directory
mkdir -p /var/log/clamav
chown clamav:clamav /var/log/clamav
chmod 755 /var/log/clamav
# Configure ClamAV based on distribution
cat > "$CLAMAV_CONF" << EOF
# Network configuration with SSL
TCPSocket 3310
TCPAddr $CURRENT_IP
MaxConnectionQueueLength 200
MaxThreads 20
# SSL Configuration
SSL yes
SSLCertificate /etc/clamav/ssl/node${CURRENT_NODE}-cert.pem
SSLKey /etc/clamav/ssl/node${CURRENT_NODE}-key.pem
SSLCACertificate /etc/clamav/ssl/ca-cert.pem
SSLVerifyClient yes
# Security settings
User clamav
LogFile /var/log/clamav/clamav.log
LogTime yes
LogClean yes
LogSyslog yes
LogRotate yes
ExtendedDetectionInfo yes
# Performance tuning
ReadTimeout 300
CommandReadTimeout 30
SendBufTimeout 500
MaxQueue 1000
IdleTimeout 120
ExcludePath ^/proc/
ExcludePath ^/sys/
ExcludePath ^/dev/
# Database settings
DatabaseDirectory /var/lib/clamav
EOF
# Configure freshclam
echo "[8/10] Configuring FreshClam..."
sed -i 's/^Example/#Example/' "$FRESHCLAM_CONF" 2>/dev/null || true
echo "DatabaseOwner clamav" >> "$FRESHCLAM_CONF"
# Configure HAProxy for load balancing
echo "[9/10] Configuring HAProxy load balancer..."
cat > "$HAPROXY_CONF" << EOF
global
daemon
user haproxy
group haproxy
log stdout local0
defaults
mode tcp
timeout connect 10s
timeout client 30s
timeout server 30s
log global
frontend clamav_frontend
bind *:3310 ssl crt /etc/clamav/ssl/node${CURRENT_NODE}-cert.pem
default_backend clamav_cluster
backend clamav_cluster
balance roundrobin
option tcp-check
server node1 $NODE1_IP:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem
server node2 $NODE2_IP:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem
server node3 $NODE3_IP:3310 check ssl verify required ca-file /etc/clamav/ssl/ca-cert.pem
EOF
# Create combined certificate for HAProxy
cat "/etc/clamav/ssl/node${CURRENT_NODE}-cert.pem" "/etc/clamav/ssl/node${CURRENT_NODE}-key.pem" > "/etc/clamav/ssl/node${CURRENT_NODE}-combined.pem"
chmod 600 "/etc/clamav/ssl/node${CURRENT_NODE}-combined.pem"
# Update virus definitions
echo "Updating virus definitions..."
freshclam
# Start and enable services
echo "[10/10] Starting and enabling services..."
case $ID in
ubuntu|debian)
$SERVICE_CMD enable clamav-daemon clamav-freshclam haproxy
$SERVICE_CMD start clamav-freshclam
$SERVICE_CMD start clamav-daemon
$SERVICE_CMD start haproxy
;;
*)
# RHEL-based systems may use clamd@scan
$SERVICE_CMD enable clamd@scan clamav-freshclam haproxy
$SERVICE_CMD start clamav-freshclam
$SERVICE_CMD start clamd@scan
$SERVICE_CMD start haproxy
;;
esac
# Configure firewall
if command -v ufw >/dev/null 2>&1; then
ufw allow 3310/tcp
elif command -v firewall-cmd >/dev/null 2>&1; then
firewall-cmd --permanent --add-port=3310/tcp
firewall-cmd --reload
fi
# Verification
echo "Verifying installation..."
sleep 5
if $SERVICE_CMD is-active --quiet clamav-daemon || $SERVICE_CMD is-active --quiet clamd@scan; then
success "ClamAV daemon is running"
else
error_exit "ClamAV daemon failed to start"
fi
if $SERVICE_CMD is-active --quiet haproxy; then
success "HAProxy is running"
else
warn "HAProxy may not be running correctly"
fi
if netstat -tlnp | grep -q ":3310"; then
success "ClamAV is listening on port 3310"
else
warn "ClamAV may not be listening on the expected port"
fi
success "ClamAV SSL cluster node $CURRENT_NODE installation completed successfully!"
echo "Node IP: $CURRENT_IP"
echo "SSL certificates created for all cluster nodes"
echo "Load balancer configured with SSL termination"
echo ""
echo "Next steps:"
echo "1. Run this script on the other cluster nodes with appropriate CURRENT_NODE values"
echo "2. Copy SSL certificates to other nodes or generate them there"
echo "3. Test connectivity between cluster nodes"
echo "4. Monitor logs at /var/log/clamav/clamav.log"
Review the script before running. Execute with: bash install.sh