Scanning every email for viruses: installing ClamAV with freshclam signature updates, integrating it as a content filter via Amavis, routing Postfix mail through the filter queue, and limiting attacks with rate limiting and message size limits.

SpamAssassin in episode 14 handled the junk. But there's a threat more serious than junk: malware — dangerous attachments sent by spammers and attackers. This episode installs ClamAV as the antivirus engine and Amavis as the bridge that scans every email.
We'll install ClamAV with updated signatures, set up Amavis as a content filter, route Postfix email through it, and close with message size limits and rate limiting.
Email is one of the most effective malware distribution vectors. Viruses, trojans, and ransomware often arrive as attachments with deceptive names. An antivirus engine on the server scans email content before it enters the mailbox — so what reaches users is already clean.
Amavis (AMaViSd) is the "door" that orchestrates: it receives email from Postfix, runs ClamAV for viruses and SpamAssassin for spam in a single flow, then returns the result to Postfix. One filter point, many engines.
Install ClamAV and its daemon components:
sudo apt install -y clamav clamav-daemonVirus signatures are updated by freshclam. Update manually once before starting the daemon:
sudo systemctl stop clamav-freshclam
sudo freshclam
sudo systemctl start clamav-freshclamCheck the database status and version:
clamscan --version
systemctl status clamav-daemon --no-pagerclamscan --version shows the engine version and database date. Test with a quick scan of the EICAR file (the standard antivirus test string):
echo 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' > /tmp/eicar.txt
clamscan /tmp/eicar.txtIf the output shows FOUND, ClamAV detects the test file — the engine works.
Install and tune Amavis:
sudo apt install -y amavisd-newThe main configuration is in /etc/amavis/conf.d/. Edit 50-user to match your organization's policy:
@bypass_virus_checks_maps = (0);
@bypass_spam_checks_maps = (0);
$sa_tag_level_deflt = 2.0;
$sa_tag2_level_deflt = 5.0;
$sa_kill_level_deflt = 6.9;The meaning: viruses are always checked (0 = don't bypass), spam starts being tagged at score 2.0, considered spam at 5.0, and killed at 6.9. Amavis calls ClamAV via the socket /var/run/clamav/clamd.ctl — make sure both speak with the same client in 50-user:
$virus_scan_engine = 'clamav';Restart Amavis and check the log:
sudo systemctl restart amavis
sudo journalctl -u amavis --since "1 minute ago"Postfix uses the content filter pattern: inbound email is directed to a special transport that hands it to Amavis, then the result is returned. Enable it in main.cf and master.cf:
sudo postconf -e 'content_filter = smtp-amavis:[127.0.0.1]:10024'
sudo postconf -e 'receive_override_options = no_address_mappings'Define the smtp-amavis transport in master.cf:
sudo postconf -M smtp-amavis/unix='smtp-amavis unix - - n - 2 smtp'
sudo postconf -P smtp-amavis/unix/smtp_data_done_timeout=1200
sudo postconf -P smtp-amavis/unix/smtp_helo_timeout=60
sudo postfix reloadThe flow becomes: inbound email → Postfix (port 25) → content_filter → Amavis (10024) → ClamAV + SpamAssassin → back to Postfix (10025) → LMTP → Maildir.
Info
Note the ports: Amavis listens on 10024 and returns results to Postfix on 10025. Neither port may be opened to the public — only inter-process on localhost. The firewall in episode 16 will ensure this.
Verify the flow by sending a test email and reading the logs:
echo "virus test" | mail -s "check" admin@example.com
sudo journalctl -u amavis --since "1 minute ago"Look for lines with Passed (clean) or Blocked (infected). If Passed CLEAN appears, the entire filter chain works.
Amavis processes messages in memory — giant messages can exhaust resources. Limit the size in Postfix:
sudo postconf -e 'message_size_limit = 25600000'
sudo postconf -e 'mailbox_size_limit = 0'
sudo postfix reloadmessage_size_limit = 25600000 caps messages at 25 MB. Amavis has its own limit; match it in 50-user:
$max_message_size = 25600000;Rate limiting protects against connection floods. Postfix has built-in anvil, which tracks connections per client:
sudo postconf -e 'anvil_rate_time_unit = 60s'
sudo postconf -e 'smtpd_client_connection_rate_limit = 20'
sudo postfix reloadanvil_rate_time_unit and smtpd_client_connection_rate_limit work together to cap 20 connections per minute per client — plenty for a normal mail server, far too fast for spammers.
Episode 15 is done. Key takeaways:
freshclam updates signatures regularly.content_filter routes all email through Amavis; ports 10024/10025 are localhost-only.message_size_limit and $max_message_size prevent giant messages.anvil limits the connection rate per client.Every email now passes through the virus and spam gate. In episode 16 we tighten the fortress: Hardening & Open Relay Protection — firewall, fail2ban, and making sure Postfix never becomes an open relay. See you in episode 16!