{"id":14,"date":"2006-03-15T15:40:52","date_gmt":"2006-03-15T14:40:52","guid":{"rendered":"http:\/\/new.tom.scholten.nu\/weblog\/?page_id=14"},"modified":"2008-04-01T20:58:25","modified_gmt":"2008-04-01T19:58:25","slug":"postfix-ldap-howto","status":"publish","type":"page","link":"https:\/\/tom.scholten.nu\/weblog\/archief\/postfix-ldap-howto-2\/postfix-ldap-howto","title":{"rendered":"Postfix LDAP Howto"},"content":{"rendered":"<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.<\/a><\/p>\n<p><a href=\"http:\/\/tom.scholten.nu\/weblog\/?page_id=232\">.This howto is outdated and kept here for historical purposes, please proceed to the newer version by following this link.<\/a><\/p>\n<p>.<\/p>\n<p>.<\/p>\n<p>.<\/p>\n<p>.<\/p>\n<p>.<\/p>\n<p>.<\/p>\n<hr \/>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<table border=\"0\" width=\"100%\">\n<tbody>\n<tr>\n<td style=\"width: 100%\">\u00c2\u00a0\u00c2\u00a0<\/p>\n<h1>Full-fledged Postfix using LDAP HOWTO<\/h1>\n<h5>Postfix, LDAP, IMAP, WebMail, Virus- and spamscanning\/checking mail system<\/h5>\n<h6>by Tom Scholten and author of likewise documents<\/h6>\n<p><sub>Special thanks to Richard from UnixGuru.nl<\/sub><\/p>\n<p>\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<hr \/>\n<p><tt>The document provides a description or howto if you will to build a unix-based postfix mailserver capable of multiple virtual domains and users based upon LDAP. There are other documents describing how to implement Postfix and LDAP, and this howto is based upon them, but is written as a 'from-scratch' to 'fully-operational' manual, as i found a great tutorial on Postfix-Mysql including a nice PHP admin frontend besides the great JAMM approach which has also a nice, but in java (server pages), admin tool. Plans for now are to deliver both a perl\/cgi interface AND a PHP interface to administrate you're very own Postfix-LDAP mail system.<\/tt><\/p>\n<p>\u00c2\u00a0<\/p>\n<hr \/>\n<p>This page aims at describing how i installed and configured Postfix with virtual domains, ldap and virusscanning on a FreeBSD 5.x system<br \/>\nThere is no reason why this wouldn&#8217;t work on any other UN*X system, including all flavors of linux, *bsd, hpux, tru64, solaris or aix. Maybe on SCO but i don&#8217;t like either there OS nor their attitude.<br \/>\nOfcourse no responsibility or liability blah blah blah, to be continued<\/p>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<hr \/>\n<p>Heads up : <span style=\"color: #ff0000;\">The proposed mailserver in this document is using amavis for content scanning, i&#8217;m working on a second version of this document using <a href=\"http:\/\/www.mailscanner.info\/\">MailScanner<\/a> instead of amavis, hopefully with user-specific SpamAssassin rules in LDAP (using SA 3.x)<\/span><\/p>\n<p>\u00c2\u00a0<\/p>\n<hr \/>\n<h4>Installed software (on FreeBSD)<\/h4>\n<p>Installed packages from the portstree (in no particular order, let alone dependancy order)<\/p>\n<ul>\n<li>procmail (3.22)<\/li>\n<li>postfix (2.2.1)<\/li>\n<li>courier-imap (3.0.4,1)<\/li>\n<li>squirrelmail (1.4.3a)<\/li>\n<li>apache2 (2.0.50)<\/li>\n<li>php (4.3.7)<\/li>\n<li>openldap (2.0.25)<\/li>\n<li>clamav (0.74)<\/li>\n<li>amavisd-new (20030616)<\/li>\n<li>p5-SpamAssassin (2.63)<\/li>\n<\/ul>\n<h4>Assumptions<\/h4>\n<p>Assumptions made for this howto<\/p>\n<ul>\n<li>The example domain name will be &#8216;example.org&#8217;<\/li>\n<li>The virtual users (mail)directory store will live under \/usr\/virtual<\/li>\n<li>The virtual user used is vmail (group vmail) uid\/gid are both 2001<\/li>\n<li>The scanner runs as vscan\/vscan (2002\/2002)<\/li>\n<\/ul>\n<hr \/>\n<h4>OpenLDAP configuration<\/h4>\n<p>First edit <tt>slapd.conf<\/tt> in <em>\/usr\/local\/etc\/openldap<\/em>, the example below does NOT have a safe password (secret), which you should create using <em>slappasswd<\/em> from the commandline. It asks you to type you&#8217;re password twice and then prints out the string to be used. You can use another (than SSHA, the default) algorythm, <tt>man slappasswd<\/tt> for more information! Also in the example below there are NO acl&#8217;s so anyone has access to you&#8217;re information, consider RTFM on (Open)LDAP to secure you&#8217;re LDAPtree some more.<\/p>\n<p>slapd.conf<\/p>\n<pre>include         \/usr\/local\/etc\/openldap\/schema\/core.schema\r\ninclude \/usr\/local\/etc\/openldap\/schema\/cosine.schema\r\ninclude \/usr\/local\/etc\/openldap\/schema\/nis.schema\r\ninclude \/usr\/local\/etc\/openldap\/schema\/mailserver.schema\r\n\r\n#######################################################################\r\n# ldbm database definitions\r\n#######################################################################\r\n\r\ndatabase        ldbm\r\nsuffix          \"dc=example,dc=org\"\r\n\r\nrootdn          \"cn=Manager,dc=example,dc=org\"\r\n#rootpw          secret\r\nrootpw\t\t{SSHA}FyQuk6XJK4aYcz1fh6RhbG2g\/CtvCtz3\r\n\r\n# The database directory MUST exist prior to running slapd AND\r\n# should only be accessable by the slapd\/tools. Mode 700 recommended.\r\ndirectory       \/var\/db\/openldap-data\r\n\r\n# Indices to maintain\r\nindex   objectClass     pres,eq\r\nindex   mail,cn         eq,sub\r\n\r\n# logging\r\nloglevel 256<\/pre>\n<hr \/>\n<h4>OpenLDAP scheme for mailserver<\/h4>\n<p>Next, create the LDAPscheme to be used for our mailserver in <em>\/usr\/local\/etc\/openldap\/schema<\/em><\/p>\n<p>The used mailserver.schema<\/p>\n<pre>#\r\n# OID prefix: 1.3.6.1.4.1.12461\r\n#\r\n# Attributes: 1.3.6.1.4.1.12461.1.1\r\n#\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.1 NAME 'postfixTransport'\r\nDESC 'A string directing postfix which transport to use'\r\nEQUALITY caseExactIA5Match\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.26{20} SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.2 NAME 'accountActive'\r\nDESC 'A boolean telling whether an account is active or not'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.3 NAME 'lastChange'\r\nDESC 'Time in unix time of last change in entry'\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE )\r\n\r\n#attributetype ( 1.3.6.1.4.1.12461.1.1.4 NAME 'jvd'\r\n#        DESC 'A virtual domain managed by Jamm'\r\n#        EQUALITY caseIgnoreIA5Match\r\n#        SUBSTR caseIgnoreIA5SubstringsMatch\r\n#        SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\r\n\r\n# The following attributes are borrowed from Courier's schema so that\r\n# the Jamm Schema can live on its own.\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.5 NAME 'mailbox'\r\nDESC 'The absolute path to the mailbox for a mail account in a non-default location'\r\nEQUALITY caseExactIA5Match\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.6 NAME 'quota'\r\nDESC 'A string that represents the quota on a mailbox'\r\nEQUALITY caseExactIA5Match\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.7 NAME 'clearPassword'\r\nDESC 'A separate text that stores the mail account password in clear text'\r\nEQUALITY octetStringMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.40{128})\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.8 NAME 'maildrop'\r\nDESC 'RFC822 Mailbox - mail alias'\r\nEQUALITY caseIgnoreIA5Match\r\nSUBSTR caseIgnoreIA5SubstringsMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.26{256} )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.9 NAME 'mailsource'\r\nDESC 'Message source'\r\nEQUALITY caseIgnoreIA5Match\r\nSUBSTR caseIgnoreIA5SubstringsMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.26 )\r\n\r\n# Back to more of Jamm specific attributes\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.10 NAME 'editAliases'\r\nDESC 'A boolean telling whether a domain manager can edit Aliases'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.11 NAME 'editAccounts'\r\nDESC 'A boolean telling whether a domain manager can edit Accounts'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.12 NAME 'editPostmasters'\r\nDESC 'A boolean telling whether a domain manager can edit Postmasters'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.13 NAME 'editCatchAlls'\r\nDESC 'A boolean telling whether a domain manager can edit CatchAlls'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\nattributetype ( 1.3.6.1.4.1.12461.1.1.14 NAME 'delete'\r\nDESC 'A boolean telling whether this item is marked for deletion'\r\nEQUALITY booleanMatch\r\nSYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE )\r\n\r\n#\r\n# Objects: 1.3.6.1.4.1.12461.1.2\r\n#\r\n\r\nobjectclass ( 1.3.6.1.4.1.12461.1.2.1 NAME 'mailAccount'\r\nSUP top STRUCTURAL\r\nDESC 'Mail account objects'\r\nMUST ( mail $ homeDirectory $ mailbox $ accountActive $ lastChange $\r\ndelete )\r\nMAY ( uidNumber $ gidNumber $ uid $ cn $ description $ quota $\r\nuserPassword $ clearPassword ) )\r\n\r\nobjectclass ( 1.3.6.1.4.1.12461.1.2.2 NAME 'mailAlias'\r\nSUP top STRUCTURAL\r\nDESC 'Mail aliasing\/forwarding entry'\r\nMUST ( name $ mail $ maildrop $ accountActive $ lastChange )\r\nMAY ( mailsource $ cn $ description $ userPassword ) )\r\n\r\nobjectclass ( 1.3.6.1.4.1.12461.1.2.3 NAME 'mailDomain'\r\nSUP domain STRUCTURAL\r\nDESC 'Virtual Domain entry to be used with postfix transport maps'\r\nMUST ( dc $ accountActive $ lastChange $ delete $ editAccounts $\r\neditPostmasters )\r\nMAY ( postfixTransport $ description  ) )\r\n#        SUP top STRUCTURAL\r\n#       MUST ( jvd $ accountActive $ lastChange $ delete $ editAccounts $\r\n\r\nobjectClass ( 1.3.6.1.4.1.12461.1.2.4 NAME 'mailPostmaster'\r\nSUP top AUXILIARY\r\nDESC 'Added to a mailAlias to create a postmaster entry'\r\nMUST roleOccupant )<\/pre>\n<hr \/>\n<h4>Ready, Aim, OpenLDAP<\/h4>\n<p>Now start up you&#8217;re OpenLDAP server either by hand or use <em>\/usr\/local\/etc\/rc.d\/slapd.sh start<\/em> and verify that <tt>slapd<\/tt> is running (<em>ps ax | egrep slap[d]<\/em>). If it&#8217;s running were ready to fill &#8230;<\/p>\n<hr \/>\n<h4>OpenLDAP tree layout and initial ldif<\/h4>\n<p>This initial ldif will create you&#8217;re domains, so if you have many, just stick to one or two at the time to test out, were creating the following tree.<\/p>\n<p>\u00c2\u00a0<\/p>\n<table border=\"2\" width=\"100%\" bgcolor=\"#dddddd\">\n<tbody>\n<tr>\n<td style=\"width: 100%\"><tt> org<br \/>\n|<br \/>\nexample----------Manager<br \/>\n|<br \/>\nmail<br \/>\n|<br \/>\n+----------------------------------+-----------------------------+----------------------------+<br \/>\nexample.org                        somenudomain.nu                someoldaccount.demon.org       domain3.org<br \/>\n<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00c2\u00a0<\/p>\n<p>setup ldif file<br \/>\nwarning! if you have openldap22 or above, you should add to the primary dc= objects the following lines (whatever is the dc declared, for the first entry below it becomes &#8216;example&#8217; for the other (mail) object it becomes mail<\/p>\n<p>\u00c2\u00a0<\/p>\n<pre>objectClass: dcObject\r\ndc: whatever<\/pre>\n<pre># example\r\ndn: dc=example, dc=org\r\nobjectClass: top\r\nobjectClass: organization\r\nobjectClass: dcObject\r\no: example\r\ndc: example\r\n\r\n# MAnager\r\ndn: cn=Manager,dc=example,dc=org\r\nobjectClass: top\r\nobjectClass: organizationalRole\r\ncn: Manager\r\n\r\n# mail, example\r\ndn: dc=mail, dc=example, dc=org\r\nobjectClass: top\r\nobjectClass: organizationalunit\r\nobjectClass: dcObject\r\nou: mail\r\ndc: mail\r\n\r\n# example.org, mail, example\r\ndn: dc=example.org, dc=mail, dc=example,dc=org\r\naccountActive: TRUE\r\neditPostmasters: TRUE\r\neditAccounts: TRUE\r\nobjectClass: top\r\nobjectClass: mailDomain\r\ndc: example.org\r\ndelete: FALSE\r\nlastChange: 111\r\npostfixTransport: virtual:\r\n\r\n# somenudomain.nu, mail, example\r\ndn: dc=somenudomain.nu, dc=mail, dc=example,dc=org\r\naccountActive: TRUE\r\neditPostmasters: TRUE\r\neditAccounts: TRUE\r\nobjectClass: top\r\nobjectClass: mailDomain\r\ndc: somenudomain.nu\r\ndelete: FALSE\r\nlastChange: 111\r\npostfixTransport: virtual:\r\n\r\n# someoldaccount.demon.org, mail, example\r\ndn: dc=someoldaccount.demon.org, dc=mail, dc=example,dc=org\r\naccountActive: TRUE\r\neditPostmasters: TRUE\r\neditAccounts: TRUE\r\nobjectClass: top\r\nobjectClass: mailDomain\r\ndc: someoldaccount.demon.org\r\ndelete: FALSE\r\nlastChange: 111\r\npostfixTransport: virtual:\r\n\r\n# domain3.org, mail, example\r\ndn: dc=domain3.org, dc=mail, dc=example,dc=org\r\naccountActive: TRUE\r\neditPostmasters: TRUE\r\neditAccounts: TRUE\r\nobjectClass: top\r\nobjectClass: mailDomain\r\ndc: domain3.org\r\ndelete: FALSE\r\nlastChange: 111\r\npostfixTransport: virtual:<\/pre>\n<p><em>cat FILENAME | ldapadd -x -D &#8216;cn=Manager,dc=example,dc=org&#8217; -w secret<\/em> to add the information to your LDAP server<\/p>\n<hr \/>\n<h4>OpenLDAP scripts<\/h4>\n<p>Using the script provide below we can add users, according to the domain setup shown below using the following commands<br \/>\nexample<\/p>\n<ul>\n<li><tt>.\/ldapadduser.pl vivian example.org \"Secret123\" mailbox | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl alexander example.org \"Dog=Cr@zy\" mailbox | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl roland_dg example.org \"a1zihw\" mailbox | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl postmaster example.org alexander@example.org,vivian@example.org alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl webmaster example.org john@webbuilders.com alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl \"*\" example.org vivian@example.org alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<\/ul>\n<p>somenudomain.nu<\/p>\n<ul>\n<li><tt>.\/ldapadduser.pl ian somenudomain.nu \"vivian\" mailbox | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl postmaster somenudomain.nu alexander@example.org alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl webmaster somenudomain.nu \"w3bs1t3\" mailbox | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<\/ul>\n<p>someoldaccount.demon.org<\/p>\n<ul>\n<li><tt>.\/ldapadduser.pl \"*\" someoldaccount.demon.org @somenudomain.nu alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<\/ul>\n<p>domain3.org<\/p>\n<ul>\n<li><tt>.\/ldapadduser.pl postmaster domain3.org alexander@example.org alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<li><tt>.\/ldapadduser.pl webmaster domain3.org alexander@example.org alias | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret<\/tt><\/li>\n<\/ul>\n<p>And so, we should have created a tree looking like this, with vivian@example.org receiving all &#8216;non-existing-mailbox&#8217; mail from example.org and all mail from &#8216;someoldaccount.demon.org&#8217; delivered to somenudomain.nu (so you can email ian@someoldaccount.demon.org and still reach ian, whom (ofcourse) should have procmail or so set up to warn people about his changed domain name, but that&#8217;s outside the scope of this document). Also mind that postmaster@example.org is delivered to BOTH alexander AND vivian. The add-script just makes to &#8216;maildrop&#8217;s in the LDAP tree!<\/p>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<table border=\"2\" width=\"100%\" bgcolor=\"#dddddd\">\n<tbody>\n<tr>\n<td style=\"width: 100%\"><tt> org<br \/>\n|<br \/>\nexample----------Manager<br \/>\n|<br \/>\nmail<br \/>\n|<br \/>\n+----------------------------------+-----------------------------+----------------------------+<br \/>\nexample.org                        somenudomain.nu                someoldaccount.demon.org       domain3.org<\/tt><tt>Vivian                          ian                            *@ -&gt; somenudomain.nu           postmaster<br \/>\nAlexander                       postmaster                                                     webmaster<br \/>\nRoland_dG                       webmaster<br \/>\npostmaster<br \/>\nwebmaster<br \/>\n*@ -&gt; vivian@example.org<br \/>\n<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p>\u00c2\u00a0<\/p>\n<p>perl script to generate ldif for changing\/adding users (just use ldapmodify instead of the suggested ldapadd)<\/p>\n<p>\u00c2\u00a0<\/p>\n<pre>#!\/usr\/local\/bin\/perl\r\n# ldapadduser.pl\r\n#\r\n# AddUser-LDAP: Generate User entry for adding in LDAP\r\n# Author:       Raymond Ho\r\n# History:      Modify from genuser.pl from\r\n#               MacGyver aka Habeeb J. Dihu\r\n# 20040601\tTom Scholten, modified to do much more than just adding\r\n# Copyright (C) 2002, He Zhi Qiang Raymond.Ho\r\n\r\nuse Carp;\r\ncroak \"Usage: $0 [accountname] [example.org] [cleartext-password|foo@bar.com{,bar@foo.com,com@foo.bar}] [mailbox|alias]\r\n\r\naccountname being everything before the @ or if you would like a 'fallthrough\/wildcard' match \"*\" (don't let shell interpret the *!!!)\r\n\r\nexamples\r\nCreate new mailbox for user132@domain1.org using password MySecretPw\r\n$0 user132 domain1.org MySecretPw mailbox\r\n\r\nMap whole domain4.org to domain5.org\r\n$0 \"*\" domain4.org @domain5.org alias\r\n\r\nCreate fallthough catch for domain6.org to postmaster@domain6.org\r\n$0 \"*\" domain6.org postmaster@domain6.org alias\r\n\r\nCreate a webmaster@domain3.org alias which expands to the scriptmanager and contentmanager addresses\r\n$0 webmaster domain3.org scriptmanager@domain3.org,contentmanager@domain3.org alias\r\n\r\n\" unless $#ARGV == 3;\r\n\r\n# Random salt.\r\n$salt = join '', ('.', '\/', 0..9, 'A'..'Z', 'a'..'z')[rand 64, rand 64];\r\n\r\n# output list\r\n$dn             = \"dn: mail=$ARGV[0]@$ARGV[1],dc=$ARGV[1], dc=mail,dc=example, dc=orgn\";\r\n$change         = \"lastChange: 1n\";\r\n$obj1           = \"objectClass: topn\";\r\nif ( $ARGV[3] eq \"mailbox\" ) {\r\n$obj2           = \"objectClass: mailAccountn\";\r\n$mailbox        = \"mailbox: $ARGV[1]\/$ARGV[0]\/Maildir\/n\";\r\n$home           = \"homeDirectory: \/usr\/virtualn\";\r\n$clearpwd       = \"clearPassword: $ARGV[2]n\";\r\n$userpwd        = \"userPassword: {crypt}\".crypt($ARGV[2], $salt).\"n\";\r\n$uid            = \"uid: $ARGV[0]n\";\r\n$del            = \"delete: FALSEn\";\r\n}\r\nelsif ( $ARGV[3] eq \"alias\" ) {\r\n$obj2           = \"objectClass: mailAliasn\";\r\n@aliases        = split(\/,\/,$ARGV[2]);\r\nforeach $alias (@aliases)\r\n{\r\n$maildrop       .= \"maildrop: $aliasn\";\r\n}\r\nif ( $ARGV[0] eq \"*\" ) {\r\n$uid            = \"name: @$ARGV[1]n\";\r\n} else {\r\n$uid            = \"name: $ARGV[0]n\";\r\n}\r\n} else {\r\ncroak \"Cannot continue, choose either mailbox or aliasn\";\r\n}\r\n$at             = \"accountActive: TRUEn\";\r\n#if ( $ARGV[0] eq \"*\" ) {\r\n#        $mail           = \"mail: @$ARGV[1]n\";\r\n#} else {\r\n$mail           = \"mail: $ARGV[0]@$ARGV[1]n\";\r\n#}\r\n\r\n# output to stdin\r\nprint \"$dn$change$uid$obj1$obj2$at$del$mail$home$mailbox$maildrop$clearpwd$userpwd\";\r\n\r\nprint  \"n# pipe this throug -&gt; | ldapadd -x -D 'cn=Manager,dc=example,dc=org' -w secret &lt;- to insert into ldapn\";<\/pre>\n<hr \/>\n<h4>OpenLDAP searching<\/h4>\n<p>Now let&#8217;s try to find our entries in LDAP  Search domains in the tree<br \/>\n<tt># ldapsearch -x -wsecret -D \"cn=Manager, dc=example, dc=org\" -s sub -b \"dc=mail,dc=example,dc=org\" \"(&amp;(objectClass=mailDomain)(accountActive=TRUE))\" <\/tt> Search accounts in the tree<br \/>\n<tt># ldapsearch -x -wsecret -D \"cn=Manager, dc=example, dc=org\" -s sub -b \"dc=mail,dc=example,dc=org\" \"(&amp;(objectClass=mailAccount)(accountActive=TRUE))\" <\/tt> Search aliases in the tree<br \/>\n<tt># ldapsearch -x -wsecret -D \"cn=Manager, dc=example, dc=org\" -s sub -b \"dc=mail,dc=example,dc=org\" \"(&amp;(objectClass=mailAlias)(accountActive=TRUE))\" <\/tt><\/p>\n<hr \/>\n<h4>ClamAV, virusscanner<\/h4>\n<p>ClamAV uses <tt>\/usr\/local\/etc\/clamav.conf<\/tt> for configuration, the only changes made were<\/p>\n<ul>\n<li><tt>changed localsocket to \/var\/amavis\/clamd<\/tt><\/li>\n<li><tt>changed user to vscan<\/tt><\/li>\n<\/ul>\n<hr \/>\n<h4>AMaViS, contentscanner<\/h4>\n<p>AMaViS uses <tt>\/usr\/local\/etc\/amavis.conf<\/tt> for configuration,<\/p>\n<ul>\n<li><tt>changed MyDomain to example<\/tt><\/li>\n<li><tt>changed user\/group to vscan<\/tt><\/li>\n<li>Also change the settings described below;<\/li>\n<\/ul>\n<pre>-- replaced the lines containing\r\n$final_virus_destiny      = D_BOUNCE;  # (defaults to D_BOUNCE)\r\n$final_banned_destiny     = D_BOUNCE;  # (defaults to D_BOUNCE)\r\n$final_spam_destiny       = D_BOUNCE;  # (defaults to D_REJECT)\r\n$final_bad_header_destiny = D_PASS;  # (defaults to D_PASS), D_BOUNCE suggested\r\n-- with the following lines\r\n$final_virus_destiny = D_DISCARD; # (defaults to D_BOUNCE)\r\n$final_banned_destiny = D_DISCARD; # (defaults to D_BOUNCE)\r\n$final_spam_destiny = D_DISCARD; # (defaults to D_REJECT)\r\n$final_bad_header_destiny = D_PASS; # (defaults to D_PASS), D_BOUNCE suggested\r\n-- uncommented the following lines and the second was changed to \/var\/amavis\/clamd\r\n['Clam Antivirus-clamd',\r\n&amp;ask_daemon, [\"CONTSCAN {}n\", \"\/var\/amavis\/clamd\"],\r\nqr\/bOK$\/, qr\/bFOUND$\/,\r\nqr\/^.*?: (?!Infected Archive)(.*) FOUND$\/ ],\r\n# NOTE: run clamd under the same user as amavisd;  match the socket\r\n# name (LocalSocket) in clamav.conf to the socket name in this entry\r\n# When running chrooted one may prefer: [\"CONTSCAN {}n\",\"$MYHOME\/clamd\"],<\/pre>\n<hr \/>\n<h4>Starting the content scanner<\/h4>\n<p>Now startup <tt>clamav<\/tt> using <em>\/usr\/local\/etc\/rc.d\/clamav-clamd.sh start<\/em> after updating <tt>\/etc\/rc.conf<\/tt> with a line<\/p>\n<ul>\n<li><tt>clamav_clamd_enable=\"YES\"<\/tt><\/li>\n<li><tt>clamav_freshclam_enable=\"YES\"<\/tt><\/li>\n<li><tt>amavisd_enable=\"YES\"<\/tt><\/li>\n<\/ul>\n<p>The last being optional but recommended since it will update you&#8217;re database, so consider using it and also running <em>\/usr\/local\/etc\/rc.d\/clamav-freshclam.sh start<\/em>. After making sure <tt>\/var\/amavis\/clamd<\/tt> exists and (ofcourse) <em>ps ax | egrep clam[d]<\/em> returns a running process, start AMAVIS using <em>\/usr\/local\/etc\/rc.d\/amavisd.sh start<\/em> and verify we can use it to send mail, as taken from the README from Amavis\/Postfix.<\/p>\n<table border=\"1\">\n<tbody>\n<tr>\n<td bgcolor=\"#eeeeee\"><tt> --&gt;  $ telnet 127.0.0.1 10024<br \/>\nTrying 127.0.0.1...<br \/>\nConnected to 127.0.0.1.<br \/>\nEscape character is '^]'.<br \/>\n220 [127.0.0.1] ESMTP amavisd-new service ready<br \/>\n--&gt;  MAIL FROM:<br \/>\n250 2.1.0 Sender test@example.com OK<br \/>\n--&gt;  RCPT TO:<\/tt> <tt>250 2.1.5 Recipient postmaster OK<br \/>\n--&gt;  DATA<br \/>\n354 End data with .<br \/>\n--&gt;  Subject: test1<br \/>\n--&gt;<br \/>\n--&gt;  test1<br \/>\n--&gt;  .<\/tt><tt>***  250 2.6.0 Ok, id=31859-01, from MTA: 250 Ok: queued as 90B7F16F<\/tt><tt>--&gt;  MAIL FROM:<br \/>\n250 2.1.0 Sender test@example.com OK<br \/>\n--&gt;  RCPT TO:<\/tt><tt>250 2.1.5 Recipient postmaster OK<br \/>\n--&gt;  DATA<br \/>\n354 End data with .<br \/>\n--&gt;  Subject: test2 - virus test pattern<br \/>\n--&gt;<br \/>\n--&gt;  X5O!P%@AP[4\\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*<br \/>\n--&gt;  .<\/tt><tt>you should get one of the following replies (or similar), depending on<br \/>\nyour $final_virus_destiny and *virus_lovers* settings in amavisd.conf:<br \/>\n***  550 5.7.1 Message content rejected, id=16968-01 - VIRUS: EICAR-AV-Test<br \/>\n***  250 2.5.0 Ok, but 1 BOUNCE<br \/>\n***  250 2.7.1 Ok, discarded, id=16984-01 - VIRUS: EICAR-AV-Test<br \/>\n***  250 2.6.0 Ok, id=17041-01, from MTA: 250 Ok: queued as 3F1841A5F5<\/tt><tt>--&gt;  QUIT<br \/>\n221 2.0.0 [127.0.0.1] (amavisd) closing transmission channel<br \/>\nConnection closed by foreign host.<br \/>\n<\/tt><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<hr \/>\n<h4>Configure postfix<\/h4>\n<p>Changes to postfix&#8217; main.cf, also change other settings to customize you&#8217;re requirements. Setting &#8216;soft_bounce=yes&#8217; for <strong>testing<\/strong> purposes while starting out using you&#8217;re new mailserver would be a wise decision!<\/p>\n<pre># Transports\r\ntransport_server_host = localhost\r\ntransport_search_base = dc=mail,dc=example,dc=org\r\ntransport_query_filter = (&amp;(dc=%s)(objectClass=mailDomain)(accountActive=TRUE)(delete=FALSE))\r\ntransport_result_attribute = postfixTransport\r\n#transport_cache = yes\r\ntransport_bind = no\r\ntransport_scope = one\r\n# Aliases\r\naliases_server_host = localhost\r\naliases_search_base = dc=mail,dc=example,dc=org\r\naliases_query_filter = (&amp;(objectClass=mailAlias)(mail=%s)(accountActive=TRUE))\r\naliases_result_attribute = maildrop\r\naliases_bind = no\r\n#aliases_cache = yes\r\n# Accounts\r\naccounts_server_host = localhost\r\naccounts_search_base = dc=mail,dc=example,dc=org\r\naccounts_query_filter = (&amp;(objectClass=mailAccount)(mail=%s)(accountActive=TRUE)(delete=FALSE))\r\naccounts_result_attribute = mailbox\r\naccounts_bind = no\r\n#accounts_cache = yes\r\naccountsmap_server_host = localhost\r\naccountsmap_search_base = dc=mail,dc=example,dc=org\r\naccountsmap_query_filter = (&amp;(objectClass=mailAccount)(mail=%s)(accountActive=TRUE)(delete=FALSE))\r\naccountsmap_result_attribute = mail\r\naccountsmap_bind = no\r\n#accountsmap_cache = yes\r\n# Transport map\r\ntransport_maps = ldap:transport\r\nmydestination = $myhostname, localhost.$mydomain, $mydomain, mail.$mydomain, $transport_maps\r\n# Virtual maps\r\nvirtual_maps = ldap:aliases, ldap:accountsmap\r\n# Virtual accounts\r\nvirtual_mailbox_base = \/usr\/virtual\r\nvirtual_mailbox_maps = ldap:accounts\r\nvirtual_minimum_uid = 2000\r\nvirtual_uid_maps = static:2000\r\nvirtual_gid_maps = static:2000\r\n# Local accounts\r\nlocal_alias_maps = hash:\/etc\/aliases\r\nlocal_recipient_maps = $local_alias_maps unix:passwd.byname\r\n# local_transport should set to \"virtual\" to deliver mail to local VirtualAccount\r\n# 's $HOME direcotry . or not set here, mail delivery would be failure\r\nlocal_transport = virtual\r\nlocal_recipient_maps = $alias_maps unix:passwd.byname $virtual_mailbox_maps\r\n\r\n# AMAVIS\r\ncontent_filter = smtp-amavis:[127.0.0.1]:10024<\/pre>\n<p>Changes to postfix&#8217; master.cf (optionally replace the &#8216;y&#8217; with &#8216;n&#8217; depending on you&#8217;re chroot wishes). Make sure &#8216;virtual&#8217; and &#8216;maildrop&#8217; also exist in master.cf<\/p>\n<pre>smtp-amavis unix -      -       y     -       2  smtp\r\n-o smtp_data_done_timeout=1200\r\n-o smtp_send_xforward_command=yes\r\n-o disable_dns_lookups=yes\r\n127.0.0.1:10025 inet n  -       y     -       -  smtpd\r\n-o content_filter=\r\n-o local_recipient_maps=\r\n-o relay_recipient_maps=\r\n-o smtpd_restriction_classes=\r\n-o smtpd_client_restrictions=\r\n-o smtpd_helo_restrictions=\r\n-o smtpd_sender_restrictions=\r\n-o smtpd_recipient_restrictions=permit_mynetworks,reject\r\n-o mynetworks=127.0.0.0\/8\r\n-o strict_rfc821_envelopes=yes\r\n-o smtpd_error_sleep_time=0\r\n-o smtpd_soft_error_limit=1001\r\n-o smtpd_hard_error_limit=1000<\/pre>\n<hr \/>\n<h4>Starting you&#8217;re mailserver<\/h4>\n<p>Next again edit <tt>\/etc\/rc.conf<\/tt> to disable sendmail and enable postfix<br \/>\nInsert<\/p>\n<ul>\n<li><tt>sendmail_enable=\"NONE\"<\/tt><\/li>\n<\/ul>\n<p>Comment out\/remove<\/p>\n<ul>\n<li><tt>sendmail_enable=\"YES\"<\/tt><\/li>\n<li><tt>sendmail_flags=\"-bd\"<\/tt><\/li>\n<li><tt>sendmail_pidfile=\"\/var\/spool\/postfix\/pid\/master.pid\"<\/tt><\/li>\n<li><tt>sendmail_outbound_enable=\"NO\"<\/tt><\/li>\n<li><tt>sendmail_submit_enable=\"NO\"<\/tt><\/li>\n<li><tt>sendmail_msp_queue_enable=\"NO\"<\/tt><\/li>\n<\/ul>\n<p>Now would be a great time to start postfix, but before you do <em>touch \/var\/log\/maillog<\/em> and open a second terminal, screen or whatever and do a <em>tail -f \/var\/log\/maillog<\/em> there (as well as maybe a tail on <tt>\/var\/log\/messages<\/tt>) to see what&#8217;s going on. <strong>Now<\/strong> you&#8217;re ready to start postfix simply by typing <em>postfix start<\/em><\/p>\n<hr \/>\n<h4>Verify mail sending and receiving<\/h4>\n<p>Verify that you can send mail by sending yourself mail. If using an address@example.org which is an alias (pointing outside one of the domains for wich your new postfix server receives mail) please check you&#8217;re headers and confirm their ok. If receiving on a mailbox address (at your new postfix server), look in <em>\/usr\/virtual<\/em> and confirm you have a new directory named <strong>example.org<\/strong> (depending on the recipient). Below that directory your mailbox name (as a directory) should emerge, containing various maildir files. Besides checking that your mail was received also check the headers!<\/p>\n<p><strong>Note that users of a mailbox should have received at least one email to have their directory (and maildir files) in place<\/strong> <em>When you create a new mailbox (not alias), you should send the new user a &#8216;welcome&#8217; mail of some kind to let postfix create their &#8216;homedirectory&#8217; in <strong>\/usr\/virtual<\/strong>. When omitted the user will receive an error when checking mail (either via imap\/pop or when using webmail)<\/em><\/p>\n<hr \/>\n<h4>Webmail, a web frontend to mail<\/h4>\n<p>In this howto we choose squirrelmail. Why? Because it&#8217;s a good, safe and easy to install webmail application. When using FreeBSD we can install squirrelmail from ports, but i decided to just download the package and do everything by hand. Either way you&#8217;ll end up with a squirrelmail directory (<em>\/usr\/local\/www\/data\/squirrelmail<\/em>) which contains a file called <tt>configure<\/tt>. Run <em>.\/configure<\/em> in the squirrelmail directory and a menu will appear. For our sample setup the following defaults were changed<\/p>\n<pre>2 - Server Settings\r\n1 - Domain\r\nexample.org\r\n3 - Sendmail or SMTP\r\nSMTP\r\nA - Update IMAP settings (reveals new options)\r\n4 - IMAP server\r\nlocalhost\r\n5 - IMAP port\r\n993\r\n6 - Authentication type\r\nlogin\r\n7 - Secure IMAP (TLS)\r\ntrue\r\n8 - Server software\r\ncourier\r\nB - Update SMTP settings (reveals new options))\r\n4 - SMTP server\r\nlocalhost\r\n5 - SMTP port\r\n25\r\nR - Return to main menu<\/pre>\n<p>You can change more options if you like, and even install some nice plugins from the <a href=\"http:\/\/www.squirrelmail.org\/\">squirrelmail website<\/a> but this will do for now. Point your webbrowser to http:\/\/www.example.org\/squirrelmail and login as one of your mailbox users. Happy webmailing!<\/p>\n<hr \/>\n<h4>Documents and resources used<\/h4>\n<p>Besides RTFM on various packages used to install a number of special resources will be named below the packages linklist; (in no particular order)<\/p>\n<ul>\n<li><a href=\"http:\/\/www.postfix.org\/\">Postfix, MTA used<\/a><\/li>\n<li><a href=\"http:\/\/www.spamassassin.org\/\"> Spam tagging software<\/a><\/li>\n<li><a href=\"http:\/\/www.openldap.org\/\"> LDAP software<\/a><\/li>\n<li><a href=\"http:\/\/www.squirrelmail.org\/\"> Webmail software<\/a><\/li>\n<li><a href=\"http:\/\/www.list.org\/\"> Mailinglist software (when done, you could add mailinglists, not captured in this document)<\/a><\/li>\n<li><a href=\"http:\/\/www.clamav.org\/\"> ClamAV Antivirus software<\/a><\/li>\n<li><a href=\"http:\/\/www.amavis.org\/\"> Short for [A] [MA]il [VI]rus [S]canner<\/a><\/li>\n<li><a href=\"http:\/\/httpd.apache.org\/\"> Apache webserver for webmail and admin (php) interface<\/a><\/li>\n<li><a href=\"http:\/\/www.php.org\/\"> The admin interface was build using php<\/a><\/li>\n<\/ul>\n<p>Most usefull to produce this document was :<a href=\"http:\/\/janus.errornet.de\/\">http:\/\/janus.errornet.de\/<\/a> with his <a href=\"http:\/\/janus.errornet.de\/files\/documents\/Postfix-LDAP-Procmail-TLS-Spamassassin.pdf\">pdf<\/a> and <a href=\"http:\/\/janus.errornet.de\/files\/documents\/mailserver.schema\">schema<\/a><br \/>\n((mirrored here) <a href=\"http:\/\/tom.scholten.nu\/pfldap\/Postfix-LDAP-Procmail-TLS-Spamassassin.pdf\">pdf<\/a> and <a href=\"http:\/\/tom.scholten.nu\/pfldap\/mailserver.schema\">schema<\/a>)<\/p>\n<p>Others resources used<\/p>\n<ul>\n<li><a href=\"http:\/\/jamm.sf.net\/\">http:\/\/jamm.sourceforge.net<\/a> everything else seems to be based upon this<\/li>\n<li><a href=\"http:\/\/www.vriesman.tk\/\">http:\/\/www.vriesman.tk<\/a> detailed enough for me<\/li>\n<\/ul>\n<hr \/>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<h6><a href=\"mailto:tom_AT_snow_DOT_nl\">Tom Scholten<\/a> is Unix specialist with <a href=\"http:\/\/www.snow.nl\/\">Snow B.V.<\/a>, a Dutch Technical Consultancy Company supplying specialists in the field of Networking and Unix<\/h6>\n<p>\u00c2\u00a0<\/p>\n<p>\u00c2\u00a0<\/p>\n<hr \/>\n<table border=\"0\">\n<tbody>\n<tr>\n<td><a href=\"http:\/\/www.freebsd.org\/\"><img decoding=\"async\" src=\"http:\/\/tom.scholten.nu\/freebsd.gif\" border=\"0\" alt=\"freebsd\" \/><\/a><\/td>\n<td>\u00c2\u00a0<\/td>\n<td>\u00c2\u00a0<\/td>\n<td>\u00c2\u00a0<\/td>\n<td>\u00c2\u00a0<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n","protected":false},"excerpt":{"rendered":"<p>. . . . . . . . . . .This howto is outdated and kept here for historical purposes, please proceed to the newer<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":215,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","meta":{"_monsterinsights_skip_tracking":false,"_monsterinsights_sitenote_active":false,"_monsterinsights_sitenote_note":"","_monsterinsights_sitenote_category":0,"footnotes":""},"class_list":["post-14","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/pages\/14","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/comments?post=14"}],"version-history":[{"count":0,"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/pages\/14\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/pages\/215"}],"wp:attachment":[{"href":"https:\/\/tom.scholten.nu\/weblog\/wp-json\/wp\/v2\/media?parent=14"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}