Setting up amavisd-new and amavisd-milter

Amavis is not a new tool, in fact AMaViS started as a shell program back in 1997. Imagine, at that time I was 12 and kept annoying people on IRC. Anyway. In this article I’ll show you how to set it up, with a milter and policy banks. I also tell you about after- and before-queue filtering with Postfix and show you how to use both.

Assumptions

I assume, that you have a fresh / plain installation of amavisd-new and amavisd-milter in Debian or Ubuntu. The version shouldn’t matter much – However, take a look at the release notes to get an idea if some variable / feature is not working. I’m using Debian Bullseye.

Because I’m not a big fan of running Postfix in a chroot environment, I removed that from my master.cf. You might want to read https://serverfault.com/a/951901. There is nothing bad with using a chroot for Postfix / the master.cf services. If you like that, do it. If you need it, do it. If you feel more secure – I won’t stop you.

Basic configuration

First of all you have to set your domain and hostname. The default configuration (I’m not sure if this is debian specific) already tries to detect it:

chomp($myhostname = `hostname --fqdn`);
chomp($mydomain = `head -n 1 /etc/mailname`);

In my case it returns mail.no-uce.de for both $myhostname as well as $mydomain. Hence I’m overriding $mydomain in /etc/amavis/conf.d/50-user:

$mydomain = 'no-uce.de';

Then you should fill @local_domains_acl as well as @mynetworks. The former gets a list of all your domains, the latter gets a list of all your IP networks. Both these settings are important to have (in a default setup). Amavis insert header fields, sends recipient notifications and so on depending on these variables. It will also make sure that amavis gets directions (inbound, outbound, etc) correct.

@mynetworks = qw( 127.0.0.0/8 84.200.7.144/28 );
@local_domains_acl = ( [ ".$mydomain", ".jeanbruenn.info" ] );

By default amavis opens port 10024 (Not sure if this is debian specific). Here’s a good place to override it or to add additional ports, which is exactly what I’m doing here:

$inet_socket_port = [10023,10024];

Now enable AV scanners you like to use (and you installed beforehand and configured correctly) in 15-av_scanners (Hint: ClamAV is a good choice at least, it’s very very memory hungry, though)

Then you need to enable virus scanning and spam scanning by adding the following to 50-user or uncommenting them in 15-content_filter_mode.

@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);

Lastly for the basic configuration (your choice if you set these or not) I do reset a few variables which I’ll selectively enable / set later (Policy Banks). Furthermore I add a few defaults:

# i had some issues getting directions right, just
# to make sure i set it to 0 globally and re-enable
# it for the specific policy bank later.
$originating = 0;

# forward method is not used for a milter; we enable
# it in the specific policy bank later.
$forward_method = undef;

# Talking with Postfix
$notify_method = 'smtp:[127.0.0.1]:10025';

# Disable quarantine
$QUARANTINEDIR = undef;  
$virus_quarantine_to = undef; 
$banned_quarantine_to = undef;
$bad_header_quarantine_to = undef; 
$spam_quarantine_to = undef;
$mailfrom_to_quarantine = ''; 

# default actions.
$final_virus_destiny = D_REJECT;
$final_banned_destiny = D_REJECT;
$final_spam_destiny = D_REJECT;
$final_bad_header_destiny = D_PASS;

# I want to be informed about viruses anyway
$virus_admin = "postmaster\@$mydomain";
# Don't send me spam reports
$spam_admin = undef;
# Don't send me info about banned
$banned_admin = undef;

While this might be all you need for a basic configuration, I advise you continue reading because the above alone might cause side effects you don’t want.

Bug? Internal vs Inbound (Enable DKIM verification!)

(1)

I noticed that something weird is happening with amavisd-new (2.11.1) and amavisd-milter (1.7.1). Whenever I send a mail from gmail to my mailserver, amavis logs “AcceptedInternal”. If I send the EICAR test it logs RejectedInternal. However, my gmail address is for sure not internal on my mailserver.

When I enable dkim verification ($dkim_verification = 1) it works as expected. For a normal mail it logs AcceptedInbound and for an EICAR test it logs RejectedInbound.

I already asked on the maillinglist about this and will write an update here was pointed to the corresponding issue at amavis’ gitlab. In the meanwhile to get directions correct I’d suggest to enable DKIM verification in amavisd-new if you have the same amavisd-new version I do.

(2)

Together with the below mail from Patrick I guess additional care must be taken if you use mynetworks together with a milter / communication through a socket and if you add 127.0.0.0/8 to mynetworks.

Contrary to Postfix, amavis does not use mynetworks to identify who is a trusted network (and who is then allowed to relay in Postfix according to permit_mynetworks), but who, from amavis’ point of view, is an internal sender. If you then send to an internal destination (local domains), it is recorded as AcceptedInternal.

Original:
Anders als in Postfix identifiziert amavis mit mynetworks nicht wer ein trusted network ist (und in Postfix dann mit permit_mynetworks relayen darf), sondern wer aus Sicht von amavis ein interner Sender ist. Wenn du dann an ein internes Ziel (local domains) sendest, wird als AcceptedInternal verbucht.

Original by Patrick Ben Koetter in response to a mail. Translated from german to english by me. Thanks P@rick!

Before-Queue vs After-Queue

The difference between before-queue and after-queue filtering is pretty simple: before queue means that you will filter BEFORE the mail is stored. That means within the SMTP session. After-queue on the other hand means your mailserver already accepted the E-Mail and will start filtering AFTER it was stored. This leads to a few interesting questions:

If your mailserver accepted the E-Mail (and notified the sender “250 OK: queued!”) are you allowed to modify the E-Mail? Are you allowed to discard (drop) it? Should you tell the recipient that you discarded it, if you did so? Should you tell the sender that you discarded it (and most likely cause backscatter by doing so)? Is it enough to tell the recipient that some mail wasn’t delivered?

In some countries[5] the legislation does not permit mail filtering to discard a mail message once it has been accepted by an MTA, so this rules out an after-queue filtering setup with discarding or quarantining of messages, but leaves a possibility of delivering (possibly tagged) messages, or rejecting them in a before-queue setup (SMTP proxy or milter).

Wikipedia. Amavis. Interface Topology. 3rd September, 2021 https://en.wikipedia.org/wiki/Amavis

The [5] references an archived version (2012-08-21) of the german Strafgesetzbuch “StGB, ยง 206 Verletzung des Post- oder Fernmeldegeheimnisses”. I’m not a lawyer so I cannot and will not answer the questions nor will I try to interpret that law.

In my humble opinion, mails should be rejected (hence telling the sender immediately that the mailserver DID NOT accept the mail) as early as possible. To be more concrete: Within the ongoing SMTP-session and NOT after the mail was queued / stored => before-queue-filtering.

Why isn’t this the default? Well. A before-queue filter might introduce some problems. A good example is time: Filtering needs to finish within the SMTP session and you cannot control the timeout of the sender. If it takes too long, the sender (more correct the sending MTA) might abort. Not to talk about resource-usage if you use heavy-filtering before-queue.

Postfix offers two ways to implement a before-queue-filter: The proxy-approach and the milter-approach. I will explain the milter-approach in this article. However, I really suggest to take a look at the following documents, which will give you an excellent overview about how it works:

So, keeping this in mind, the idea is to configure the following:

  1. External mails should go through the Milter (before-queue)
  2. Internal mails from systems in my network should go through the Milter (before-queue) as well
  3. Internal mails from clients should go through amavis as content_filter (after-queue)

The reason for (3) is simple: Because the internal / local users are known, one may safely send bounces and it can be avoided to waste the resources amavis implemented as milter uses. This split of MTA/MTA traffic, relay traffic and client traffic does not only allow to use both a before-queue and an after-queue filter, it also allows to set different defaults (REJECT vs BOUNCE, max spam score, ecetera).

Amavisd-new: Policy Banks

Policy-Banks basically allow to filter/configure mail (per message) differently based on e.g. a port. This allows exactly the split I was writing about above. Mails sent from MTA to MTA could be filtered stronger than mails which are sent from internal users. Basically this is a way to override the default / global settings without setting up multiple amavis instances.

Submission using content_filter (after-queue)

I believe using an after-queue filter on submission port is okay, because the clients/senders are known and I can inform them using NDRs. While some people believe it’s not a good thing to send non-delivery-reports to your own users I believe it might help to notify them about malicious ongoing activities with their mailaccount(s). I also require authentication and encryption and I only send non-delivery-reports to my local domains so I won’t cause backscatter.

In Postfix master.cf uncomment the submission block and add a few lines like this:

submission inet n       -       n       -       -       smtpd
  -o content_filter=amavisfeed:[127.0.0.1]:10023
  -o syslog_name=postfix/submission
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

This will use the service amavisfeed as content_filter (after queue processing) using port 10023 for incoming mail on port 587. To define the amavisfeed service in Postfix add the following to the end of the master.cf:

amavisfeed unix    -       -       n        -      2     lmtp
     -o lmtp_data_done_timeout=1200
     -o lmtp_send_xforward_command=yes
     -o disable_dns_lookups=yes
     -o max_use=20

Then a reinjection path is required, which you can set by adding the following to the end of the master.cf:

127.0.0.1:10025 inet n    -       n       -       -     smtpd
    -o content_filter=
    -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 smtpd_restriction_classes=
    -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,no_milters
    -o local_header_rewrite_clients=

The above settings are very good explained in https://www.ijs.si/software/amavisd/README.postfix.txt. Please take a look at that document if you want to learn more about it. Hint: You may add -o syslog_name=amavis/reinject to 127.0.0.1:10025 and check your logs. It helps to understand what’s going on.

For the policy banks we just add the following to our 50-user configuration file:

# Mails from our clients
$interface_policy{'10023'} = 'ORIGINATING';
$policy_bank{'ORIGINATING'} = {
  originating => 1,
  forward_method => 'smtp:[127.0.0.1]:10025',
  terminate_dsn_on_notify_success => 0,
};

This first routes port 10023 to the Policy ORIGINATING. The Policy ORIGINATING sets originating => 1 so amavis knows this is one of our clients. The forward_method tells amavis to use that to re-inject mails into Postfix. You can verify that this is working, by sending a mail from a local account to e.g. gmail and then check the log:

Aug 9 09:36:38 mail postfix/smtpd[13049]: connect from localhost[127.0.0.1]
Aug 9 09:36:38 mail postfix/smtpd[13049]: 41FFA13F95D: client=localhost[127.0.0.1]
Aug 9 09:36:38 mail postfix/cleanup[13030]: 41FFA13F95D: message-id=7fc97d3a-396c-0b65-63be-ecc754d8cca9@jeanbruenn.info
Aug 9 09:36:38 mail postfix/qmgr[9059]: 41FFA13F95D: from=himself@jeanbruenn.info, size=1812, nrcpt=1 (queue active)
Aug 9 09:36:38 mail amavis[12438]: (12438-02) Passed CLEAN {RelayedOutbound}, ORIGINATING LOCAL [91.11.190.91]:59010 [91.11.190.91] himself@jeanbruenn.info -> xxx@gmail.com, Queue-ID: 60AE113F8F9, Message-ID: 7fc97d3a-396c-0b65-63be-ecc754d8cca9@jeanbruenn.info, mail_id: qdLl7ssb94l9, Hits: 1.985, size: 847, queued_as: 41FFA13F95D, dkim_new=dkim20210807:jeanbruenn.info, 1813 ms
Aug 9 09:36:38 mail postfix/lmtp[13031]: 60AE113F8F9: to=xxx@gmail.com, relay=127.0.0.1[127.0.0.1]:10023, delay=2, delays=0.14/0/0.01/1.8, dsn=2.0.0, status=sent (250 2.0.0 from MTA(smtp:[127.0.0.1]:10025): 250 2.0.0 Ok: queued as 41FFA13F95D)
Aug 9 09:36:38 mail postfix/qmgr[9059]: 60AE113F8F9: removed
Aug 9 09:36:38 mail postfix/smtp[13038]: Trusted TLS connection established to gmail-smtp-in.l.google.com[74.125.206.26]:25: TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (P-256)
Aug 9 09:36:38 mail postfix/smtp[13038]: 41FFA13F95D: to=xxx@gmail.com, relay=gmail-smtp-in.l.google.com[74.125.206.26]:25, delay=0.4, delays=0.06/0/0.13/0.21, dsn=2.0.0, status=sent (250 2.0.0 OK 1628494598 r6si16905207wru.571 - gsmtp)
Aug 9 09:36:38 mail postfix/qmgr[9059]: 41FFA13F95D: removed

Looks fine ๐Ÿ™‚

MTA to MTA using amavisd-milter (before-queue)

For the MTA to MTA traffic (incoming mails on port 25) I will use a before-queue / pre-queue milter to talk with amavis. Amavisd-new in Debian has the following snippet in 25-amavis_helpers:

$interface_policy{'SOCK'} = 'AM.PDP-SOCK';
$policy_bank{'AM.PDP-SOCK'} = {
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

Since this socket gets mails from amavisd-milter I set (just to make sure) originating = 0 here and store it in 50-user like this:

# milter traffic (inbound)
$interface_policy{'SOCK'} = 'AM.PDP-SOCK'; 
$policy_bank{'AM.PDP-SOCK'} = {
  originating => 0,
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

I will use amavisd-milter through a unix socket; you may use ports which are simplier to set up especially if you’re using chroot. Permissions are somewhat tricky. I edit /etc/default/amavisd-milter to contain (simply uncomment these):

MILTERSOCKET=/var/spool/postfix/amavis/amavis.sock
MILTERSOCKETOWNER="postfix:postfix"
MILTERSOCKETMODE="0660"

Postfix side you just have to add -o smtpd_milters= to the specific service in your master.cf. If you do use Postscreen like I do, the right line would look like this:

smtpd     pass  -       -       n       -       -       smtpd
  -o smtpd_milters=${milter_amavis}

Lastly I define milter_amavis within my main.cf together with milter_connect_macros as per amavisd-milter documentation:

#
# milter configuration
#
milter_amavis = unix:/var/spool/postfix/amavis/amavis.sock
milter_connect_macros = "j {client_name} {daemon_name} v _"

The default is “j {daemon_name} {daemon_addr} v _”. You can verify that this works by checking the log again:

Aug  9 12:05:16 mail postfix/postscreen[13705]: CONNECT from [209.85.218.44]:33540 to [84.200.7.154]:25
Aug  9 12:05:22 mail postfix/postscreen[13705]: PASS OLD [209.85.218.44]:33540
Aug  9 12:05:23 mail postfix/smtpd[13707]: connect from mail-ej1-f44.google.com[209.85.218.44]
Aug  9 12:05:23 mail postfix/smtpd[13707]: 620DD13FB6D: client=mail-ej1-f44.google.com[209.85.218.44]
Aug  9 12:05:23 mail postfix/cleanup[13711]: 620DD13FB6D: message-id=<CAHJLFyeqfxGBkTadP-jNq4tJgAa+M2ZYxAK17WaFv6fyTjwo6w@mail.gmail.com>
Aug  9 12:05:27 mail amavis[13672]: (13672-01) Passed CLEAN {AcceptedInbound}, AM.PDP-SOCK [209.85.218.44] [209.85.218.44] <xxx@gmail.com> -> <himself@jeanbruenn.info>, Queue-ID: 620DD13FB6D, Message-ID: <CAHJLFyeqfxGBkTadP-jNq4tJgAa+M2ZYxAK17WaFv6fyTjwo6w@mail.gmail.com>, mail_id: l-lKDmN1sSk1, Hits: 0.787, size: 2521, dkim_sd=20161025:gmail.com, 3700 ms
Aug  9 12:05:27 mail postfix/qmgr[9059]: 620DD13FB6D: from=<xxx@gmail.com>, size=2753, nrcpt=1 (queue active)
Aug  9 12:05:27 mail postfix/smtpd[13707]: disconnect from mail-ej1-f44.google.com[209.85.218.44] ehlo=2 starttls=1 mail=1 rcpt=1 bdat=1 quit=1 commands=7
Aug  9 12:05:27 mail dovecot: lmtp(13716): Connect from local
Aug  9 12:05:27 mail dovecot: lmtp(himself@jeanbruenn.info)<13716><W2BqD+f9EGGUNQAA8qPOQg>: msgid=<CAHJLFyeqfxGBkTadP-jNq4tJgAa+M2ZYxAK17WaFv6fyTjwo6w@mail.gmail.com>: saved mail to INBOX
Aug  9 12:05:27 mail postfix/lmtp[13715]: 620DD13FB6D: to=<himself@jeanbruenn.info>, relay=mail.no-uce.de[private/dovecot-lmtp], delay=4.3, delays=3.9/0.01/0.01/0.38, dsn=2.0.0, status=sent (250 2.0.0 <himself@jeanbruenn.info> W2BqD+f9EGGUNQAA8qPOQg Saved)
Aug  9 12:05:27 mail postfix/qmgr[9059]: 620DD13FB6D: removed
Aug  9 12:05:27 mail dovecot: lmtp(13716): Disconnect from local: Client has quit the connection (state=READY)

Looks fine, too.

Internal Systems using amavisd-milter (before-queue)

Basically this part is about playing relay / smarthost for my internal systems. Since all Milter stuff goes through the socket and you cannot filter multiple sockets with policy banks from what I’ve read one needs to use something else to differentiate between external systems and internal systems coming in through SOCK. Let me re-post the configuration for that policy bank:

# milter traffic (inbound)
$interface_policy{'SOCK'} = 'AM.PDP-SOCK';
$policy_bank{'AM.PDP-SOCK'} = {
  originating => 0, # just to make sure
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

There’s a configuration variable called $client_ipaddr_policy. Sounds exactly like what I need. Doesn’t it?

@client_ipaddr_policy = (
    [qw( !84.200.7.154/32 84.200.7.144/28 )] => 'INT-MTA'
);

With the above variable I tell amavis to route mails from 84.200.7.144/28 (my IP network) except for 84.200.7.154 (my mailserver) to a policy bank called INT-MTA.

# internal systems traffic (inbound)
$policy_bank{'INT-MTA'} = {
  originating => 1,
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

Note, that I set originating => 1 here. You may verify that this works for example by setting final_virus_destiny => D_PASS followed by sending the EICAR test through that system. You’ll see something like this:

Aug  9 19:46:46 mail amavis[15560]: (15560-01) Passed INFECTED (Eicar-Signature) {AcceptedOutbound}, AM.PDP-SOCK/INT-MTA LOCAL [84.200.7.149] [84.200.7.149] <xxx@jeanbruenn.info> -> <xxx@gmail.com>, Queue-ID: 7A15A13F95D, Message-ID: <20210809174644.72111807C8@jeanbruenn.info>, mail_id: 4fM5YJnrUQAK, Hits: 5.262, size: 621, 1933 ms

Any mail from within my ip networks which is not the mailserver itself, will go through the INT-MTA policy.

Custom Filtering per Policy Bank

With all the above configuration I did set up three Policy Banks. Let me re-post those together with some of the defaults I did set / variables I did reset at the start of this article.

# default actions.
$final_virus_destiny = D_REJECT;
$final_banned_destiny = D_REJECT;
$final_spam_destiny = D_REJECT;
$final_bad_header_destiny = D_PASS;

# I want to be informed about viruses anyway
$virus_admin = "postmaster\@$mydomain";
# Don't send me spam reports
$spam_admin = undef;
# Don't send me info about banned
$banned_admin = undef;

# mails from our clients / local users
$policy_bank{'ORIGINATING'} = {
  originating => 1, # just to make sure
  forward_method => 'smtp:[127.0.0.1]:10025',
  terminate_dsn_on_notify_success => 0,
};

# milter traffic: inbound from external mtas
$policy_bank{'AM.PDP-SOCK'} = {
  originating => 0, # just to make sure
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

# milter traffic: inbound from internal / my own mtas
$policy_bank{'INT-MTA'} = {
  originating => 1,
  protocol => 'AM.PDP',
  auth_required_release => 0,
};

I will fine-tune these Policy-Banks now. If someone external sends a virus to me, I don’t need a notification. I will see that in my logs. So I can disable that for AM.PDP-SOCK. If one of my internal systems or a client sents out viruses, I want a notification, hence I keep the default as is. Furthermore, if my internal systems try to send spam or banned stuff I really want to know that. Hence I enable spam_admin and banned_admin in INT-MTA.

Due to the After-Queue setup for local clients, those will get a bounce in case they try to send out viruses / spam. If I used a milter for my local users as well, i would probably set D_BOUNCE in ORIGINATING. You will still need to make sure to NOT cause backscatter (if e.g. a client fakes addresses).

DKIM signing should be enabled for my clients (ORIGINATING) as well as my internal systems sending out mails (INT-MTA). DKIM signing should be disabled for external mail getting in. Hence I add $enable_dkim_signing = 1; and disable it for AM.PDP-SOCK. How to setup DKIM verification and signing with amavisd-new can be read in my last blog article.

In addition, one may set different scores for e.g. $sa_kill_level_deflt. The default score is 6.31 in my Debian installation. You might set it lower, say to 5 for incoming mails (AM.PDP-SOCK) and keep it at 6.31 for internal mailservers using your system (INT-MTA) while you increase it to 8 (to be a bit more user-friendly) for your clients (ORIGINATING). These are example numbers – I suggest to leave it at the default and change the scores slowly according to the incoming and outgoing spam.

# mails from our clients / local users
$policy_bank{'ORIGINATING'} = {
  originating => 1, # just to make sure
  forward_method => 'smtp:[127.0.0.1]:10025',
  terminate_dsn_on_notify_success => 0,
};

# milter traffic: inbound from external mtas
$policy_bank{'AM.PDP-SOCK'} = {
  originating => 0, # just to make sure
  protocol => 'AM.PDP',
  auth_required_release => 0,
  virus_admin_maps => undef,
  enable_dkim_signing => 0,
};

# milter traffic: inbound from internal / my own mtas
$policy_bank{'INT-MTA'} = {
  originating => 1,
  protocol => 'AM.PDP',
  auth_required_release => 0,
  spam_admin_maps => ["postmaster\@$mydomain"],
  banned_admin_maps => ["postmaster\@$mydomain"],
};

I guess you get the idea, how Policy Banks are useful?

Full configuration so far

The relevant configuration of this article goes here. Hopefully this will make it easier for you, to try it yourself.

/etc/postfix/main.cf

#
# milter configuration
#
milter_amavis = unix:/var/spool/postfix/amavis/amavis.sock
milter_connect_macros = "j {client_name} {daemon_name} v _"

/etc/postfix/master.cf

smtp      inet  n       -       n       -       1       postscreen
smtpd     pass  -       -       n       -       -       smtpd
  -o smtpd_milters=${milter_amavis}

[..]

submission inet n       -       n       -       -       smtpd
  -o content_filter=amavisfeed:[127.0.0.1]:10023
  -o syslog_name=postfix/submission
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject

[..]

amavisfeed unix    -       -       n        -      2     lmtp
     -o lmtp_data_done_timeout=1200
     -o lmtp_send_xforward_command=yes
     -o disable_dns_lookups=yes
     -o max_use=20

127.0.0.1:10025 inet n    -       n       -       -     smtpd
    -o content_filter=
    -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 smtpd_restriction_classes=
    -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,no_milters
    -o local_header_rewrite_clients=

/etc/default/amavisd-milter

MILTERSOCKET=/var/spool/postfix/amavis/amavis.sock
AMAVISSOCKET=/var/lib/amavis/amavisd.sock
MILTERSOCKETOWNER="postfix:postfix"
MILTERSOCKETMODE="0660"

/etc/amavis/conf.d/50-user

#
# Basic settings
# ips, networks, ports, sockets, domains belong here
#

$mydomain = 'no-uce.de';
@mynetworks = qw( 127.0.0.0/8 84.200.7.144/28 );
$inet_socket_port = [10023,10024];
@local_domains_maps = ( [ ".$mydomain", ".jeanbruenn.info" ] );

#
# Spam / Virus checks
# enabled here
#

@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);

#
# Reset and defaults
# resetting a few variables here, setting some defaults
#

# i had some issues getting directions right, just to make
# sure i set it to 0 globally and re-enable it for the
# specific policy bank later.
$originating = 0;

# forward method is not used for a milter; we enable it in
# the specific policy bank later.
$forward_method = undef;

# Talking with Postfix
$notify_method = 'smtp:[127.0.0.1]:10025';

# Disable quarantine
$QUARANTINEDIR = undef;  
$virus_quarantine_to = undef; 
$banned_quarantine_to = undef;
$bad_header_quarantine_to = undef; 
$spam_quarantine_to = undef;
$mailfrom_to_quarantine = ''; 

# default actions.
$final_virus_destiny = D_REJECT;
$final_banned_destiny = D_REJECT;
$final_spam_destiny = D_REJECT;
$final_bad_header_destiny = D_PASS;

#
# DKIM
#

$enable_dkim_signing = 1;
$enable_dkim_verification = 1;
# See: https://blog.jeanbruenn.info/2021/08/07/amavisd-new-and-dkim/
# for remaining DKIM configuration here.

#
# Policy Banks
#

# mails from our clients
$interface_policy{'10023'} = 'ORIGINATING';
$policy_bank{'ORIGINATING'} = {
  originating => 1,
  forward_method => 'smtp:[127.0.0.1]:10025',
  terminate_dsn_on_notify_success => 0,
};

# milter traffic (inbound)
$interface_policy{'SOCK'} = 'AM.PDP-SOCK';
$policy_bank{'AM.PDP-SOCK'} = {
  originating => 0,
  protocol => 'AM.PDP',
  auth_required_release => 0,
  virus_admin_maps => undef,
  enable_dkim_signing => 0,
};

# traffic from internal mtas
@client_ipaddr_policy = (
    [qw( !84.200.7.154/32 84.200.7.144/28 )] => 'INT-MTA'
);
$policy_bank{'INT-MTA'} = {
  originating => 1,
  protocol => 'AM.PDP',
  auth_required_release => 0,
  spam_admin_maps => ["postmaster\@$mydomain"],
  banned_admin_maps => ["postmaster\@$mydomain"],
};

If you’re new to this, please do me a favour and take a look at the README.postfix.txt. It’s very well written and contains additional stuff like an optimization for the cleanup service. It also explains amavis/postfix integration a lot more in-depth than I did here.

See also

2 thoughts on “Setting up amavisd-new and amavisd-milter”

  1. Hello,

    Trying to follow your guide to setup amavisd-milter and it all seems to work however, opendmarc milter seems to get ignored by postfix. I see e-mails coming in, spf and dkim pass but opendmarc is not involved in the smtp session.

    1. Hi,

      I haven’t used that milter, yet. I’ll have a look at when I have a bit time, though. Maybe you can send me your configuration so that I can have a look?

      Jean

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.