Setup Email Server From Scratch On FreeBSD #2 - 12 Amavis Clam AntiVirus

11 SpamAssassin <- Intro -> 13 Postscreen

We believe in data independence, and support others who want data independence.
This tutorial is complete 2025-08-14 except there is no page for setting up postscreen.

This is version 2 and everthing works.

#####################################################
# Installing Antivirus Amavis and ClamAV on FreeBSD #
#####################################################

Install Amavisd and Clam Anti Virus

pkg install amavisd-new clamav
pkg install p5-Mail-SPF p5-Net-DNS p5-Digest-SHA1 p5-IO-Stringy py311-pyzor razor-agents
pkg install cabextract gcpio rpm2cpio file lhasa nomarch pax-utils bzip2 gzip rar
pkg install arj unrar 7-zip unzip zip lrzip lzip liblz4 lzop unrar file

sysrc amavisd_enable="YES"
sysrc amavisd_pidfile="/var/amavis/amavisd.pid"
sysrc amavisd_ram="512m"
sysrc amavisd_ram="1g"

Check Amavis Version

amavisd -V
amavis-2.13.1 (20240304)

Create a 1G memory mapped tmp filesystem for amavis, amavis on FreeBSD creates user and group vscan

grep vscan /etc/group /etc/passwd
/etc/group:vscan:*:110:
/etc/passwd:vscan:*:110:110:Scanning Virus Account:/var/maiad:/bin/sh

mkdir -p /var/amavis/tmp
chown -R vscan:vscan /var/amavis/tmp

nano /etc/fstab
echo 'tmpfs /var/amavis/tmp tmpfs rw,noexec,nosuid,size=1G,mode=1750,uid=110,gid=110 0 0' >> /etc/fstab

mount /var/amavis/tmp

Configure Amavis

SpamAssassin headers can get removed by amavis if run separately so I enable spamassassin in amavis and disable it it postfix. This will retain the X-Spam-Score and have X-Virus-Scanned, otherwise if run SpamAssassin from postfix amavis will remove the X-Virus-Scanned headers.

Check how many cpu are available.

sysctl hw.ncpu
hw.ncpu: 8

When trying to restart amavis fails to start and logs show that port 10024 is already in use. Some child processes may not be killed and seems to be caused if max_servers is set too high.

If amavis doesn't restart use ...

pkill -U vscan
service amavisd start

Please note that for some reason amavis has problems opening berkley db file and
will crash unless we disable with $enable_db = 0; in /usr/local/etc/amavis.conf.

nano /usr/local/etc/amavisd.conf
--- add or change ignore the ... ---
@bypass_virus_checks_maps = ( \%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re); @bypass_spam_checks_maps = ( \%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re); # disable db or else will crash $enable_db = 0; $max_servers = 4; # num of pre-forked children (2..30 is common), -m $mydomain = 'okbsd.com'; # a convenient default for other settings $X_HEADER_LINE = "by smtp.$mydomain"; ... $enable_dkim_verification = 0; @local_domains_maps = ( [".$mydomain", ".coragarden.com"] ); # list of all local domains # $inet_socket_port = 10024; # listen on this local TCP port(s) $inet_socket_port = [10024,10026]; # listen on multiple TCP ports $sa_tag_level_deflt = -999.0; # add spam info headers if at, or above that level virus_admin_maps => ["postmaster\@$mydomain"], spam_admin_maps => ["postmaster\@$mydomain"], #forward_method => 'smtp:[127.0.0.1]:10027',

Confirm it is running

service amavisd start
service amavisd status
amavisd is running as pid 8078.

sockstat -l | grep 10024
vscan perl 8083 8 tcp4 127.0.0.1:10024 *:*
vscan perl 8083 9 tcp6 ::1:10024 *:*

Add Amavis to Postfix main.cf / spamass will be run my amavis so remove from smtpd_milters

nano /usr/local/etc/postfix/main.cf
--- add to bottom of file ---
# amavisd smtpd_proxy_options = speed_adjust content_filter = smtp-amavis:[127.0.0.1]:10024

nano /usr/local/etc/postfix/master.cf
--- add to end of file, -- cores - v ---
smtp-amavis unix - - n - 4 smtp -o syslog_name=postfix/amavis -o smtp_data_done_timeout=1200 -o smtp_send_xforward_command=yes -o smtp_dns_support_level=disabled -o max_use=20 -o smtp_tls_security_level=none 127.0.0.1:10025 inet n - n - - smtpd -o syslog_name=postfix/10025 -o content_filter= -o mynetworks_style=host -o mynetworks=127.0.0.0/8 -o local_recipient_maps= -o relay_recipient_maps= -o strict_rfc821_envelopes=yes -o smtp_tls_security_level=none -o smtpd_tls_security_level=none -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_end_of_data_restrictions= -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,no_address_mappings

service postfix restart

Configure FreshClam

nano /usr/local/etc/freshclam.conf

NotifyClamd /usr/local/etc/clamd.conf

nano /usr/local/etc/freshclam.conf
NotifyClamd /usr/local/etc/clamd.conf

sysrc clamav_clamd_enable="YES"
service clamav_clamd start
service clamav_clamd status
clamav_clamd is running as pid 8785.

Update FreshClam Database

freshclam
sysrc clamav_freshclam_enable="YES"
service clamav_freshclam start
service clamav_freshclam status
clamav_freshclam is running as pid 8803.

pw groupmod vscan -m clamav

adduser clamav amavis

service clamav_clamd restart
service clamav_clamd status

service amavisd restart
service amavisd status

Use a dedicated port for submissions, max_servers should be the number of cores on the server, I have 4 virtual cores.

nano /usr/local/etc/amavisd.conf
$max_servers = 4; $enable_dkim_verification = 0; $sa_tag_level_deflt = -999.0; $X_HEADER_LINE = "by smtp.$mydomain"; # Fix amavis to scan other virtual domains @local_domains_maps = ( [".$mydomain"], ".coragarden.com" ); # list of all local domains $inet_socket_port = [10024, 10026]; $interface_policy{'10026'} = 'ORIGINATING'; $policy_bank{'ORIGINATING'} = { # mail supposedly originating from our users originating => 1, # declare that mail was submitted by our smtp client allow_disclaimers => 1, # enables disclaimer insertion if available # notify administrator of locally originating malware virus_admin_maps => ["postmaster\@okbsd.com"], spam_admin_maps => ["postmaster\@okbsd.com"], warnbadhsender => 1, # forward to a smtpd service providing DKIM signing service # forward_method => 'smtp:[127.0.0.1]:10027', # force MTA conversion to 7-bit (e.g. before DKIM signing) smtpd_discard_ehlo_keywords => ['8BITMIME'], bypass_banned_checks_maps => [1], # allow sending any file names and types terminate_dsn_on_notify_success => 0, # don't remove NOTIFY=SUCCESS option };

Add the following 2 line to /usr/local/etc/postfix/master.cf submission and smtps sections Add the no_milters or else outgoing gets DKIM signed twice!

-o content_filter=smtp-amavis:[127.0.0.1]:10026
-o receive_override_options=no_milters

nano /usr/local/etc/postfix/master.cf
--- add the content_filter line in 2 places like this ---
submission inet n - y - - smtpd -o syslog_name=postfix/submission -o smtpd_tls_security_level=encrypt -o smtpd_tls_wrappermode=no -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o content_filter=smtp-amavis:[127.0.0.1]:10026 -o receive_override_options=no_milters smtps inet n - y - - smtpd -o syslog_name=postfix/smtps -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_recipient_restrictions=permit_mynetworks,permit_sasl_authenticated,reject -o smtpd_sasl_type=dovecot -o smtpd_sasl_path=private/auth -o content_filter=smtp-amavis:[127.0.0.1]:10026 -o receive_override_options=no_milters

service postfix restart

Remove SpamAssasin socket in smtpd_milters, it is running in amavis

nano /usr/local/etc/postfix/main.cf
#smtpd_milters = unix:opendkim/opendkim.sock,unix:opendmarc/opendmarc.sock,unix:spamass/spamass.sock smtpd_milters = unix:opendkim/opendkim.sock,unix:opendmarc/opendmarc.sock

service amavisd restart
service postfix restart

Adjust your style of spf header if desired.

nano /usr/local/etc/python-policyd-spf/policyd-spf.conf
# default = Recieved-SPF # Header_Type = Received-SPF #Header_Type = SPF # AR requires Authserv_Id #Authserv_Id = smtp.okbsd.com #Header_Type = AR

service pyspf-milter restart

Check your incoming mail headers by sending a test mail to jack@okbsd.com. You may need to adjust to following to get X-Spam-Score to show up.

$remove_existing_spam_headers = 0;
$sa_tag_level_deflt = -999.0;

nano /usr/local/etc/postfix/header_checks
/To:.*<>/ REJECT "5.7.1 Rejected, invalid To: header." /From:.*<>/ REJECT "5.7.1 Rejected, invalid From: header." /free mortgage quote/ REJECT /repair your credit/ REJECT /^Received:/ IGNORE

postmap /usr/local/etc/postfix/header_checks
service postfix restart

service amavisd restart
service sa-spamd restart
service spamass-milter restart

service amavisd status

If amavisd is not running use pkill.

pkill -U vscan
service amavisd start

################################################
# Next Up Reducing Server Load With Postscreen #
################################################

11 SpamAssassin <- Intro -> 13 Postscreen