Postfix - amavisd-new without localhost or a new mail server
- Tutorial
There are lots of instructions on how to raise the mail server on the postfix bundle - amavisd-new - dovecot. And the vast majority of them repeat each other almost literally, including errors and inaccuracies.
It seems boring to me to thoughtlessly press buttons, so I decided to optimize the standard configuration: what if the postfix and amavisd-new interaction is not built via localhost, but on a unix socket?
As it turned out, everything is not so simple, but I did it! Instructions and patch under the cut.
Honestly, I don’t like interaction through localhost within the same machine at all. If it is required to organize data exchange between two applications, then it is much more correct, safer and less resource-intensive to do this through a unix socket on the file system. Moreover, this way it is possible to organize protection (by means of rights on the file system) even where it is absent at the application or protocol level.
So, the path of the mail in the bundle under discussion looks like this:

It turns out that we need to organize two docking: when passing to filtering and returning to the MTA. Since the socket is created by the listener, in the first case it will be amavisd, and in the second - postfix.
Let's start with the second, because it is easier and better described. In order for postfix to create a listening socket, you just need to specify unix in the second column of master.cf (type column), and not inet. In this case, the first column defines the path and file name of the socket.
Since postfix processes work in chroot (this can be disabled for a specific process, but not worth it), the folder must be created inside the postfix home directory: / var / spool / postfix. It will contain both sockets:
Well, the postfix configuration:
Specific options depend on your settings, this is my option.
There are two troubles:
So, we substitute crutches:
Not very nice, but not destructive for the standard folder structure of the package.
Restart postfix and make sure the socket file appears in the amavis folder, and the pid file in pid / unix.amavis. Unfortunately, the rights to the socket - 666, but the rights to the folder that you created earlier will protect the file from the extra eyes.
You can check the work with the command:
Great, coped with it. Now to amavisd.
First, we configure the return path of the mail through the unix socket, owned by postfix. It works out of the box:
Well, now the most difficult thing is to set up sockets in amavisd. The solution can be found on the Internet , but there it is proposed to use a single socket, specified by the $ unix_socketname parameter. I also wanted my own amavisd-new protocol (AM.PDP) and mail reception to occur through sockets.
The default configuration file contains the @listen_sockets directive, but there is no description for it. But it is in release notes , even with examples! True, there is only one socket, but what's stopping to try?
OK, and how to set the protocol for the socket (which is indicated in the policy bank)? In all examples, they write just SOCK. By analogy with inet-sockets (where you can specify the host port), I suggested that you need to specify the full path to the socket file. Here's what happened:
I restart, check - really, both sockets were created! Victory? Not really, when you try to connect to a socket, nothing happens, and an error is written to the log that the protocol is not defined for it. It turns out that the policy bank does not apply to them.
How so? I had to go to the code.
This campaign brought two news - as usual, good and bad. The good thing is that the assumption about% interface_policy was true:
The bad one is that $ unix_socket_path comes into this function empty. It is completed as follows:
And both properties are empty.
Examining the documentation suggested this option:
And it worked! Ready .patch can be said here .
Stayed the final touch. Since amavisd creates its own socket with rights only to itself, and we denied access for the rest (which is true), we need to add postfix to the amavis group so that it can write to the socket:
Is done.
PS: I sent the patch and description of the problem to Mark Martinec by mail as I did not find any mention of bugtracker on the site . I still haven’t received an answer, but I don’t expect much of it - the project looks abandoned (the last release was more than two years ago).
It seems boring to me to thoughtlessly press buttons, so I decided to optimize the standard configuration: what if the postfix and amavisd-new interaction is not built via localhost, but on a unix socket?
As it turned out, everything is not so simple, but I did it! Instructions and patch under the cut.
Honestly, I don’t like interaction through localhost within the same machine at all. If it is required to organize data exchange between two applications, then it is much more correct, safer and less resource-intensive to do this through a unix socket on the file system. Moreover, this way it is possible to organize protection (by means of rights on the file system) even where it is absent at the application or protocol level.
So, the path of the mail in the bundle under discussion looks like this:

It turns out that we need to organize two docking: when passing to filtering and returning to the MTA. Since the socket is created by the listener, in the first case it will be amavisd, and in the second - postfix.
Let's start with the second, because it is easier and better described. In order for postfix to create a listening socket, you just need to specify unix in the second column of master.cf (type column), and not inet. In this case, the first column defines the path and file name of the socket.
Since postfix processes work in chroot (this can be disabled for a specific process, but not worth it), the folder must be created inside the postfix home directory: / var / spool / postfix. It will contain both sockets:
mkdir /var/spool/postfix/amavis
chown amavis:postfix /var/spool/postfix/amavis
chmod 770 /var/spool/postfix/amavis
Well, the postfix configuration:
amavis/postfix-in unix y - y - - smtpd
-o smtpd_client_restrictions=$local_clients_only
-o smtpd_helo_restrictions=
-o smtpd_sender_restrictions=
-o smtpd_recipient_restrictions=
-o smtpd_milters=
Specific options depend on your settings, this is my option.
There are two troubles:
- The path will be relative to / var / spool / postfix / private, on which very strict rights are attached.
- Not sure about all distros like this, but in Ubuntu for sure. It is better not to touch the folder rights (there are sockets of all postfix services), it is better to simply create symlink.
- In addition to the socket, postfix creates a pid file for a process, the name of which is generated automatically using the $ type. $ Name mask. Where type will be equal to unix, and name is taken from the first column of master.cf. It turns out unux.amavis / postfix-in ie file in a subfolder. He himself will not create it and will fall with an error.
So, we substitute crutches:
cd /var/spool/postfix/private
ln -s ../amavis .
mkdir /var/spool/postfix/pid/unix.amavis
Not very nice, but not destructive for the standard folder structure of the package.
Restart postfix and make sure the socket file appears in the amavis folder, and the pid file in pid / unix.amavis. Unfortunately, the rights to the socket - 666, but the rights to the folder that you created earlier will protect the file from the extra eyes.
You can check the work with the command:
netcat -U /var/spool/postfix/amavis/postfix-in
220 mail.example.ru ESMTP Postfix
Great, coped with it. Now to amavisd.
First, we configure the return path of the mail through the unix socket, owned by postfix. It works out of the box:
$forward_method = 'smtp:/var/spool/postfix/amavis/postfix-in';
$notify_method = \$forward_method;
Well, now the most difficult thing is to set up sockets in amavisd. The solution can be found on the Internet , but there it is proposed to use a single socket, specified by the $ unix_socketname parameter. I also wanted my own amavisd-new protocol (AM.PDP) and mail reception to occur through sockets.
The default configuration file contains the @listen_sockets directive, but there is no description for it. But it is in release notes , even with examples! True, there is only one socket, but what's stopping to try?
OK, and how to set the protocol for the socket (which is indicated in the policy bank)? In all examples, they write just SOCK. By analogy with inet-sockets (where you can specify the host port), I suggested that you need to specify the full path to the socket file. Here's what happened:
$unix_socketname = undef;
$inet_socket_bind = undef;
$inet_socket_port = undef;
@listen_sockets = ('/var/lib/amavis/amavisd.sock', '/var/spool/postfix/amavis/amavis-in.sock');
$unix_socket_mode = 0660;
%interface_policy = (
'/var/lib/amavis/amavisd.sock' => 'AM.PDP-SOCK',
'/var/spool/postfix/amavis/amavis-in.sock' => 'LMTP-SOCK'
);
$policy_bank{'LMTP-SOCK'} = {
protocol =>'LMTP'
};
$policy_bank{'AM.PDP-SOCK'} = {
protocol =>'AM.PDP',
auth_required_release =>0, # don't require secret-id for release
};
I restart, check - really, both sockets were created! Victory? Not really, when you try to connect to a socket, nothing happens, and an error is written to the log that the protocol is not defined for it. It turns out that the policy bank does not apply to them.
How so? I had to go to the code.
This campaign brought two news - as usual, good and bad. The good thing is that the assumption about% interface_policy was true:
# load policy banks according to my socket (destination),# then check for allowed access from the peer (client/source)#subaccess_is_allowed($;$$$$) {
my($unix_socket_path, $src_addr, $src_port, $dst_addr, $dst_port) = @_;
my(@bank_names);
if (defined $unix_socket_path) {
push(@bank_names, $interface_policy{"SOCK"});
push(@bank_names, $interface_policy{$unix_socket_path});
} elsif (defined $dst_addr && defined $dst_port) {
$dst_addr = '['.lc($dst_addr).']'if $dst_addr =~ /:[0-9a-f]*:/i; # IPv6?push(@bank_names, $interface_policy{$dst_port});
push(@bank_names, $interface_policy{"$dst_addr:$dst_port"});
}
load_policy_bank($_) for @bank_names;
The bad one is that $ unix_socket_path comes into this function empty. It is completed as follows:
my $is_ux = $sock && $sock->UNIVERSAL::can('NS_proto') &&
$sock->NS_proto eq 'UNIX';
And both properties are empty.
Examining the documentation suggested this option:
my $unix_socket_path = $sock->hostpath();
And it worked! Ready .patch can be said here .
Stayed the final touch. Since amavisd creates its own socket with rights only to itself, and we denied access for the rest (which is true), we need to add postfix to the amavis group so that it can write to the socket:
gpasswd -a postfix amavis
Is done.
PS: I sent the patch and description of the problem to Mark Martinec by mail as I did not find any mention of bugtracker on the site . I still haven’t received an answer, but I don’t expect much of it - the project looks abandoned (the last release was more than two years ago).