Topic: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Preamble
I take no credit for the immense hardwork done by several people; this howto is built extensively upon their howtos which can (at the time of this writing) be found here:
http://kirb.insanegenius.net/postfix.html
http://www.ezunix.org/modules.php?op=mo … amp;page=1
http://ntecs.de/blog/Tech/Sysadm

I thank them greatly for what they have done, and for making this possible. I am simply using their howtos to create a complete as possible NetBSD version. A *LOT* of this howto will be a direct copy of their work - I am NOT taking credit for those bits!

Although this howto is predominantly aimed at NetBSD, only the location of files and the process used to start each of the daemons should be NetBSD specific - the rest should be generic in nature.


Purpose
The purpose behind this document is to create a mail server solution that includes postfix as the MTA,  Courier-IMAP as the IMAP/POP server and MySQL for all potentially dynamic configuration.  The goal is to have completely virtual users and domains.  jimbob@domain1.com != jimbob@domain2.com. This means creating a separate namespace for each domain.


Theory
The first thought is how to create that separate namespace.  Using Postfix virtual delivery agent, the problem is relatively trivialized.  You just deliver mail to the appropriate maildir for the appropriate address.  Postfix really doesn't care that you have multiple people named jimbob because it looks at the entire address.  Separate namespace for Courier gets a little trickier.  You could ask all of your users to login as user@domain.com or user.domain.com or even user.somenumber and this is the best solution we've come up with so far.  The problem with user@domain.com logins is that drain bamaged MUA's may interpret that to mean one's complete address is "user@domain.com@domain.com".  Needless to say, this is bad.  Of course, the same faulty MUA's might cause problems with thinking "user.domain.com@domain.com" is the address...


Installation
I'm a *HUGE* fan of pkgsrc, so recommend its use where possible. We first need to setup our needed options which is done by adding them to /etc/mk.conf:

# echo "PKG_OPTIONS.courier-authlib=mysql" >> /etc/mk.conf
# echo "PKG_OPTIONS.postfix=mysql" >> /etc/mk.conf

Now, to install the programs needed, simply:

# cd /usr/pkgsrc/databases/mysql4-server
# make install clean clean-depends
# cd /usr/pkgsrc/security/courier-authlib
# make install clean clean-depends
# cd /usr/pkgsrc/mail/courier-imap
# make install clean clean-depends
# cd /usr/pkgsrc/mail/postfix
# make install clean clean-depends

this will install all that we need to complete this HowTo.


Setup  and Start MySQL
You can start the MySQL server doing the following steps:

# echo "mysqld=yes" >> /etc/rc.conf
# cp /usr/pkg/share/examples/rc.d/mysqld /etc/rc.d
# /etc/rc.d/mysqld start

You now need to set the root password to secure your db, change new-password to suit your memory:

/usr/pkg/bin/mysqladmin -u root -p password 'new-password'
/usr/pkg/bin/mysqladmin -h `hostname` -u root -p password 'new-password'

The Database
Here is the quick description of the database:

mysql> show tables;
+------------------+
| Tables_in_maildb |
+------------------+
| transport        |
| users            |
| virtual          |
+------------------+
3 rows in set (0.00 sec)

mysql> describe transport;
+-----------+--------------+------+-----+---------+-------+
| Field     | Type         | Null | Key | Default | Extra |
+-----------+--------------+------+-----+---------+-------+
| domain    | varchar(128) |      | PRI |         |       |
| transport | varchar(128) |      | MUL |         |       |
+-----------+--------------+------+-----+---------+-------+
2 rows in set (0.00 sec)

mysql> describe virtual;
+---------+--------------+------+-----+---------+-------+
| Field   | Type         | Null | Key | Default | Extra |
+---------+--------------+------+-----+---------+-------+
| address | varchar(255) |      | PRI |         |       |
| goto    | varchar(255) |      |     |         |       |
+---------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

mysql> describe users;
+---------+----------------------+------+-----+----------------------------+-------+
| Field   | Type                 | Null | Key | Default                    | Extra |
+---------+----------------------+------+-----+----------------------------+-------+
| id      | varchar(128)         |      | PRI |                            |       |
| address | varchar(128)         |      | UNI |                            |       |
| crypt   | varchar(128)         |      |     |                            |       |
| clear   | varchar(128)         |      |     |                            |       |
| name    | varchar(128)         |      |     |                            |       |
| uid     | smallint(5) unsigned |      |     | 0                          |       |
| gid     | smallint(5) unsigned |      |     | 0                          |       |
| home    | varchar(128)         |      |     | /var/spool/postfix/virtual |       |
| domain  | varchar(128)         |      |     |                            |       |
| maildir | varchar(255)         |      |     |                            |       |
| imapok  | tinyint(3) unsigned  |      |     | 1                          |       |
| bool1   | tinyint(3) unsigned  |      |     | 1                          |       |
| bool2   | tinyint(3) unsigned  |      |     | 1                          |       |
+---------+----------------------+------+-----+----------------------------+-------+
13 rows in set (0.01 sec)

Database Notes
Transport table:
The transport table is your transport map.  It tells postfix what transport agent to use for each domain.  It is not absolutely necessary if you want all of your users to be in the database.  We do , however, highly recommend using a transport map.  Having at least one domain handled with the local delivery agent means, among other things, simplicity if you have to deal with mailing lists later and that any users added to the system as system users will automatically receive mail properly.  Even if you have domains explicitly defined in the mydestinations config option for postfix, you should make sure that every domain has a corresponding entry in the transport map.  Another nice feature of the transport map is that you do not have to define mydestinations explicitly, simply add $transport_maps to mydestinations and you have all your domains added cleanly and effectively without having to reload Postfix when you add a domain.

domain    =  Any domain you host.  Including those you want to deliver to using local: as the transport.
transport =  The transport method.  All transport methods are legal, but usually either "local:" for local transport or "virtual:" for the virtual transport agent.

Virtual table:
The virtual table is your virtual map.  It is similar to aliases, but different.  An entry in virtual cannot point to an executable or a file.  It is simply address to address mapping.  However, this doesn't mean it has to be one to one.  You can have a single virtual alias point to multiple addresses in a comma-delimited list.  The important thing to remember is that you can have either sendmail-style or postfix-style virtual domains.  Under sendmail-style, all local aliases and users exist in the virtual domain's namespace as well.  In other words, a local user jimbob would be able to recieve jimbob@virtual1.com, jimbob@virtual2.com, and jimbob@localmachinedomain.com all at the same time.  This can cause a problem when hosting many domains (or only a few if you have more than one user named JimBob.)   Enter postfix-style virtuals.  With a postfix-style virtual, the virtual domain doesn't share anything.  You must define every alias and/or user within that domain.  One caveat is that this means root and postmaster aliases must be defined for every postfix-style virtual.  In order to implement a postfix-style virtual, you will need one line in your virtual map that has the domain name on the left, and some random text on the right.  One handy way of doing this in a database virtual table is to have an entry where address is the domain name and goto is the name of the domain owner, or some other informative little piece of text.  A nice convention is to use sendmail-style virtuals on all of your domains that are delivered with the local delivery agent and postfix-style on all of your domains delivered with the virtual agent. For a more thorough description of the virtual table and sendmail-style vs. postfix-style virtuals, please read "man 5 virtual".

address   =  This is the virtual address address@domain.tld which will be forwarded to the address in goto.  For Postfix-style virtuals, this will also be the domain name.
goto      =  Where the virtual address above goes.  This can be a comma-delimited list of addresses.  It can include simply "user" entries for local users (root, postmaster, etc.), or, more commonly, complete user@domain.tld entries for users in virtual domains on your machine or for going out to other domains.

Users table:
The users table is the heart and soul of this whole thing.  Transport and Virtual can easily be done as text files, but the users table makes this all worthwhile.  While the other two tables are used only by Postfix, this table is shared between Postfix and Courier.  That leads to interesting decisions in design.  First of all, some will note the different id and address fields.  In some cases, these fields may be identical.  In that case, remove the address field, and replace address in all postfix config files with id.  The reason there are two different fields is so you can use user.domain.tld, or user.somenumber or something along those lines rather than user@domain.tld as a username.  If you plan on using user@domain.tld for logins and never changing it, don't waste space on an address field.  Another important thing to remember is that the encrypted form of the password must be encrypted using the OS crypt(), not with MySQL's default encryption algorithm.  This can be done by using crypt=encrypt('password') in your insert or update statement.  You can increase the security of this by giving encrypt a second argument of a two-character salt.  If you write a user management program to handle your data additions and updates, then creating it randomly will be useful.  You can then use encrypt('password','salt') for the improved security.  You also should note that you can have as many or as few (including none) of the fields beginning with "imapok" above.  Those fields are not required, but can come in handy as mentioned in the table description.  Also, remember that if you would like to use the maildir quota extension in courier-imap, you will need to add a quota field to hold that.  Probably an unsigned int would be more than sufficient.

Important I have recently found that spaces within the passwd (crypted / unencrypted) seem very hit and miss as to whether they work. I suggest NOT using spaces - so be warned.

id         =  username.  Either user.domain.tld or user@domain.tld
address    =  email address. user@domain.com
crypt      =  The crypt() form of the password.  The easies way to accomplish this is to use encrypt('password') in your insert query
clear      =  Clear text password.  (for support purposes or Cram-MD5) You really only need one of the two password forms.  If you choose that one to be clear, make sure ALL of your users can handle Cram-MD5 authentication
name       =  Users real name or whatever string you feel belongs here. (not required unless you tell Courier it's there.)
uid        =  virtual uid
gid        =  virtual gid.  (hint to make your life simpler:  Make each domain have one and only one gid)
home       =  You can set this to just about anything above your maildir directory.  Easiest (but not very secure) is to set it to "/".  If you are running postfix chroot, this needs to be somewhere inside of the jail.  For instance, "/var/spool/postfix/".  And, yes, the maildirs must all be subdirectories of this directory
domain     =  user's domain name. This is far from necessary but comes in handy for support/programming purposes
maildir    =  This is the users maildir.  Use the full path.  You don't have to, but use the full path.  If this is a maildir, make sure that you include the trailing slash. (e.g. .../Maildir/ and NOT .../Maildir)  Of course, if you want to use mbox (why, oh why?!) then you would need to exclude the slash
imapok     =  This field would allow one to prevent users from accessing their mail by setting it to 0 if you use the appropriate Courier setting in authmysqlrc of "MYSQL_WHERE_CLAUSE= imapok"  This could be useful for torturing your users, among other things
bool1      =  Same as above
bool2      =  ditto. (yes, Kirby is an aspiring BOFH, how'd you guess?)

Create the Database
To make your (our) life easier, a MySQL database creation script has been created. Simply copy the mysql script into a 'filename' and at the command line type:

# mysql -u root -p < filename
Enter password:
#

issuing the password you created above (the new-password one). Here's the mysql script:

#First Create the Database
CREATE DATABASE maildb;
use maildb;
#
# Table structure for table 'transport'
#

CREATE TABLE transport (
 domain varchar(128) NOT NULL default '',
 transport varchar(128) NOT NULL default '',
 UNIQUE KEY domain (domain)
) TYPE=MyISAM;

#
# Table structure for table 'users'
#

CREATE TABLE users (
 id varchar(128) NOT NULL default '',
 address varchar(128) NOT NULL default '',
 crypt varchar(128) NOT NULL default '',
 clear varchar(128) NOT NULL default '',
 name varchar(128) NOT NULL default '',
 uid smallint(5) unsigned NOT NULL default '1000',
 gid smallint(5) unsigned NOT NULL default '1000',
 home varchar(128) NOT NULL default '/var/spool/postfix/virtual',
 domain varchar(128) NOT NULL default '',
 maildir varchar(255) NOT NULL default '',
 imapok tinyint(3) unsigned NOT NULL default '1',
 bool1 tinyint(3) unsigned NOT NULL default '1',
 bool2 tinyint(3) unsigned NOT NULL default '1',
 PRIMARY KEY  (id),
 UNIQUE KEY id (id),
 UNIQUE KEY address (address),
 KEY id_2 (id),
 KEY address_2 (address)
) TYPE=MyISAM;

#
# Table structure for table 'virtual'
#

CREATE TABLE virtual (
 address varchar(255) NOT NULL default '',
 goto varchar(255) NOT NULL default '',
 UNIQUE KEY address (address)
) TYPE=MyISAM;

Now we create our user and password to access the DB:

# mysql -u root -p maildb
Enter password:
mysql> GRANT ALL ON maildb.* TO 'uname'@'localhost' IDENTIFIED BY 'passwd';
Query OK, 0 rows affected (0.01 sec)

mysql>FLUSH PRIVILEGES;
Query OK, 0 rows affected (0.00 sec)

mysql> \q
Bye
#

Further Notes on the Database
You might, after looking at the above, be wondering why on a few data type and indexing choices.  Well, according to the mysql docs, varchar is faster in lookups and takes less space than char, so that's why it is used for all strings here.  As for the size of integers, it was just logical choosing based on potential size of the values.  In the case of the essentially boolean values, even the tinyint given to them is too much.  These could easily be defined as "int(1) unsigned" if MySQL allows. You could also go with ENUM as a data type for these. See MySQL docs for more details.  As for indexing and such, it's a good idea to have both id and address unique and indexed for faster lookups since almost every select performed on the users table will include either id or address in the where clause.  On transport, it's a good idea to index the domain field.  On virtual, you definitely want address indexed. Your virtual table will get very large, very quickly, when you consider that every virtual domain will have three minimum entries.  As you can see, the general idea is to index any field that is likely to show up in a where clause in database query. One other thing on the database: If you run Postfix in a chroot jail, and you are using localhost as your mysql server, you might want to add a hardlink to the mysql socket somewhere inside the jail/sandbox you are running in. Of course, this will likely break when you restart the MySQL server. Instead, it would be better to just use TCP. You can even use TCP with everything on the same system. Consult the MySQL documentation for details.


Configure Postfix
run the following command to update main.cf and master.cf with the latest Postfix services and configuration directives:

# /usr/pkg/sbin/postfix upgrade-configuration

Configure postfix to start on boot

# echo "postfix=YES" >> /etc/rc.conf
# echo "sendmail=NO" >> /etc/rc.conf

The existing /etc/rc.d/postfix can be forced to start /usr/pkg/sbin/postfix instead of /usr/sbin/postfix, by adding the following lines to /etc/rc.conf.d/postfix:

required_files='/usr/pkg/etc/postfix/main.cf'
start_cmd='/usr/pkg/sbin/postfix start'
stop_cmd='/usr/pkg/sbin/postfix stop'
reload_cmd='/usr/pkg/sbin/postfix reload'

Before we can start changing system wide files we need to stop sendmail; find the process and kill it:

# ps aux | grep sendmail
root     500  0.0  0.5  1012   1828 ??  Ss   Wed08AM 0:03.61 sendmail: accepting connections 
# kill 500
#

/etc/mailer.conf needs to be updated with the correct information. Change it to the following:

sendmail       /usr/pkg/sbin/sendmail
send-mail      /usr/pkg/sbin/sendmail
mailq          /usr/pkg/sbin/sendmail
newaliases     /usr/pkg/sbin/sendmail

If you don't want all users, including those locally defined to be in the database, you'll want to set up a transport map. We used a database-driven transport map as described above.  We will assume transport to be either "virtual:" for virtual delivery or "local:" for local delivery.  Other methods are available, but are beyond the scope of this howto.  The files listed here will be in your postfix config directory, which will be /usr/pkg/etc/postfix

in main.cf add the following lines

transport_maps=mysql:/usr/pkg/etc/postfix/transport.cf
mydestination = $mydomain, $myhostname, $transport_maps
virtual_mailbox_maps=mysql:/usr/pkg/etc/postfix/mysql_virt.cf
virtual_uid_maps=mysql:/usr/pkg/etc/postfix/uids.cf
virtual_gid_maps=mysql:/usr/pkg/etc/postfix/gids.cf
#You might want to change the following to something you'd prefer - BUT the 'home' field in the database MUST match
virtual_mailbox_base=/var/spool/postfix/virtual
virtual_maps=mysql:/usr/pkg/etc/postfix/virtual.cf
# The following two lines are optional, and hopefully self explanitory
virtual_mailbox_limit=102400000      # 100MB
virtual_minimum_uid=100

in master.cf you need to uncomment or add these lines if they aren't already there.

smtp      inet  n       -       n       -       -       smtpd
virtual   unix  -       n       n       -       -       virtual

Now create the following files:
transport.cf

user=uname
password=passwd
dbname=maildb
table=transport
select_field=transport
where_field=domain
hosts=localhost

mysql_virt.cf

user=uname
password=passwd
dbname=maildb
table=users
select_field=maildir
where_field=address
hosts=localhost

uids.cf

user=uname
password=passwd
dbname=maildb
table=users
select_field=uid
where_field=address
hosts=localhost

gids.cf

user=uname
password=passwd
dbname=maildb
table=users
select_field=gid
where_field=address
hosts=localhost

virtual.cf

user=uname
password=passwd
dbname=maildb
table=virtual
select_field=goto
where_field=address
hosts=localhost

Configure Courier-IMAP
The file to edit in Courier-IMAP is "authmysqlrc".  You'll want to define the following within it appropriately.  I've noticed that the sample config file is well-commented on my installation.  Hopefully the same is true for you.  If you do not see a authmysqlrc.dist or some sample authmysqlrc, the below should give you a working configuration.  There are a few other options, but we will not cover them here as they are unnecesary for our purposes. One of note is MYSQL_QUOTA_FIELD which allows you to use the courier quota extensions to maildir. The files below should be located in your courier-auth directory, /usr/pkg/etc/authlib:

Settings in authmysqlrc

MYSQL_SERVER            localhost  #your mysql server
MYSQL_USERNAME          uname
MYSQL_PASSWORD          passwd
MYSQL_SOCKET            /tmp/mysql.sock  #necessary if you are on localhost
MYSQL_DATABASE          maildb
MYSQL_USER_TABLE        users
MYSQL_CRYPT_PWFIELD     crypt
MYSQL_CLEAR_PWFIELD     clear
MYSQL_UID_FIELD         uid
MYSQL_GID_FIELD         gid
MYSQL_LOGIN_FIELD       id
MYSQL_HOME_FIELD        home
MYSQL_NAME_FIELD        name
MYSQL_MAILDIR_FIELD     maildir
MYSQL_WHERE_CLAUSE      imapok=1 AND bool1=1 AND bool2=1

IMPORTANT NOTE:  key values should be seperated with tabs NOT spaces!!!

Settings in authdaemonrc

#replace authpam with whatever your local auth is, authpwd, authsasl, whatever.
authmodulelist="authmysql authpam"
# If you only want users who are in the database to login, then only use authmysql above
version="authdaemond.mysql"

Sample Database Entries
Here is a sample of what should be in your database. It's one domain, "foo.com" with a single user "John Doe" who has the email adree "johndoe@foo.com". He also has two aliases, "root@foo.com" and anything not already assigned to a mailbox at "foo.com":

mysql> INSERT INTO transport VALUES ('foo.com', 'virtual:');
Query OK, 1 row affected (0.00 sec)

mysql> select * from transport;
+----------+-----------+
| domain   | transport |
+----------+-----------+
| foo.com  | virtual:  |
+----------+-----------+
1 row in set (0.01 sec)



mysql> INSERT INTO virtual VALUES ('@foo.com', 'johndoe@foo.com');
Query OK, 1 row affected (0.00 sec)

mysql> select * from virtual;
+--------------+-----------------+
| address      | goto            |
+--------------+-----------------+
| root@foo.com | johndoe@foo.com |
| @foo.com     | johndoe@foo.com |
+--------------+-----------------+
2 rows in set (0.00 sec)



mysql> INSERT INTO users VALUES ('johndoe', 'johndoe@foo.com', encrypt('abc'), 'abc', 'John Doe', '5000', '5000', '/', 'foo.com', 'foo.com/johndoe/', '1', '1', '1');
Query OK, 1 row affected (0.00 sec)

mysql> select * from users;
+---------+-----------------+---------------+-------+----------+------+------+...
| id      | address         | crypt         | clear | name     | uid  | gid  |
+---------+-----------------+---------------+-------+----------+------+------+...
| johndoe | johndoe@foo.com | uduM6SQn95Xjo | abc   | John Doe | 5000 | 5000 |
+---------+-----------------+---------------+-------+----------+------+------+...

...+----------------------------+---------+------------------+--------+-------+-------+
   | home                       | domain  | maildir          | imapok | bool1 | bool2 |
...+----------------------------+---------+------------------+--------+-------+-------+
   | /var/spool/postfix/virtual | foo.com | foo.com/johndoe/ |      1 |     1 |     1 |
...+----------------------------+---------+------------------+--------+-------+-------+

1 row in set (0.00 sec)

Create the directories
Based upon the user we just created, we need to create our directory structure:

# cd /var/spool/postfix
# mkdir virtual
# chown postfix:postfix virtual
# cd virtual
# mkdir foo.com
# chown postfix:postfix foo.com
# chmod 0775 foo.com
# cd foo.com
# mkdir johndoe
# chmod 0770 johndoe
# chown 5000:5000 johndoe

Starting the services
Now that everything is in place, we'll setup and start the services.

Courier-Imap Auth

# cp /usr/pkg/share/examples/rc.d/authdaemond /etc/rc.d
# echo "authdaemond=YES" >> /etc/rc.conf
# /etc/rc.d/authdaemond start
Starting authdaemond.
#

Courier-Imap

# cp /usr/pkg/share/examples/rc.d/courierimap /etc/rc.d
# echo "courierimap=YES" >> /etc/rc.conf
# /etc/rc.d/courierimap start
Starting courierimap.
# cp /usr/pkg/share/examples/rc.d/courierimaps /etc/rc.d
# echo "courierimaps=YES" >> /etc/rc.conf
# /etc/rc.d/courierimaps start
Starting courierimaps.
#

Postfix

# /etc/rc.d/postfix start
postfix/postfix-script: starting the Postfix mail system
#

What about POP3?
If you'd rather have your email users use pop3 (so the email load isn't sitting on your server) CourierIMAP includes a pop3 daemon which you simply need to "turn on":

# cp /usr/pkg/share/examples/rc.d/courierpop /etc/rc.d/
# echo "courierpop=YES" >> /etc/rc.conf
# /etc/rc.d/courierpop start
Starting courierpop.
#

OPTIONAL CLAMAV VIRUS CHECKING
Facing the realities of this world, you may be hosting email that contains viri. So the following will update postfix to use ClamAV to scan for viri:

# cd /usr/pkgsrc/mail/clamav
# make install clean clean-depends
# cd /usr/pkgsrc/mail/clamsmtp
# make install clean clean-depends

First thing to do is update the viri database. Edit /usr/pkg/etc/clamd.conf and /usr/pkg/etc/freshclam.conf, and comment the "Example" line in both, which looks as follows:

# Comment or remove the line below.
Example

Now update the viri database with this command (you may also want to cron this command to keep your database updated):

# /usr/pkg/bin/freshclam
ClamAV update process started at Sat Apr  2 22:08:55 2005
Downloading main.cvd [*]
main.cvd updated (version: 30, sigs: 31086, f-level: 4, builder: tkojm)
Downloading daily.cvd [*]
daily.cvd updated (version: 802, sigs: 1273, f-level: 4, builder: arnaud)
Database updated (32359 signatures) from database.clamav.net (IP: 62.26.160.3)
#

Now start ClamAV:

# cp /usr/pkg/share/examples/rc.d/freshclamd /etc/rc.d/freshclamd
# echo "freshclamd=YES" >> /etc/rc.conf
# /etc/rc.d/freshclamd start
Starting freshclamd.
# cp /usr/pkg/share/examples/rc.d/clamd /etc/rc.d/clamd
# echo "clamd=YES" >> /etc/rc.conf
# /etc/rc.d/clamd start
Starting clamd.
#

Now configure and start ClamSMTP:
Edit /usr/pkg/etc/clamsmtpd.conf and add the following line in the appropriate place:

ClamAddress: /tmp/clamd

and start the daemon:

# cp /usr/pkg/share/examples/rc.d/clamsmtpd /etc/rc.d/clamsmtpd
# echo "clamsmtpd=YES" >> /etc/rc.conf
# /etc/rc.d/clamsmtpd start
Starting clamsmtpd.
#

Configure postfix to use ClamAV
In main.cf add these lines:

content_filter = scan:localhost:10025
receive_override_options = no_address_mappings

In master.cf add these:

# AV scan filter (used by content_filter)
scan      unix  -       -       n       -       16      smtp
    -o smtp_send_xforward_command=yes
# For injecting mail back into postfix from the filter
localhost:10026 inet  n -       n       -       16      smtpd
    -o content_filter=
    -o receive_override_options=no_unknown_recipient_checks,no_header_body_checks
    -o smtpd_helo_restrictions=
    -o smtpd_client_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks_style=host
    -o smtpd_authorized_xforward_hosts=127.0.0.0/8

Now have Postfix use the new ClamAV setup:

# /usr/pkg/sbin/postfix stop
postfix/postfix-script: stopping the Postfix mail system
# /usr/pkg/sbin/postfix start
postfix/postfix-script: starting the Postfix mail system
#

Emails that are scanned will contain an extra header as follows:

X-Virus-Scanned: ClamAV using ClamSMTP

OPTIONAL SPAMASSASSIN CHECKING
SpamAssassin is a well known app designed to tag email as spam based upon a score rating. The configuration specifics of SpamAssassin are beyond the scope of this HowTo, however, full details can be found at: http://spamassassin.apache.org/doc.html  NetBSD's default location for the configuration files is : /usr/pkg/etc/spamassassin     We first install spamassassin:

# cd /usr/pkgsrc/mail/spamassassin
# make install clean clean-depends

You can now edit the main spamassassin config file to suit your needs /usr/pkg/etc/spamassassin/local.cf, although the default is actually quite good. Ensure you have at least the following:

rewrite_header Subject *****SPAM*****

A useful online generator can be found here: http://www.yrex.com/spam/spamconfig.php

You can then easily create your own rules within the file. For example, the following searches for the word "pharmacy" in the body of emails utilising Perl's standard matching methodology; and then gives it a score of 1.0 - the default is 5.0 before the email is classed as spam:

body PHARMACY_WORD_SEARCH /pharmacy/i
score PHARMACY_WORD_SEARCH 1.0

Now we can start the SpamAssassin daemon (spamd):

# cp /usr/pkg/share/examples/rc.d/spamd /etc/rc.d/spamd
# echo "spamd=YES" >> /etc/rc.conf
# /etc/rc.d/spamd start
Starting spamd.
#

Finally, we simply need to tell Postfix to use SpamAssassin on all incoming emails via smtp. Edit the master.cf file and change this line:

smtp      inet  n       -       n       -       -       smtpd

to this:

smtp      inet  n       -       n       -       -       smtpd
        -o content_filter=spamassassin

and then at the end of the file add this:

#SpamAssassin
spamassassin unix -     n       n       -       -       pipe
        user=nobody argv=/usr/pkg/bin/spamc -f -e
        /usr/pkg/sbin/sendmail -oi -f ${sender} ${recipient}

and restart postfix.  External emails sent in to your box will now include details such as these in the header:

X-Spam-Flag: YES
X-Spam-Checker-Version: SpamAssassin 3.0.2 (2004-11-16) on www.foo.com

Last edited by WIntellect (2005-07-01 23:57:13)

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Good Work !!! Applause smile

Keep Smiling

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Does it make sense having a printable version?

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

just because i had so many problems with this i'll just post some helping hands .......

Linux users:
You may see a few different errors if you have this problem. Most complain about can't access transport.cf. If you read the logs carefully when you restart postfix, there's actually a problem with postfix talking to mysql.sock because  postfix is chrooted, Fix for this is putting the following in /etc/init.d/postfix startup script

if [ -e /var/spool/postfix/var/run/mysqld/mysqld.sock ]; then
  rm /var/spool/postfix/var/run/mysqld/mysqld.sock
fi
mkdir -p /var/spool/postfix/var/run/mysqld
chown mysql /var/spool/postfix/var/run/mysqld

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

umm another problem i just ran into ... ID10T but i'm sure someone else is going to do it

when adding the maildir to the database, make sure you have that last '/' ..... else you'll get errors such as

/var/spool/postfix/virtual/domain.com/user is not a directory or something like that

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

i added the last '/' at the maildir, but i can't login to courier, because: "sending of password did not succeed. mail server .. responded: maildir invalid (no 'cur' directory)". in mysql db maildir = southpark.szinyei.hu/kocka/ and home= /var/spool/postfix/virtual. what can be the matter? hmm  ..sorry for my n00b

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

You'll have to create the maildir first with the maildirmake tool that comes with courier.

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

this is a pretty tight guide. i couldn't find one this good when i was starting up a hosting company a few years ago.

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

lucas wrote:

this is a pretty tight guide. i couldn't find one this good when i was starting up a hosting company a few years ago.

Thank you kindly big_smile

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

would you like to write a manual pls, how can i set up an sasl authentication from the mysql server, that the users could send message via the postfix server?

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

kocka wrote:

would you like to write a manual pls, how can i set up an sasl authentication from the mysql server, that the users could send message via the postfix server?

I'm not quite sure I understand you : "sasl authentication from the mysql server"

Are you saying that the people who send email need sasl authentication? Is that what you mean?

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

WIntellect wrote:

Are you saying that the people who send email need sasl authentication? Is that what you mean?

you're right. sorry for my english. i don't want to get an openrelay server, but i would like to use my postfix for send mails from mail clients on other machine. i hope this is clear smile

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

outgoing smtp authentication, right?

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

kocka wrote:
WIntellect wrote:

Are you saying that the people who send email need sasl authentication? Is that what you mean?

you're right. sorry for my english. i don't want to get an openrelay server, but i would like to use my postfix for send mails from mail clients on other machine. i hope this is clear smile

ok, so you want other machines using Outlook/Kmail/Sylpheed/mutt/etc. to use authentication to send their mail out to the world, right?

If you clarify that for me, then I can look into it smile   I think I recently did this for work smile

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

yes, this is what i want to do smile

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

classy

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

WIntellect wrote:

The existing /etc/rc.d/postfix can be forced to start /usr/pkg/sbin/postfix instead of /usr/sbin/postfix, by adding the following lines to /etc/rc.conf.d/postfix:

required_files='/usr/pkg/etc/postfix/main.cf'
start_cmd='/usr/pkg/sbin/postfix start'
stop_cmd='/usr/pkg/sbin/postfix stop'
reload_cmd='/usr/pkg/sbin/postfix reload'

This is where I

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

FreeBSD installs this into /usr/local/etc/postfix for the *.cf scripts and such. The postfix.sh script to start postfix is stored in /usr/ocal/etc/rc.d

So that start and stop commands would be:

/usr/local/etc/rc.d/postifx.sh start
/usr/local/etc/rc.d/postfix.sh stop
"An educator never says what he himself thinks, but only that which he thinks it is good for those whom he is educating to hear."
-Nietzsche

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Thank You RoddieRod.  I looked at the files and they were all pointing to the correct place.  I guess with FreeBSD 6.1 and ports they set it up so they all know where each other are and play together nice.  smile Yay!

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Hello,

i've follow this doc to build on freebsd 6.1 but i've always this message in /var/log/maillog when a user try a check mail on the server.

Sep 27 17:49:02 calimero pop3d: chdir twice.com/john/: No such file or directory


i've followed this:

# cd /var/spool/postfix
# mkdir virtual
# chown postfix:postfix virtual
# cd virtual
# mkdir foo.com
# chown postfix:postfix foo.com
# chmod 0775 foo.com
# cd foo.com
# mkdir johndoe
# chmod 0770 johndoe
# chown 5000:5000 johndoe

but nothing .

Many thx for your help.

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

What wave you got in your DB for the "john" entry?

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Found it !

in mysql db it was / instead of  the full path.

Thx !!

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

wink

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

Hi,

Very good Howto WInttelect! I got it setup and working in minutes! However, I came across another problem which is bugging me for 2 days now...

I tried to integrate maildrop in the process, which worked out fine partially, except for delivery to aliases. I use maildrop to move spam to a users ".Spam" folder in the Maildir, like I used to have in Qmail (which I was using before). Maildrop now delivers mail correctly for the virtual users themselves, but for the aliases of these virtual users I get the error below:

May 16 19:47:25 chernobyl postfix/pipe[3928]: 730D619EDF8: to=<alias@hostname.com>, relay=maildrop, delay=1.8, delays=1.7/0/0/0.02, dsn=5.1.1, status=bounced (user unknown. Command output: Invalid user specified. )

alias@hostname.com is an alias which should redirect to vincent@hostname.com. I have this correctly setup in the "virtual" table. I have also setup the domain transport to "maildrop:" for hostname.com in the domain table. Thats why I use "virtual_transport = $transport_maps".

I will post my configuration below.

MAIN.CF

smtpd_helo_required = yes
maildrop_destination_recipient_limit=1

transport_maps=mysql:/usr/pkg/etc/postfix/transport.cf
mydestination = $mydomain, $myhostname, $transport_maps
virtual_transport = $transport_maps
virtual_mailbox_maps=mysql:/usr/pkg/etc/postfix/mysql_virt.cf
virtual_uid_maps=mysql:/usr/pkg/etc/postfix/uids.cf
virtual_gid_maps=mysql:/usr/pkg/etc/postfix/gids.cf
#You might want to change the following to something you'd prefer - BUT the 'home' field in the database MUST match
virtual_mailbox_base=/data/mail/virtual
virtual_maps=mysql:/usr/pkg/etc/postfix/virtual.cf
virtual_alias_maps = $virtual_maps
alias_maps = $virtual_maps
# The following two lines are optional, and hopefully self explanitory
virtual_mailbox_limit=2048000000
virtual_minimum_uid=100

### ClamAV ###
content_filter = scan:localhost:10025
receive_override_options = no_address_mappings

### SASL ###
smtpd_sasl_auth_enable = yes
smtpd_sasl2_auth_enable = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain = $myhostname

# smtp_sasl_password_maps = mysql:/usr/local/etc/postfix/mysql_virtual_pass_maps.cf

broken_sasl_auth_clients = yes
smtpd_recipient_restrictions = permit_mynetworks,
  permit_sasl_authenticated,
  reject_non_fqdn_hostname,
  reject_non_fqdn_sender,
  reject_non_fqdn_recipient,
  reject_unauth_destination,
  reject_unauth_pipelining,
  reject_invalid_hostname,
  reject_rbl_client opm.blitzed.org,
  reject_rbl_client list.dsbl.org,
  reject_rbl_client bl.spamcop.net,
  reject_rbl_client sbl-xbl.spamhaus.org
  # check_recipient_access mysql:/usr/pkg/etc/postfix/mysql-recipient.cf
# The above line must be fitted in one line DONT FORGET

MASTER.CF

maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=mailusr argv=/usr/pkg/bin/maildrop -d ${recipient}

I am using maildrop from /usr/pkgsrc/mail/maildrop on NetBSD 3.1. If more information is needed, please let me know.

Does anyone have a clue what needs to be done here?

Thanks in advance,

CentralX

Last edited by CentralX (2007-05-17 22:15:17)

Re: PostfixCourierIMAP MySQL Virt. hosts + optionals ClamAV & SpamAssassin

I'm afraid I don't know maildrop, sorry sad

"UBER" means I don't drink the coffee... I chew the beans instead
             -- Copyright BSDnexus