Index  Page 1  Page 2  Bottom
Create Debian Installer CD
Debian Installation
PuTTY, additional programs and apt
Create Firewall Rules
Install MySQL
Install programs that use SSL certificates
Create and install SSL Certificates
Add our home page
Install and configure phpMyAdmin
Perform some preliminary Postfix configuration
Install SquirrelMail software
Install and configure PostfixAdmin and deliver
Configure SASL and TLS
Install and configure amavisd-new
Create Bayes and AWL tables in MySQL
Configure and customize SquirrelMail
Configure Razor
Install pflogsumm
Install BIND
Additional Postfix configuration
Set up PostfixAdmin Vacation
Install postfixadmin SquirrelMail plugin
Install MailZu
Quota
Install Mailgraph - Optional
Install mysql-zrm
The alias issue
Alias Domains
Acting as a relay server
This Virtual 3 document cannot be used to upgrade a server created with my obsolete Versions 1 or 2 documents (http://verchick.com/mecham/public_html/spam/virtual.html http://verchick.com/mecham/public_html/spam/virtual2.html). I do have a document that you may want to look at if you are thinking of upgrading a Virtual2 box to a Virtual3 box however:
http://verchick.com/mecham/public_html/spam/lenny-squeeze.html

You don't want to loose mail due to a failed hard drive. I suggest using software or hardware RAID1 (I provide a separate software RAID1 howto). I am not going to install a GUI when I install Debian, so during installation, "Standard system" will be the only package group I select. In other words, I will not select "Desktop environment". This is a copy and paste document. You need to save all three pages of this document to your computer then edit this page and do a search and replace of certain items in order to customize it for your server. Open this saved document in WordPad, a plain text HTML editor or other similar text editor that will not modify the HTML code and read the instructions at the top of the document. The instructions will point you to some items that should be changed. Note that once this document is edited to match your preferences, there will be passwords in this document. Please take steps to secure your copy of this document. Once edited, make backup copies of all three pages to a CD or floppies or at least to a different directory. Don't make the same mistake I have made a couple times by editing this page and then downloading another fresh copy and overwriting it. I personally find that when selecting a block of text, it is easier to start at the bottom and work up rather than starting at the top and working down.

Without going into much detail about hardware requirements, I can tell you that the biggest problem you will face is how long it takes to scan a message for spam and viruses. On a powerful computer on a wide Internet pipe it typically takes around 5 seconds. On a lower powered machine (like an older P4 2.0Ghz single processor) on a narrower pipe (like a T1), it can take 20 or 30 seconds. However, you control the number of concurrent spam scanning processes. Theoretically you add more processes for greater throughput. The problem is, each process takes a considerable amount of CPU power and RAM. If you incrementally add processes, at some point your server will grind to a halt - most likely due to swap thrashing. That said, your server should have a minimum of 1GB RAM. Mark Martinec wrote a paper that illustrates the capacity of a single moderately powerful dual processor machine http://www.ijs.si/software/amavisd/amavisd-new-magdeburg-20050519.pdf. Mailscanner is a product similar to amavisd-new. Here are some samples of servers used with Mailscanner: http://wiki.mailscanner.info/doku.php?id=maq:index#setup_examples. Note that these examples are using older (less CPU intensive) versions of SpamAssassin. Also note that on this server you will have the significant additional overhead of Apache, MySQL and Dovecot. Basically, you need fast hardware.

If you want to install intrusion detection (I use an older version of AIDE) you will need a floppy drive. I have to warn you, this setup has not been tested in a production environment and has only been tested using the en_US locale.

Create Debian Installer CD:

Index Top Bottom

Make a new directory on your Windows computer and call it 'debian' or something. Then download the latest version of the Debian installer for 'squeeze' and save it there. Go to: http://www.debian.org/releases/squeeze/debian-installer/. Read the errata while you are on that page. One interesting errata is http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=401435. Also note that in version 6.0.6 there are potential installation problems with RTL8169-based network cards.

Note that there are squeeze 6.0.6 i386 or squeeze 6.0.6 amd64 (x86-64) CDs available from this location but since I am poor, I have only tested this setup using the i386 CD (32bit). If any of the hardware in your system requires non-free firmware to be loaded with the device driver, you can download a non official image including these non-free firmwares. squeeze 6.0.6 i386 or squeeze 6.0.6 amd64 (x86-64) If you have a 64-bit processor, it would be best to install the 64-bit version. In a 32-bit installation, mailbox quotas are limited to 2GB.

Debian Installation:

Index Top Bottom

Information on the Debian Installer is located at http://www.debian.org/releases/stable/installmanual

If using RAID1, you should have both drives installed. Here is an installation walkthrough using RAID1. If not using RAID, here is a quick installation walkthrough. We are going to erase the hard drive so make sure you don't have any data on it you might need. You may have to change the order of boot devices in your BIOS before we begin (so the CD-ROM boots first). Make sure the date and local time are correct in BIOS. Boot up the computer using the Installer CD. When the system boots up to the Debian screen, simply press [Enter] at the Install prompt.

[ !! Select a Language]
This determines the language of the installer.
This installation has only been tested with English - English
[ !! Select your location]
Choose what is appropriate.

Unplug the ethernet cable so DHCP will fail.

[!! Select a keyboard layout]
American English selects a standard QWERTY keyboard.

There will be a few screens of activity, then this will come up:
[Configuring the network with DHCP]
Hit [Cancel] because we want DHCP configuration to fail. If it should succeed and you end up at [Hostname:], choose [Go Back].

Plug the ethernet cable back in.

[!! Configure the network]
Network autoconfiguration failed

We wanted that to happen, simply press:
[Continue]

On the next screen, choose the default of:
[Configure network manually]

[!! Configure the network]
Make sure Num Lock is on!
[IP address:]
111.111.111.111
[Netmask:]
255.255.255.x
[Gateway:]
333.333.333.333
[Name server addresses:]
444.444.444.444 555.555.555.555
[Hostname:]
msa
[Domain name:]
example.com

[!! Set up users and passwords]
This will ask for root's password and allow you to create a "normal" user and a password for that user. Watch your [Num Lock] status. Use really good passwords and don't forget them. Please add one, and just one, normal user here. If you plan on storing mail locally on this machine (not documented here), or even if you don't, create a user whose main purpose in life might be to hold root's mail. I suggest calling the user myroot or something similar. Keep in mind that all the best hacker tools run on Linux. If a hacker gains root access to this box, your entire network is history.

[! Configure the clock]
[Select your time zone:]

Simply choose what is appropriate.

[! Configure the clock]
[Is the system clock set to UTC?]
If this comes up it may be an indication the system clock is set to UTC. I prefer to set the system clock to local time so I [tab] over and answer [NO] but this is up to you.

[!! Partition disks]

For partitioning hard drives, I am going to use software RAID1. I suggest using a pair of 320GB or larger hard drives. Another possibility is using four drives and allocating an entire pair of drives to the mail store: /var/vmail. See: http://verchick.com/mecham/public_html/raid/raid1.html.
Continue on if you are not setting up RAID.

[Partitioning method:]
Choose Guided - use entire disk

[Select disk to partition:]
Choose what is appropriate

[Partitioning scheme:]
[All files in one partition - (recommended for new users)]

[Finish partitioning and write changes to disk]
[Write changes to disk?] [Yes]

[Installing the base system]
Wait....

[! Configure the package manager]
[Debian archive mirror country:]
Choose your country
[Debian archive mirror:]
Choose a mirror near you (ftp.us.debian.org works very well in the US)
[HTTP proxy information]
(configure if needed, otherwise leave unconfigured)
Scanning the mirror... [ Configuring apt ]...

[! Configuring popularity contest]
You decide if you would like participate in the package usage survey.

[! Software selection]
[Choose software to install:]

This is 'tasksel'. You only want to select 'Standard system utilities' here (nothing else). Use the [spacebar] to deselect 'Graphical desktop environment'. Then, simply [Tab] over and select [Continue]. I heartily recommend you do not run a GUI; however, if you absolutely insist on doing so, leaving 'Graphical desktop environment' selected is the way to install it.
Software will download now. I hope you have a fast Internet connection. What software we don't have now, we can easily get later. We are trying to keep this system somewhat clean. We will use apt-get to install most software after the fact. Some software may also be upgraded, and as a result, you may be asked some questions. When asked a question, usually the default answer will be the correct answer.

[! Configuring grub-pc]
[Install the GRUB boot loader to the master boot record?]
Choose [Yes]
[!! Finish the installation]
The CD door should open, remove the CD, then hit [Continue] This will reboot.

Once the system is installed, login as root and issue the following command:
apt-get install rdate ssh vim


PuTTY, additional programs and apt:

Index Top Bottom

I will now turn off the monitor and head on over to my trusty old Windows computer. I am going to configure everything else from there.

You should have this document open in a window on your Windows computer because we are going to use the Windows SSH client PuTTY to operate our server remotely. We are going to save a lot of typing because you are going to select text with the mouse, copy it to the clipboard with [Ctrl]+c and then paste it into the PuTTY window with a right-click of the mouse. Also, simply highlighting text in the PuTTY window copies that text to the clipboard.

Place putty.exe on your desktop, open it up, select SSH, input the IP address of the server then enter a name for your session in the Saved Sessions box. In the Category window, expand Terminal and click on Features. Check the box "Disable application keypad mode". Just below this in the Category window, click on Window, and increase "Lines of scrollback" from 200 to 800. click on Session (at the top), then Save. The "Disable application keypad mode" enables us to use the numeric keypad when using vi.

When you use PuTTY again, simply double click on the saved session. Make sure you are at a command (shell) prompt before exiting PuTTY. You can log out of the bash shell (and hence close the the PuTTY window) by issuing the command 'logout' or 'exit' or [Ctrl]+d.

Open up a PuTTY session now and log in as root.

This would be a good time to also download and install WinSCP. WinSCP is a great GUI file browser that lets you transfer files between your Windows machine and your new Debian box. You can also edit files on your Debian box from your Windows machine using WinSCP's editor. I suggest when you save sessions you leave the password blank so you are prompted for it each time you log in.

We are going to use vi (vim actually) to do most of our editing. Fortunately we only need to learn a few commands to be able to accomplish our tasks. There are 3 operating modes in vi. There is the "Command" mode, the "Write" mode and the "Command line" mode. When you first open a file for editing, you are in Command mode. You change to Write mode by entering the letter "i", (short for "insert"). You can edit text pretty much as you would expect in Write mode. You exit out of Write mode and return to Command mode by hitting the [Esc] key. There are many commands that can be learned in Command mode but we only need to learn two more in addition to "i". Those commands are ":" (a colon) and "/" (a forward slash). The colon is used to enter the third mode, the Command line mode and the slash enables the Search command. When you are in Command line mode, you will see a colon at the bottom of the screen. Here is a list of commands we will use while in Command line mode:

:q    quit (provided you have not made any changes) By the way, the lower case q is used often in *nix as a way to exit a screen.
:q!   exits vi and discards changes (great when you trashed the file and just want to start over!)
:wq   saves the changes and exits vi (write and quit)
:w    saves the current changes but does not exit vi (write)
And in command mode:
G    The capital "G" Goes to the bottom of the page (very handy)

And here is how the Search command works:
/text_to_search_for   moves the cursor to the first occurrence of     text_to_search_for

Once the first occurrence of the text we searched for is found, use a lower case 'n' to find the next occurrence.

That's all we need to know for now! The biggest mistake you will make at first is you will forget to hit "i" (to enter insert mode) before you paste stuff into a document.
If you would like a cheat sheet for additional commands: http://www.fprintf.net/vimCheatSheet.html and http://amath.colorado.edu/computing/unix/vi/

Set the system's hardware clock:
Correct the time:
rdate -ncv clock.fmt.he.net
rdate -ncv ntp1.tummy.com


/sbin/hwclock --systohc

If this process hangs and you get a time out error, it's possible you have a BIOS incompatibility with the hwclock software. This is most common on some Dell machines. If and only if you have a problem here, perform this next step:
/sbin/hwclock --systohc --directisa


There is an errata dealing with tcp_window_scaling on Linux kernel 2.6.17 (and newer).
http://kerneltrap.org/node/6723 http://marc.info/?l=postfix-users&m=117457942431349 You may want to consider what may happen (large files fail to transfer between systems) when there is a buggy router between you and someone else, and may wish to make this change to the system (you decide):
echo "net.ipv4.tcp_wmem = 4096 65536 65536" >>/etc/sysctl.conf
echo "net.ipv4.tcp_rmem = 4096 65536 65536" >>/etc/sysctl.conf
sysctl -p


I am going to assume this may slow down communications between systems under certain circumstances.

Since we are using the squeeze CD, our default system editor will be nano, and not vim. We soon need to edit a system file called crontab but we don't want to have to learn another new editor so we will change our default system editor:
update-alternatives --config editor

Choose /usr/bin/vim.basic

vim /etc/apt/sources.list

At this point, the contents of the file may look something like this:
# deb cdrom:[Debian GNU/Linux 6.0.0 _Squeeze_ - Official i386 NETINST Binary-1 20110205-14:34]/ squeeze main

deb http://ftp.us.debian.org/debian/ squeeze main
deb-src http://ftp.us.debian.org/debian/ squeeze main

deb http://security.debian.org/ squeeze/updates main
deb-src http://security.debian.org/ squeeze/updates main

deb http://ftp.us.debian.org/debian/ squeeze-updates main
deb-src http://ftp.us.debian.org/debian/ squeeze-updates main
We need to modify this file so the result will look something like this:
(with only the http server unique to your particular system)
deb http://ftp.us.debian.org/debian/ squeeze main non-free
deb-src http://ftp.us.debian.org/debian/ squeeze main

deb http://security.debian.org/ squeeze/updates main non-free
deb-src http://security.debian.org/ squeeze/updates main

deb http://ftp.us.debian.org/debian/ squeeze-updates main
deb-src http://ftp.us.debian.org/debian/ squeeze-updates main
Note what I have done here. Any lines that use the cdrom:
#deb cdrom:[Debian GNU/Linux 6.0.0
have been erased and the words "non-free" and "contrib" have been added.

You must run apt-get update next.
apt-get update


You should also make sure the system is up to date. To clean up a couple things, we will reboot:
apt-get upgrade

reboot



For security reasons I remove some programs. My server will not provide shared disk space.
apt-get remove nfs-common portmap


We will save the time to the real-time clock once each day:
crontab -e

and insert on the first available blank line (below the comments):
56 11 * * * /sbin/hwclock --systohc

Then save and exit the file. Remember that some Dell's may need the --directisa parameter added here.

Create Firewall Rules:

Index Top Bottom

On my system I try to keep as much traffic as possible between this machine and my users encrypted, so personally I don't open port 80. I will configure my clients to use 443 (apache2 with SSL), 993 (dovecot-imapd) and 995 (dovecot-pop3d) whenever possible. I also limit access to ssh to my local network.

DO NOT USE AS IS, CHANGE NETWORK ADDRESS FIRST IF YOU HAVE NOT ALREADY DONE SO:
You can copy and paste this whole section to the shell prompt:
iptables -F
iptables -N FIREWALL
iptables -F FIREWALL
iptables -A INPUT -j FIREWALL
iptables -A FORWARD -j FIREWALL
iptables -A FIREWALL -p tcp -m tcp --dport 25 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 80 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 110 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 143 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 443 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 465 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 587 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 993 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 995 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --dport 4650 --syn -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp -s 222.222.222.222/24 --dport 22 --syn -j ACCEPT
iptables -A FIREWALL -i lo -j ACCEPT
iptables -A FIREWALL -p udp -m udp --sport 53 -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --sport 53 -j ACCEPT
iptables -A FIREWALL -p udp -m udp --dport 123 -j ACCEPT
iptables -A FIREWALL -p udp -m udp --sport 123 -j ACCEPT
iptables -A FIREWALL -p udp -m udp --sport 6277 -j ACCEPT
iptables -A FIREWALL -p udp -m udp --sport 24441 -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type destination-unreachable -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type source-quench -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type time-exceeded -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type parameter-problem -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type echo-reply -j ACCEPT
iptables -A FIREWALL -p icmp --icmp-type echo-request -j ACCEPT
iptables -A FIREWALL -p tcp -m tcp --syn -j REJECT
iptables -A FIREWALL -p udp -m udp -j REJECT
iptables -A FIREWALL -p icmp -j DROP
iptables-save > /etc/firewall-rules
iptables-restore < /etc/firewall-rules

This will write the firewall rules to a file on this server, but iptables starts with an empty rule set each time the computer restarts. The rule set I saved to /etc/firewall-rules must be "loaded" into iptables every time the system starts up.

I am going to insert the command to configure iptables into a file that starts up the network interfaces when the system boots up:
vi /etc/network/interfaces

And insert the following text in the blank line just below "iface lo inet loopback":
pre-up iptables-restore < /etc/firewall-rules

While you are at it, 2 lines down, change allow-hotplug eth0 to:
auto eth0

Save and exit the file, then reboot again:
reboot
exit


If you have problems accessing the system after it boots up, enter the command  iptables -F   from the console to clear out iptables. This will allow you another shot at it.

Not related to iptables, but continue on by installing ntp, a c compiler, logcheck and some other stuff. Select the entire text - but when selecting apt-get commands, do not select past the text:
apt-get install ntp make automake1.9 gcc bison flex libc6-dev logcheck logcheck-database psmisc libcurl3 libssh2-1 byacc libbz2-dev

For more information about logcheck rules and patterns to include or ignore:
vi -R /usr/share/doc/logcheck-database/README.logcheck-database.gz

and to debug logcheck:
su -s /bin/bash -c "/usr/sbin/logcheck -otd" logcheck

We will suppress some items in the logcheck database. Copy and paste this entire section - some lines wrap on the screen. When selecting commands like this (non apt-get commands), you can select past the text:
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: Kernel logging \(proc\) stopped." >>/etc/logcheck/ignore.d.server/syslogd
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ kernel: imklog .* log source = /proc/kmsg started." >>/etc/logcheck/ignore.d.server/syslogd
echo '^\w{3} [ :0-9]{11} [._[:alnum:]-]+ rsyslogd: \[origin software="rsyslogd" swVersion=.*x-pid=.*x-info="http://www.rsyslog.com"\] restart' >>/etc/logcheck/ignore.d.server/syslogd
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ ntpd\[[0-9]+\]: clock is now unsynced$" >>/etc/logcheck/ignore.d.server/ntp
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ ntpd\[[0-9]+\]: clock is now synced$" >>/etc/logcheck/ignore.d.server/ntp



Install MySQL

Index Top Bottom
We will install MySQL next. During MySQL installation you will be asked for root's password: roots_password
apt-get install mysql-server

Out of the box MySQL is tuned for a system with very little memory. We need to allocate more memory for caching items. This will make a huge difference in performance. We are going to change all tables to InnoDB so we will adjust some InnoDB settings. If you currently have data in MySQL or you have made changes to /etc/mysql/my.cnf you should not perform these steps to replace your my.cnf file with mine! Also, I am assuming you have at the very least 1GB of physical memory installed (2GB recommended - 3GB even better):
cd /etc/mysql/
cp my.cnf my.cnf.original
wget http://verchick.com/mecham/public_html/spam/my.cnf.innodb.patch.txt
patch my.cnf < my.cnf.innodb.patch.txt
/etc/init.d/mysql restart


Log into MySQL using root's password: roots_password
mysql -p

From the mysql> prompt, run these commands:
SHOW VARIABLES LIKE 'innodb_fast_shutdown';
QUIT


Make a mental note of the innodb_fast_shutdown setting. The default is "1".

Since my patch changed the size of innodb_log_file_size, we will have to create new logs. If innodb_fast_shutdown was something other than "1", edit /etc/mysql/my.cnf and add an entry "innodb_fast_shutdown = 1" and then restart mysql with "/etc/init.d/mysql restart". Once innodb_fast_shutdown = 1 then:
/etc/init.d/mysql stop

Make sure that it shuts down without errors (to ensure that there is no information for outstanding transactions in the logs). Then:
mv /var/lib/mysql/ib_logfile0 /var/lib/mysql/iblogfile0-old
mv /var/lib/mysql/ib_logfile1 /var/lib/mysql/iblogfile1-old
/etc/init.d/mysql start
ls -l /var/lib/mysql/ib_*


It should show our two log files are now 48MB in size (50331648). I have set "innodb_buffer_pool_size = 192M" which will give much better performance than the default of 8M, and "innodb_log_file_size = 48M" which is 25% of the innodb_buffer_pool_size. If you have plenty of RAM, you can increase innodb_buffer_pool_size even more, but personally I would not set it to more than 25% of physical RAM. You may need the extra space for a polar bear or dung beetle.

Install programs that use SSL certificates

Index Top Bottom
When selecting apt-get commands, select all the text, but not past the text. These next apt-get commands are four separate commands:
apt-get install apache2 libapache2-mod-php5 php5 php5-common php5-mysql php5-gd php5-mcrypt mcrypt ca-certificates openssl dovecot-common dovecot-imapd dovecot-pop3d

apt-get install libcompress-raw-zlib-perl libcompress-zlib-perl libdigest-hmac-perl libdigest-sha1-perl libfile-remove-perl libio-compress-base-perl libio-compress-zlib-perl libio-stringy-perl

apt-get install libsys-hostname-long-perl libuser-identity-perl libmail-box-perl libobject-realize-later-perl liburi-perl libio-socket-ssl-perl libnet-ident-perl libmail-dkim-perl pax

apt-get install libarchive-zip-perl libberkeleydb-perl libconvert-binhex-perl libconvert-tnef-perl libconvert-uulib-perl libio-multiplex-perl libmime-tools-perl libnet-cidr-perl libnet-server-perl libunix-syslog-perl


Now install Postfix:
apt-get install postfix postfix-pcre postfix-mysql libsasl2-modules-sql libsasl2-modules

Answer the questions:
General type of configuration? Internet Site (the default)
Mail name? msa.example.com (the default)


And change a PHP logging setting:
sed -i "s/error_reporting = E_ALL \& ~E_DEPRECATED/error_reporting = E_ALL \& ~E_NOTICE \& ~E_DEPRECATED/" /etc/php5/apache2/php.ini

Change the default shell from dash to bash:
dpkg-reconfigure dash

Answer: Use dash as the default system shell (/bin/sh)? <No>


Create and install SSL Certificates

Index Top Bottom
Every client that connects to this server will need to be able to resolve the hostname of the server. Add an entry to your hosts file, or add an A record to your DNS server so we can properly interact with the server. Of course, at some point an MX record (or records) will also need to be created. We are going to be our own Certificate Authority and sign our own certificates. These commands are dependent on /etc/ssl/openssl.cnf as supplied by Debian. We start by making a small change to /etc/ssl/openssl.cnf. We make it so by default our certificates are good for 10 years instead of 1:
sed -i 's/= 365\t/= 3653\t/' /etc/ssl/openssl.cnf
grep 365 /etc/ssl/openssl.cnf


We will set up a common place to put our certificates:
cd /root
mkdir CA
cd CA
mkdir demoCA
cd demoCA
mkdir newcerts
mkdir private
echo '01' > serial
touch index.txt
cd ..


Create a Root Certificate:
openssl req -new -x509 -extensions v3_ca -keyout demoCA/private/cakey.pem -out cacert.pem -days 7320

Enter a passphrase when prompted. You will need this passphrase in the future. What I mean is: make it unique and don't ever loose it. You will be asked questions. Sample answers follow. Be sure to use the full name for your state or province name and the Common Name should be something that describes you as an authority (like Widgits Inc. RootCA):
Country Name US
State or Province Name Utah
Locality Name Salt Lake City
Organisation Name Widgits Inc.
Organisational Unit Name WebMail Server
Common Name (eg, YOUR name) Widgits Inc. RootCA
Email Address postmaster@example.com


This process produces two files as output: a private key in demoCA/private/cakey.pem and a root CA certificate in cacert.pem. Any and all key files we produce will need to be protected from unauthorized persons reading them, and must not be lost for the next 20 years. Also realize that the CA you created can sign any number of certificates (until it expires 20 years from now) so you only need to (or want to) create the CA once. We will copy our cert and our key to something more descriptive:
cd CA
cp -i demoCA/private/cakey.pem demoCA/private/cakey.example.com.pem
chmod 600 demoCA/private/*
cp -i cacert.pem cacert.example.com.pem
cp -i cacert.pem cacert.example.com.crt


We will copy the root CA certificate to the web server so we can install it on clients by having them browse to it:
cp -i cacert.example.com.crt /var/www/
chmod 0644 /var/www/cacert.example.com.crt


We copy the root CA certificate to /usr/share/ca-certificates/self:
mkdir /usr/share/ca-certificates/self
cp -i cacert.example.com.crt /usr/share/ca-certificates/self/


Now, run dpkg-reconfigure ca-certificates, answer yes to "Trust new certificates from certificate authorities?" and then scroll down to self/cacert.example.com.crt, use the spacebar to select it, Tab to Ok, and press Enter to finish the job. This will create a sym-link to our CA certificate in /etc/ssl/certs:
dpkg-reconfigure ca-certificates

The cacert.example.com.pem and cacert.example.com.crt are copies of our certificate and are the files that can be distributed and installed on the client's machines. Windows clients would use the .crt file. On my Windows 2000 system, double clicking this file would install it in Internet Explorer (which is exactly what want). Simply browsing to our server will give us the opportunity to install the web server certificate we will create (this will be the Common Name msa.example.com) but this is not the same as installing the CA certificate in the Trusted Root Certification Authorities store (seen as the Common Name you entered above). Just in case you are not familiar, in IE6 it's Tools->Internet Options->Content->Certificates->Trusted Root Certification Authorities. Outlook and Outlook Express use the same certificate store as Internet Explorer. In Mozilla Thunderbird it's Tools->Options->Privacy->Security->View Certificates->Authorities. In Firefox it's Tools->Options->Advanced->Encryption->View Certificates->Authorities->Import. If you go through this process more than once while testing, don't install duplicate certificates. Delete any old 'test' certificate you previously installed before adding your new one that replaces it. In my old version of The Bat! I add a new contact in the "Trusted Root CA" section of the address book and import the certificate from there. I suggest using WinSCP to transfer the cacert.example.com.crt certificate to your machine. As an possible alternative to manually adding this CA certificate to various clients, we have placed the certificate in our web server's root directory. Once Apache is up and running we should be able to browse to http://msa.example.com.com/cacert.example.com.crt and simply open (run) the file. I think the worst part of getting this server set up is getting the CA certificates installed on the clients. Sometimes it's worth it to buy a certificate from a well known commercial CA that is already in the Trusted Root Certification Authorities store.

OK, we are a certificate authority. We have the ability to sign our own certificates. We are now going to create a request for a certificate from the CA (which is us - but could be a commercial CA if you like). Everyone that connects to us will connect to the hostname of this machine. The Secure Web server, Secure IMAP server, Secure POP server and Postfix Secure SMTP server will all be msa.example.com, so the Common Name MUST BE our FQDN hostname when we create the request. The Organization name needs to be the same as the one in the CA cert. Do not enter your email address, challenge password or an optional company name when generating the CSR:
openssl req -new -nodes -out req.pem

Country Name US
State or Province Name Utah
Locality Name Salt Lake City
Organisation Name Widgits Inc.
Organisational Unit Name WebMail Server
Common Name (eg, YOUR name) msa.example.com
Email Address


This process produces two files as output, a private key in privkey.pem and a certificate signing request in req.pem. These files should be kept. The private key is of course necessary for SSL encryption. We will make backup copies of these files with more descriptive names:
cp -i privkey.pem privkey.msa.example.com.pem
chmod 600 privkey.*
cp -i req.pem req.msa.example.com.pem


Sign the Certificate (you will be asked for the pass phrase):
openssl ca -out cert.pem -cert cacert.pem -infiles req.pem

This process updates the CA database and produces two files as output, a certificate in cert.pem and a copy of the certificate in demoCA/newcerts/ named xx.pem, where xx is the serial number. We will copy the cert to a more descriptive name. The certificate has both the encoded version and a human-readable version in the same file. We want to strip off the human-readable portion as follows:
mv -i cert.pem temp.cert.msa.example.com.pem
openssl x509 -in temp.cert.msa.example.com.pem -out cert.pem
cp -i cert.pem cert.msa.example.com.pem


Postfix and Dovecot will want the cert and the key in two separate files, apache2 will want the two combined (but can use two separate files if configured to do so).
cat privkey.msa.example.com.pem cert.msa.example.com.pem >key-cert.pem
cp -i key-cert.pem key-cert.msa.example.com.pem
chmod -R 600 /root/CA


After those steps, you have three installable components (and some more descriptive backup copies):
A private key in privkey.pem (with a copy in privkey.msa.example.com.pem)
A certificate in cert.pem (with a copy in cert.msa.example.com.pem)
A combined private key and certificate in key-cert.pem (with a copy in key-cert.msa.example.com.pem)

Now give a copy of the combined certificate to Apache2. We will configure msa.example.com in Apache2 to use /etc/apache2/key-cert.msa.example.com.pem:
/etc/init.d/apache2 stop
/etc/init.d/apache2 start

cd /root/CA/
cp key-cert.msa.example.com.pem /etc/apache2/
chmod 600 /etc/apache2/key-cert.msa.example.com.pem


Now we will configure Apache2. First enable the SSL module, and we will also enable the rewrite module so we can optionally redirect port 80 requests to port 443:
a2enmod ssl
a2enmod rewrite


We will make a copy of the default site. This copy will be used for configuration of the SSL site:
cp /etc/apache2/sites-available/default /etc/apache2/sites-available/ssl

Now edit /etc/apache2/sites-available/default:
vi /etc/apache2/sites-available/default

And change:
<VirtualHost *:80>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www/
To:
<VirtualHost *:80>
        ServerAdmin webmaster@example.com
        ServerName msa.example.com
        DocumentRoot /var/www/
Now edit /etc/apache2/sites-available/ssl:
vi /etc/apache2/sites-available/ssl

And change:
<VirtualHost *:80>
        ServerAdmin webmaster@localhost

        DocumentRoot /var/www/
To:
<VirtualHost *:443>
        ServerAdmin webmaster@example.com
        ServerName msa.example.com
        SSLEngine on
        SSLCertificateFile /etc/apache2/key-cert.msa.example.com.pem
        DocumentRoot /var/www/
Once the files have been edited, enable the new site we called ssl, and restart Apache2:
a2ensite ssl
/etc/init.d/apache2 stop
/etc/init.d/apache2 start


If you did it like I did it, you should have no errors when it shuts down or starts up. Now I suggest either adding an entry in your personal computer's hosts file, e.g.:
111.111.111.111 msa.example.com
or creating an A record in your zone on the appropriate DNS server (which will have to be done at some point anyway). Now you should be able to browse to both: http://msa.example.com and https://msa.example.com. The default web page simply says It works!.

If you have not already installed the Root CA certificate on your computer, see if you can install it by downloading it via your browser at http://msa.example.com/cacert.example.com.crt. Newer versions of Windows may require that you manually select the "Trusted Root Certificate Authorities" store as shown in this example.

I have enabled port 80 only as a convenience. We don't want people to connect to our site without using SSL, so I am going to set up redirection. This is optional, but highly recommended. Edit /etc/apache2/sites-available/default once again:
vi /etc/apache2/sites-available/default

And insert these additional items in the location shown:
<VirtualHost *:80>
        RewriteEngine   on
        RewriteCond     %{SERVER_PORT} ^80$
        RewriteRule     ^(.*)$ https://%{SERVER_NAME}$1 [L,R]
        RewriteLog      "/var/log/apache2/rewrite.log"
        RewriteLogLevel 2
Then restart Apache2:
/etc/init.d/apache2 restart

Now, if you close down the browser window for http://msa.example.com and then browse to it again, you should be redirected to the SSL site. This is a bit wasteful however as twice as many processes are required to maintain both port 80 and port 443. If you like, you can optionally prevent Apache2 from starting up processes on port 80 by editing /etc/apache2/ports.conf and commenting out 'Listen 80' then restarting Apache2. Of course, then your users will always have to remember to use the https:// URL. BTW, personally I use lsof -P | grep LISTEN to show what ports are in use. Note that I am an Apache noob. If you want to host multiple http sites with multiple certificates, I leave it up to you to figure out how to do it. I can tell you that you can create and sign as many certificates as you want, just make sure you do not create a new certificate authority. The one and only certificate authority you created can sign all your certificates. And from what I understand, if more than one site uses SSL, each site will need a different IP address. Google is your friend. To be clear, you can host as many email domains as you need, but in my setup, I access all of them through one and only one http interface. Let's continue on...

Backup and replace Dovecot's certificate and key:
cd /root/CA/
mv /etc/ssl/certs/dovecot.pem /etc/ssl/certs/dovecot.pem.backup
cp cert.msa.example.com.pem /etc/ssl/certs/dovecot.pem
mv /etc/ssl/private/dovecot.pem /etc/ssl/private/dovecot.pem.backup
cp privkey.msa.example.com.pem /etc/ssl/private/dovecot.pem
chmod 0600 /etc/ssl/private/dovecot.pem
/etc/init.d/dovecot restart


Tell Postfix where to find the certificates (and set a couple other TLS parameters). We also make a backup of main.cf before we modify it for the first time:
cp -i /etc/postfix/main.cf /etc/postfix/main.cf-15may2011

postconf -e "smtpd_tls_cert_file = /etc/ssl/certs/dovecot.pem"
postconf -e "smtpd_tls_key_file = /etc/ssl/private/dovecot.pem"
postconf -e "smtpd_tls_CAfile = /etc/ssl/certs/cacert.example.com.pem"
postconf -e "smtpd_tls_received_header = yes"
postfix reload


Running some of these commands again will result in overwriting keys and certificates. That may not be good. Some files will necessarily be overwritten if additional certificates are requested, signed and created. That is expected, and is the reason we make host-specific copies of everything as we go along. Just be careful not to overwrite any host-specific files we have created. And remember, only one Root Certificate Authority needs creation. Make a backup of the session, both on and off the system (transfer the directory via WinSCP).
cp -r /root/CA /root/CA-15may2011



Note: If you create certificates for additional hosts and want to provide SSL for multiple hosts via the VirtualHost directive, it is my understanding you will need a separate IP address for each host: http://httpd.apache.org/docs/trunk/ssl/ssl_faq.html#vhosts. For example, I created a request for a new cert for mailzu.domain.tld. Then I signed it using our CA cert, stripped off the human readable portion, combined the key and certificate into a single file called key-cert.mailzu.domain.tld.pem and copied this certificate to the /etc/apache2 directory. Later we will install MailZu, which has a home directory of /var/www/mailzu. If you wanted to access /var/www/mailzu using a virtual host (using SSL) via https://mailzu.domain.tld, you would have to add another IP address. Here I show adding one to an existing interface that has the IP address 192.168.1.222 - (this is in /etc/network/interfaces):
auto eth0
iface eth0 inet static
        address 192.168.1.222
        netmask 255.255.255.0
        network 192.168.1.0
        broadcast 192.168.1.255
        gateway 192.168.1.1
		
auto eth0:0
iface eth0:0 inet static
        address 192.168.1.223
        netmask 255.255.255.0
        network 192.168.1.0
        broadcast 192.168.1.255
        gateway 192.168.1.1
Then I created a file called /etc/apache2/sites-available/mailzu with the contents:
<VirtualHost 192.168.1.223:443>
        DocumentRoot /var/www/mailzu
        ServerName mailzu.domain.tld
        SSLEngine on
        SSLCertificateFile /etc/apache2/key-cert.mailzu.domain.tld.pem
</VirtualHost>
I personally do not go this route (who has IP addresses to spare?), so I do not do this. I choose to access MailZu via https://msa.example.com/mailzu. This means I only have to deal with one host certificate (and one IP address). BTW, I reboot after making changes to /etc/network/interfaces.


Add our home page

Index Top Bottom
Download a home page. This page describes some of the features of the mail system to new users. Those features are not yet installed but we can install this page. Once it's in place, edit it (you may prefer the WinSCP editor) and do a search and replace on the items listed at the top of the page. No doubt once this entire system is installed you will do a lot more editing of this page:
cd /var/www
wget http://verchick.com/mecham/public_html/spam/virtual.index.html
cp index.html index.html-apache-original
cp virtual.index.html index.html
sed -i 's/Our Organization/Widgits Inc./' index.html
sed -i 's/somehost.domain.tld/msa.example.com/' index.html
sed -i 's/domain.tld/example.com/' index.html
chown root:www-data index.html


Browse to the server:
https://msa.example.com


Install and configure phpMyAdmin

Index Top Bottom
apt-get install phpmyadmin

Answer the prompts as follows:

Web server to reconfigure automatically: [apache2]
Configure database for phpmyadmin with dbconfig-common? [Yes]
Password of the database's administrative user: roots_password
MySQL application password for phpmyadmin: (leave this blank to generate a random password)

At this point our https://msa.example.com/phpmyadmin program is open for abuse. We will secure it by making the URL obscure, by setting a user name and password and by optionally (but highly recommended) only allowing chosen IP addresses to access the URL. Obscure the URL by changing its name:
sed -i 's/Alias \/phpmyadmin /Alias \/phpmyadmiNx /' /etc/apache2/conf.d/phpmyadmin.conf

With the current settings in /etc/apache2/apache2.conf, our files used for access control (files like .htpasswd) will not be used unless we tell apache2 to use them. We will modify the provided access control file, then change /etc/apache2/conf.d/phpmyadmin.conf so it uses it. The current user name is 'admin'. Let's begin by obscuring the user name:
sed -i 's/admin/myadmin_username/' /etc/phpmyadmin/htpasswd.setup

Then, create a password for that user:
htpasswd -c /etc/phpmyadmin/htpasswd.setup myadmin_username

New password: myadmin_password
Re-type new password: myadmin_password

Now we direct apache2 to use this access file by placing directives in /etc/apache2/conf.d/phpmyadmin.conf. Once this is in place, attempts to browse to the /phpmyadmiNx directory will be met with a login dialog box. Note that our version of /etc/apache2/apache2.conf contains the command "Include /etc/apache2/conf.d" so that any config files placed in this directory will be read. I am also going to illustrate limiting access to one single workstation (yours of course). This is optional, but recommended. If you need to allow access to a network of machines - use a partial IP address (e.g. 192.168), see http://httpd.apache.org/docs/2.2/howto/access.html:

vi /etc/apache2/conf.d/phpmyadmin.conf

and insert the following at the position shown, making sure the IP address is the IP address of your workstation (as the mailserver sees it). If you like, you can drop one or more of the last octets of the IP address in order to allow a wider range of hosts access to the phpMyAdmin login prompt. They will still have to get through two layers of security.
<Directory /usr/share/phpmyadmin/>

 order deny,allow
 deny from all
 allow from 666.666.666.666

 AuthUserFile /etc/phpmyadmin/htpasswd.setup
 AuthGroupFile /dev/null
 AuthName "phpMyAdmin"
 AuthType Basic
 Require valid-user
Save the file then restart apache2:
/etc/init.d/apache2 restart

and browse to phpMyAdmin:
https://msa.example.com/phpmyadmiNx

The first login is the phpMyAdmin user name and password: myadmin_username myadmin_password
The second login is your mysql login (at this point root and roots_password). You can close the phpMyAdmin web page.

Perform some preliminary Postfix configuration

Index Top Bottom
We want to explicitly set our domain name and host name in Postfix so there is no possibility Postfix finds something else. We also remove one line because we will turn TLS on or off in master.cf:
postconf -e "mydomain = example.com"
postconf -e "myorigin = example.com"
postconf -e "myhostname = msa.example.com"
postconf -e "mydestination = example.com, msa.example.com, localhost.example.com, localhost"
sed -i 's/smtpd_use_tls=yes//' /etc/postfix/main.cf
postfix reload


At this point, if you issue this command:
postconf -n

Postfix should show main.cf is configured something like this:
alias_database = hash:/etc/aliases
alias_maps = hash:/etc/aliases
append_dot_mydomain = no
biff = no
config_directory = /etc/postfix
inet_interfaces = all
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
mydestination = example.com, msa.example.com, localhost.example.com, localhost
mydomain = example.com
myhostname = msa.example.com
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
myorigin = example.com
readme_directory = no
recipient_delimiter = +
relayhost =
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
smtpd_tls_CAfile = /etc/ssl/certs/cacert.example.com.pem
smtpd_tls_cert_file = /etc/ssl/certs/dovecot.pem
smtpd_tls_key_file = /etc/ssl/private/dovecot.pem
smtpd_tls_received_header = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
Please note that 'postconf -n' does not necessarily show every setting we have in main.cf. Postfix should accept mail for the addresses listed in /etc/aliases and deliver it to a mailbox in /var/mail. You should test this. Configure an MUA to use this mail server as its outgoing SMTP server. I like to designate an MUA I'm not using at the moment for testing purposes. I sometimes will simply create a separate Outlook profile for use with testing.

For test purposes I typically I have several different MUAs (Outlook, Outlook Express, Thunderbird, The Bat!) set up on the system I am sending mail from. Do not send mail from the command line (using sendmail or other means) when testing this system. In all tests we perform I expect you to send test messages from an external client (unless of course we are testing something like SquirrelMail). Now send a message to postmaster@example.com. See what /var/log/mail.log said about the transaction (look for errors). It should also show you the user the message was delivered to.
tail -50 /var/log/mail.log

ls -l /var/mail


In the /var/mail directory you should see the mbox of the user the message was delivered to. You can ' more /var/mail/user ' to read the contents of the mbox.

Grab the Postfix source code (we need a few samples from it):
cd /usr/local/src
wget http://ftp.debian.org/debian/pool/main/p/postfix/postfix_2.7.1.orig.tar.gz
tar xzf postfix_2.7.1.orig.tar.gz


Make sure you answer "n" to "Overwrite?". Do the first command separately:
cp -i /usr/local/src/postfix-2.7.1/conf/* /etc/postfix

cp -i /etc/postfix/header_checks /etc/postfix/body_checks
cp -i /etc/postfix/access /etc/postfix/sender_access
cp -i /etc/postfix/access /etc/postfix/rbl_client_exceptions
cp -i /etc/postfix/access /etc/postfix/rbl_sender_exceptions
cp -i /etc/postfix/access /etc/postfix/rbl_recipient_exceptions

cp /usr/local/src/postfix-2.7.1/examples/chroot-setup/LINUX2 /usr/sbin
chmod +x /usr/sbin/LINUX2
LINUX2


On Debian, Postfix runs chrooted. The LINUX2 script is used to copy files to the chroot jail.

Install SquirrelMail software

and a few other things
Index Top Bottom
We just need the software in place at this time.
apt-get install squirrelmail squirrelmail-locales maildrop

apt-get install sudo php-pear php5-mysql php5-gd php5-cli php5-common php5-mcrypt

apt-get install php-db php-net-socket php-log php-net-smtp php5-imap



Install and configure PostfixAdmin and deliver

Index Top Bottom
Add our virtual user and group:
groupadd vmail -g 6060
useradd vmail -u 6060 -g 6060


And create the directory where our mail will be stored:
mkdir /var/vmail
chown -R vmail:vmail /var/vmail
chmod -R 700 /var/vmail


Install PostfixAdmin 2.3.3, and put the package on hold. You must use this version. You do not want to upgrade this package. We are going to install a few patches to this software. If you upgrade, you would have to patch again (and the existing patches may not work):
cd /usr/local/src
wget http://cdnetworks-us-2.dl.sourceforge.net/project/postfixadmin/postfixadmin/postfixadmin-2.3.3/postfixadmin-2.3.3_all.deb

If the package fails to download, see http://sourceforge.net/projects/postfixadmin/files/postfixadmin/. Or try:
wget http://softlayer.dl.sourceforge.net/sourceforge/postfixadmin/postfixadmin-2.3.3_all.deb

When installing PostfixAdmin, be prepared to answer the dialogs as shown below.
dpkg -i postfixadmin-2.3.3_all.deb

Web server to reconfigure automatically: [apache2]
Configure database for postfixadmin with dbconfig-common? [No]
What do you want to do about modified configuration file config.inc.php? keep the local version currently installed

We need to create the initial 'postfix' database in MySQL.
First log in to MySQL using root's password: roots_password:
mysql -p

Then create the database and user:
CREATE DATABASE postfix;
CREATE USER 'postfix'@'localhost' IDENTIFIED BY 'pfix_password';
GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, INDEX, ALTER, DROP ON `postfix`.* TO 'postfix'@'localhost';
FLUSH PRIVILEGES;
QUIT
echo "postfixadmin hold" | dpkg --set-selections
cd /usr/share/postfixadmin
cp config.inc.php config.inc.php.original
sed -i "s/configured'] = false/configured'] = true/" config.inc.php
sed -i "s|admin_url'] = ''|admin_url'] = 'https://msa.example.com/postFixadminx'|" config.inc.php
sed -i "s/database_type'] = '_DBC_DBTYPE_'/database_type'] = 'mysqli'/" config.inc.php
sed -i "s/database_host'] = '_DBC_DBSERVER_'/database_host'] = 'localhost'/" config.inc.php
sed -i "s/database_user'] = '_DBC_DBUSER_'/database_user'] = 'postfix'/" config.inc.php
sed -i "s/password'] = '_DBC_DBPASS_'/password'] = 'pfix_password'/" config.inc.php
sed -i "s/database_name'] = '_DBC_DBNAME_'/database_name'] = 'postfix'/" config.inc.php
sed -i 's/postmaster@change-this-to-your.domain.tld/postmaster@example.com/' config.inc.php
sed -i 's/abuse@change-this-to-your.domain.tld/abuse@example.com/' config.inc.php
sed -i 's/hostmaster@change-this-to-your.domain.tld/hostmaster@example.com/' config.inc.php
sed -i 's/postmaster@change-this-to-your.domain.tld/postmaster@example.com/' config.inc.php
sed -i 's/webmaster@change-this-to-your.domain.tld/webmaster@example.com/' config.inc.php
sed -i 's/autoreply.change-this-to-your.domain.tld/autoreply.example.com/' config.inc.php
sed -i 's|to change-this-to-your.domain.tld|to https://msa.example.com/postFixadminx|' config.inc.php
sed -i 's|http://change-this-to-your.domain.tld/main|https://msa.example.com/postFixadminx/users/main.php|' config.inc.php
sed -i 's|http://change-this-to-your.domain.tld|https://msa.example.com/postFixadminx|' config.inc.php
sed -i "s/domain_path'] = 'NO/domain_path'] = 'YES/" config.inc.php
sed -i "s/domain_in_mailbox'] = 'YES/domain_in_mailbox'] = 'NO/" config.inc.php
sed -i "s/mailboxes'] = '10'/mailboxes'] = '300'/" config.inc.php
sed -i "s/aliases'] = '10'/aliases'] = '300'/" config.inc.php
sed -i "s/maxquota'] = '10'/maxquota'] = '500'/" config.inc.php
sed -i "s/quota'] = 'NO/quota'] = 'YES/" config.inc.php
sed -i "s/new_quota_table'] = 'NO/new_quota_table'] = 'YES/" config.inc.php
sed -i 's/EHLO/HELO/' functions.inc.php
You should vi config.inc.php and browse through it to familiarize yourself with all the possible settings and to make sure your domain name was properly updated. Now create an .htaccess password for the admin url (user name will be pfadmin_username). Assign the password pfadmin_password

cd /usr/share/postfixadmin
htpasswd -c .htpasswd pfadmin_username


Now tell apache2 to use the file. We also limit admin login access to our own workstation, but you can add more IP addresses (or networks) if needed. If you wanted to add a class C network for example, this would be in the form 192.168.0 and a class B network would be in the form 172.16. Access by administrators to https://msa.example.com/postFixadminx/login.php is controlled by the IP address(es) of the client and the .htaccess user name and password.

vi /etc/postfixadmin/apache.conf

and insert this phrase in the position shown. Don't forget to edit the ip address if you have not already done so and remember that comments must left justified:
Alias /postfixadmin /usr/share/postfixadmin

<Directory /usr/share/postfixadmin/>
  <Files ~ "login.php">
    Order Deny,Allow
    Deny from All
    Allow from 666.666.666.666
    AuthUserFile /usr/share/postfixadmin/.htpasswd
    AuthGroupFile /dev/null
    AuthName "Postfix Admin"
    AuthType Basic
    Require valid-user
  </Files>
</Directory>

<Directory /usr/share/postfixadmin/users/>
  <Files ~ "login.php">
    Order Allow,Deny
    Allow from All
    Satisfy Any
  </Files>
</Directory>

# END FOR POSTFIXADMIN
Just like phpMyAdmin, we obscure the postfixadmin URL:
sed -i s'|Alias /postfixadmin|Alias /postFixadminx|' /etc/postfixadmin/apache.conf

Restart apache2:
/etc/init.d/apache2 restart

When we add a user to postfixadmin we also need to create a profile in squirrelmail. Additionally, we want to be able to both create, and manipulate the quota limit when necessary. These two patches allow us to do so:
cd /usr/share/postfixadmin/
wget http://verchick.com/mecham/public_html/spam/edit-mailbox.patch.v3.txt
wget http://verchick.com/mecham/public_html/spam/create-mailbox.v3.patch.txt

cp create-mailbox.php create-mailbox.php.original
cp edit-mailbox.php edit-mailbox.php.original
patch -p0 < edit-mailbox.patch.v3.txt
patch -p0 < create-mailbox.v3.patch.txt

cd /usr/sbin
wget http://verchick.com/mecham/public_html/spam/mail_profile.sh.txt
mv mail_profile.sh.txt mail_profile.sh
chmod +x mail_profile.sh
wget http://verchick.com/mecham/public_html/spam/quotaedit.sh.txt
mv quotaedit.sh.txt quotaedit.sh
chmod +x quotaedit.sh


In order to allow the www-data user to run these scripts, you must run:
visudo

and on the bottom of the page add this stuff (notice our hostname):
www-data msa=NOPASSWD: /usr/sbin/mail_profile.sh
www-data msa=NOPASSWD: /usr/sbin/quotaedit.sh


This is another patch that solves a problem where items on the drop down menu disappear. This simply adds a few breaks at the right place.
cd /usr/share/postfixadmin/templates
wget http://verchick.com/mecham/public_html/spam/postfixadmin.menu.patch.v3.txt
cp menu.php menu.php.original
patch -p0 < postfixadmin.menu.patch.v3.txt


Assuming everything is OK, click on this link:
https://msa.example.com/postFixadminx/setup.php
Enter a setup password (twice) and "Generate password hash". Now per the instructions,
vi /usr/share/postfixadmin/config.inc.php and edit the setup_password configuration variable as illustrated. As soon as you make the change, you will be able to enter the setup password and add yourself as the Admin. You are not adding a mailbox for yourself here, just a login. Running this script creates the initial database structure. Close this web page once the Admin has been created. We will now change the tables to InnoDB, delete a redundant index and add a field where a clear text password may be entered (needed if cram-md5 is used):
cd /usr/share/postfixadmin
wget http://verchick.com/mecham/public_html/spam/convert.postfixadmin.tables.sql.v2211
mysql -u root -p < convert.postfixadmin.tables.sql.v2211
Enter roots_password in order to complete the last command. We do not want to visit setup.php again:
mv setup.php setup.php.txt

Now we can log in as Super Admin. https://msa.example.com/postFixadminx. We still have more Postfix configuration to do before we can test mail delivery however. Get past the apache security with pfadmin_username and pfadmin_password. Click on Domain List->New Domain and add the primary domain (example.com). Leave the optional "Add default mail aliases:" and "Mail server is backup MX:" boxes unchecked. All domains you add should have either an A record or MX record in DNS in order for PostfixAdmin to consider them valid. There is a setting in config.inc.php that allows you to disable this requirement however (emailcheck_resolve_domain). Add any other domains you need. Only add domains that will store mail locally (we will talk about relay domains later). If you click on Admin List->New Admin you can add 'Normal' admins that have control only of the domains you assign to them, as opposed to having complete control of all domains.

You should add an additional normal admin login for yourself that you will use on a day to day basis. Normal admins see different menu options. The https://msa.example.com/postFixadminx link will be used by administrators. Users can change their forwarding information and password at https://msa.example.com/postFixadminx/users; however, they may not need to once we add a SquirrelMail plugin.

Once you have created at least one domain, the next step is to Add Mailbox for yourself and the one Unix user you currently have on your system (if it is different than you). If logged in as Super Admin, you may have to click on 'Domain List', select a domain, then click the 'Add Mailbox' link at the bottom of the page. This can be an issue if you have a lot of users because you would have to scroll to the bottom of the page to get to the 'Add Mailbox' link. If you are not a Super Admin, you could simply click on 'Main' and then 'Add Mailbox'. The "Active:" and "Create mailbox:" check boxes should of course be checked. Notice that Username: is only the local part; you choose the @domain from the drop down box. Create the username in lower case. This is your IMAP (and/or POP3) account. It is imperative that our mail_profile.sh script runs correctly when a mailbox is created in postfixadmin, otherwise the user will have errors when logging into SquirrelMail, they will not be subscribed to the Spam folder, and quotas will not work. Note that on a 32-bit system, the biggest number you can enter in the Quota field that will actually work is 2048 (2GB). Check that a mailbox exists, and a squirrelmail profile was created:
ls -l /var/vmail/example.com
ls -l /var/lib/squirrelmail/data


Note that if you delete a mailbox from postfixadmin, as a safety precaution we will NOT automatically delete the user's maildir (or mail). You will have to manually remove it. You should add another regular user "test@example.com" to use when you need to test sending and receiving messages as a normal user (not related to any administrator accounts). At this point we still need to configure Postfix to send mail to our virtual mailboxes using the information stored in the postfixadmin databases. Begin by downloading and modifying Postfix data access configuration files:
cd /etc/postfix
wget http://verchick.com/mecham/public_html/spam/mysql_virtual_alias_maps.cf
wget http://verchick.com/mecham/public_html/spam/mysql_virtual_domains_maps.cf
wget http://verchick.com/mecham/public_html/spam/mysql_virtual_mailbox_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_virtual_alias_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_virtual_domains_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_virtual_mailbox_maps.cf
chmod 640 mysql_*
chown root:postfix mysql_*


We need to remove our domain name from $mydestination because our domain will soon be listed as a virtual mailbox domain - and you cannot have a domain in more than one address class:
cp /etc/postfix/main.cf /etc/postfix/main.cf-domain
postconf -e "mydestination = msa.example.com, localhost.example.com, localhost"


Now tell Postfix to use our MySQL data files (and Dovecot's deliver - which still needs configuration):
touch /etc/postfix/virtual
postmap /etc/postfix/virtual
cp /etc/postfix/virtual /etc/postfix/amavis_virtual
postmap /etc/postfix/amavis_virtual
postconf -e "virtual_minimum_uid = 6060"
postconf -e "virtual_gid_maps = static:6060"
postconf -e "virtual_uid_maps = static:6060"
postconf -e "virtual_alias_maps = proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf, hash:/etc/postfix/virtual"
postconf -e "virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf"
postconf -e "virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf"
postconf -e "virtual_transport = dovecot"
postconf -e "virtual_mailbox_base = /var/vmail/"
postconf -e "dovecot_destination_concurrency_limit = 2"
postconf -e "dovecot_destination_recipient_limit = 1"


Note that I use proxy:mysql: here (proxymap(8)). When proxymap(8) is used, changes to the MySQL tables may not be recognized immediately. During testing you may want to remove proxy: because it may cause frustration and confusion. Alternately, reload Postfix so changes are recognized sooner.

To configure dovecot 'deliver', first get a global.sieve file from me that contains instructions to deliver spam to the user's Spam folder. Also, we need to create a dovecot-deliver.log file so we can see where 'deliver' delivers mail:
mkdir /var/sieve
cd /var/sieve
wget http://verchick.com/mecham/public_html/spam/global.sieve.txt
mv global.sieve.txt global.sieve
chown -R vmail:vmail /var/sieve
chmod -R 0700 /var/sieve
touch /var/log/dovecot-deliver.log
chown vmail:vmail /var/log/dovecot-deliver.log
cd /etc/logrotate.d/
wget http://verchick.com/mecham/public_html/spam/deliver.logrotate.txt
mv deliver.logrotate.txt dovecot-deliver


Now we need to edit the dovecot entry in master.cf. We will also make a backup of the original master.cf:
cp -i /etc/postfix/master.cf /etc/postfix/master.cf-15may2011

vi /etc/postfix/master.cf


Locate the current maildrop transport then insert the dovecot transport as shown:
maildrop  unix  -       n       n       -       -       pipe
  flags=ODRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
  
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/lib/dovecot/deliver -d ${recipient} -e
Then restart Postfix:
/etc/init.d/postfix restart

We need to make quite a few changes to /etc/dovecot/dovecot.conf and /etc/dovecot/dovecot-sql.conf to get them to work in our current environment, so rather than edit them, get new ones from me:
cd /etc/dovecot
mv dovecot.conf dovecot.conf.original
wget http://verchick.com/mecham/public_html/spam/dovecot.conf.1.2.txt
mv dovecot.conf.1.2.txt dovecot.conf
sed -i 's/postmaster@example.tld/postmaster@example.com/' dovecot.conf
chmod 0644 dovecot.conf
mv dovecot-sql.conf dovecot-sql.conf.original
wget http://verchick.com/mecham/public_html/spam/dovecot-sql.conf.txt
mv dovecot-sql.conf.txt dovecot-sql.conf
sed -i 's/password=password/password=pfix_password/' dovecot-sql.conf
chmod 0600 dovecot-sql.conf
wget http://verchick.com/mecham/public_html/spam/dovecot-crammd5.conf.txt
mv dovecot-crammd5.conf.txt dovecot-crammd5.conf
sed -i 's/password=password/password=pfix_password/' dovecot-crammd5.conf
chmod 0600 dovecot-crammd5.conf
/etc/init.d/dovecot restart


If you would like to see what these configuration files look like without all the comments. Note that the actual files on our system have been customized by the sed statements above:
/etc/dovecot/dovecot.conf dovecot.conf.1.2.plain.txt
/etc/dovecot/dovecot-sql.conf dovecot-sql.conf.plain.txt
/etc/dovecot/dovecot-crammd5.conf dovecot-crammd5.conf.txt
/var/sieve/global.sieve global.sieve.txt

Let's simply start off clean, we need to make certain everything works after a reboot anyway:
reboot
exit


Once the system comes back up, log in and:
tail -f /var/log/mail.log

Now we will send a message through the system to see if it lands in our maildir. You should already have an MUA set up to use this server as its outgoing server. You should also now be able to configure it to connect to the IMAP server using your username (full email address) and password. By default, Dovecot will not allow you to authenticate with the IMAP server by passing a plain text password over a network, so you will need to configure your MUA to use SSL (TLS) on port 993 (secure IMAP). However, we have not yet configured Postfix to use SASL, so you would want port 25 as your SMTP (outgoing) port. Note that if your mail client is using CRAM-MD5 for authentication, it will fail because there is currently no password in the 'clear' field of the 'mailbox' table in the 'postfix' database for any users. I suggest that initial tests are not done using a client that is using CRAM-MD5, but if you must, then you may want to use https://msa.example.com/phpmyadmiNx and add a clear text password for the user you are testing. Assuming you have successfully created a maildir for yourself (via the PostfixAdmin interface), send a message to yourself and see if you get it. Tail the mail.log file as the message goes through. Also: grep fatal /var/log/mail.log
Success should look like:

Jan 3 14:02:08 msa postfix/pipe[12517]: C2506E99C5: to=<garyv@example.com>, relay=dovecot, delay=0.19, delays=0.11/0.02/0/0.06, dsn=2.0.0, status=sent (delivered via dovecot service)

If you have an error, you must fix it before you continue. We have made a lot of changes to a lot of files. It is certainly possible something happened along the way that would prevent proper delivery of a message. You should have an understanding of the files involved. You just have to find the incorrect setting(s). Some familiarity with Postfix would really be handy. Basic stuff like familiarity with the mailq, postsuper, qshape and postqueue commands. Also remember that you have phpMyAdmin to browse the database (and possibly make manual changes). You should browse the sql database 'postfix' to get familiar with the structure of the data. You will also want to log into https://msa.example.com/postFixadminx and set up the account for the test@example.com user if you have not already done so and send a message to that address, then check that it was received into the maildir:
ls -al /var/vmail/example.com/test/new

cat /var/log/dovecot-deliver.log


If you can deliver mail to a user, it would be a good idea to familiarize yourself with PostfixAdmin at this time. Add some aliases, add some domains. Play with the software. Postfix will no longer use /etc/aliases for our virtual domains so:

You will need to make aliases (or mailboxes) for root, abuse, postmaster, webmaster, clamav and logcheck @example.com.

root@example.com should be an alias of a real mailbox, it should not be a real mailbox itself. If you fail to make aliases or mailboxes for recipients of system generated mail, dovecot may bounce the messages, or you may end up with stray messages in /var/mail/. It would be a good idea to see what you have there now.

Set up your MUA to retrieve mail from the mailserver via IMAP SSL (TSL) on port 993. If you must use POP3, use POP3 SSL (TLS) on port 995. POP3 clients will only be able to retrieve mail from the /new folder which means they will never see their Spam folder unless they also use SquirrelMail. By default, unsecured plaintext passwords on ports 110 (POP3) and 143 (IMAP) are not permitted. If you must use standard POP3 or IMAP, then you will have to add the setting "disable_plaintext_auth = no" in dovecot.conf. Some clients may wish to use CRAM-MD5 to authenticate. If so, you will have to fire up phpMyAdmin and place a cleartext password in the 'clear' field for those users. Hopefully you are using my database schema with the 'clear' field added. Install the CA certificate we created earlier on the test client if you have not already done so. Not having the root certificate properly installed will cause all kinds of grief. Remember that you should be able to download it from http://msa.example.com/cacert.example.com.crt. Keep in mind that if you created and installed a root certificate on your PC earlier as a test, and have no intention of actually using it, you need to remove it from your PC before installing the newest version in order to avoid problems with duplicate certificates of the same name.

There are (contributed) scripts that come with PostfixAdmin in the /usr/share/doc/postfixadmin/examples directory that are used to delete orphaned maildirs (mailboxes you delete in PostfixAdmin, but the files remain on the system). As mentioned earlier, I do not automatically delete maildirs when a user is removed. I personally would leave orphaned files alone for some length of time. One of the provided scripts would delete all your mail if you let it. That can't be good. One of the scripts (cleanupdirs.pl) appears to be Ok, but I'm still not going to allow it do delete maildirs; I'm only going to use it to report orphaned ones. I'm also going to rename it maildircheck.

zcat /usr/share/doc/postfixadmin/examples/cleanupdirs.pl.gz > /usr/sbin/maildircheck

touch /var/log/maildircheck.log
sed -i "s|/home/vmail|/var/vmail|" /usr/sbin/maildircheck
sed -i "s/removed_maildirs.log/maildircheck.log/" /usr/sbin/maildircheck
sed -i "s/someuser/postfix/" /usr/sbin/maildircheck
sed -i "s/somepass/pfix_password/" /usr/sbin/maildircheck
sed -i "s/      rmtree/#     rmtree/" /usr/sbin/maildircheck
sed -i "s/'\$to_delete'/\$to_delete/" /usr/sbin/maildircheck
sed -i "s/Need to delete/Orphaned/" /usr/sbin/maildircheck
chmod 700 /usr/sbin/maildircheck

cd /etc/logrotate.d
wget http://verchick.com/mecham/public_html/spam/maildircheck.txt
mv maildircheck.txt maildircheck


Note that in the code above, the 'rmtree' line is commented out in the file. This is the important part. To test, you need to delete a test user via the PostfixAdmin interface. Then run:
maildircheck

If we delete the test@example.com account, then run maildircheck, look and see (and make sure) that the mail directory was NOT deleted. For example, run:
ls -l /var/vmail/example.com/test
Then add the test user back in. As you can see, if the mail is not deleted, then it's possible to regain access to the mailbox by adding the user back in. This will be an issue when it's not actually the same person. If you actually wanted to use this script to delete the directories, you would have to uncomment the 'rmtree' line. I hope the script works and you don't end up deleting every mailbox on the system.

Now I have an optional script that will automatically delete messages left in each user's Spam folder that are older than 24 days. As with this entire document, USE ENTIRELY AT YOUR OWN RISK. You could test it (a few days from now) by temporarily modifying the number of days (-mtime 0 = 1 day).
cd /etc/cron.daily
wget http://verchick.com/mecham/public_html/spam/rmmailboxspam.txt
mv rmmailboxspam.txt rmmailboxspam
chmod +x rmmailboxspam


I like to turn off the ability of the super administrators to delete domains. This is optional. This removes the link to 'del' the domain:
sed -i "s|$PALANG\['del'\]|$PALANG\[''\]|" /usr/share/postfixadmin/templates/admin_list-domain.php

To turn it back on (if you actually do need to delete a domain - along with all its mailboxes and aliases):
sed -i "s|$PALANG\[''\]|$PALANG\['del'\]|" /usr/share/postfixadmin/templates/admin_list-domain.php


Configure SASL and TLS

Index Top Bottom
Tell Postfix to use SASL AUTH (we make a backup copy of main.cf should you need to refer to it):
cp -i /etc/postfix/main.cf /etc/postfix/main.cf-before-sasl
postconf -e "smtpd_sasl_type = dovecot"
postconf -e "smtpd_sasl_path = private/auth"
postconf -e "broken_sasl_auth_clients = yes"
postconf -e "smtpd_sasl_auth_enable = yes"
postconf -e "smtpd_sasl_local_domain = \$myhostname"
postconf -e "smtpd_sasl_security_options = noanonymous"
postconf -e "smtpd_sasl_authenticated_header = yes"
postconf -e "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination"


We need to configure master.cf. You may want to use the WinSCP editor for this. You want to replace everything between the two grayed out lines:
vi /etc/postfix/master.cf
smtp      inet  n       -       -       -       -       smtpd
  -o smtpd_sasl_auth_enable=no
# -o content_filter=smtp-amavis:[127.0.0.1]:10024
# -o receive_override_options=no_address_mappings
# If they want to relay, make them use port 587 (submission) or port 465 (smtps)
# If using submission port, configure client to use CRAM-MD5
submission inet n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=may
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
# -o content_filter=smtp-amavis:[127.0.0.1]:10026
# -o receive_override_options=no_address_mappings  
# Outlook and OE (and others) expect smtpd_tls_wrappermode,
# so have them submit here (port 465):
smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
# -o content_filter=smtp-amavis:[127.0.0.1]:10026  
# -o receive_override_options=no_address_mappings
# We will use port 4650 for clients that use STARTTLS:
4650     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
# -o content_filter=smtp-amavis:[127.0.0.1]:10026  
# -o receive_override_options=no_address_mappings
#628      inet  n       -       -       -       -       qmqpd
Then of course:
/etc/init.d/postfix restart

FYI, at this point output from 'postconf -n' looks like:
alias_database = hash:/etc/aliases
alias_maps = hash:/etc/aliases
append_dot_mydomain = no
biff = no
broken_sasl_auth_clients = yes
config_directory = /etc/postfix
inet_interfaces = all
mailbox_command = procmail -a "$EXTENSION"
mailbox_size_limit = 0
mydestination = msa.example.com, localhost.example.com, localhost
mydomain = example.com
myhostname = msa.example.com
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
myorigin = example.com
readme_directory = no
recipient_delimiter = +
relayhost =
smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU)
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination
smtpd_sasl_auth_enable = yes
smtpd_sasl_authenticated_header = yes
smtpd_sasl_local_domain = $myhostname
smtpd_sasl_path = private/auth
smtpd_sasl_security_options = noanonymous
smtpd_sasl_type = dovecot
smtpd_tls_CAfile = /etc/ssl/certs/cacert.example.com.pem
smtpd_tls_cert_file = /etc/ssl/certs/dovecot.pem
smtpd_tls_key_file = /etc/ssl/private/dovecot.pem
smtpd_tls_received_header = yes
smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
virtual_alias_maps = proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf, hash:/etc/postfix/virtual
virtual_gid_maps = static:6060
virtual_mailbox_base = /var/vmail/
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf
virtual_mailbox_maps = proxy:mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf
virtual_minimum_uid = 6060
virtual_transport = dovecot
virtual_uid_maps = static:6060
Please note that 'postconf -n' does not show every setting we have in main.cf.

Configure an Outlook Express IMAP account to require authentication and use SMTPS (AKA SSMTP) port 465 for outgoing, and IMAPS (secure IMAP) port 993 for incoming. Similar to this. Most other clients will use 465 also. Clients that can't use a dedicated TLS port (Postfix offers this via smtpd_tls_wrappermode=yes) may use port 4650 and instead use STARTTLS. Port 587 will be used for clients unable to get TLS working and will instead use CRAM-MD5 to encrypt the cleartext password. There is an issue with Mozilla Thunderbird. It tries to use CRAM-MD5 before it tries PLAIN. This will result in a "SASL authentication failure: empty secret" warning unless you enter a cleartext password for these clients. If you don't wish to do that, you can either fix Thunderbird, or not use CRAM-MD5. To fix Thunderbird, go to Tools->Options->Advanced->General->Config Editor and then double click on mail.smtpserver.default.trySecAuth (which is set to true by default) in order to set it to false. Alternately, when setting up the account's Server settings, you can simply uncheck "Use secure authentication".


Install and configure amavisd-new

Index Top Bottom
Here we install amavisd-new, SpamAssassin, ClamAV, Razor and Pyzor. Then we add the clamav user to the amavis group. We then will actually remove the Debian Amavisd-new package and install amavisd-new from source. I do this because there were a number of bugs in version 2.6.4 and this also makes it easier to upgrade or patch amavisd-new in the future:
apt-get update

apt-get install amavisd-new spamassassin


The Debian squeeze package of amavisd-new is somewhat outdated. Personally I find it much easier to stay current with amavisd-new if I migrate away from the Debian package. With the instructions provided we will still continue to use the Debian style configuration files. Once this is set up, we no longer have to rely on the Debian package maintainers to produce updated versions of amavisd-new. Amavisd-new is actively developed by Mark Martinec and new versions come out often. Log in as root. Before we remove amavisd-new we make backup copies of our important files. You should only perform this operation once (to avoid overwriting the original backup). When the amavisd-new package is removed, the /var/lib/amavis and /etc/amavis directories should not be removed (and I do not make a backup copy of /var/lib/amavis). If you have items in /var/lib/amavis that are important to you, you may want to consider making a copy of that directory (and subdirectories):
mkdir /usr/ambackup
mkdir -p /usr/ambackup/etc/init.d/
cp /etc/init.d/amavis /usr/ambackup/etc/init.d/amavis
cp -r /etc/amavis /usr/ambackup
mkdir /usr/ambackup/etc/cron.daily
cp /etc/cron.daily/amavisd-new /usr/ambackup/etc/cron.daily/amavisd-new
mkdir /usr/ambackup/etc/cron.d
cp /etc/cron.d/amavisd-new /usr/ambackup/etc/cron.d/amavisd-new
mkdir -p /usr/ambackup/usr/sbin
cp /usr/sbin/amavisd-new-cronjob /usr/ambackup/usr/sbin/amavisd-new-cronjob
mkdir -p /usr/ambackup/usr/share/amavis
cp -r /usr/share/amavis/conf.d /usr/ambackup/usr/share/amavis
cp /usr/sbin/amavisd-new /usr/ambackup/usr/sbin/amavisd-new
mkdir /usr/ambackup/usr/share/perl5
cp /usr/share/perl5/JpegTester.pm /usr/ambackup/usr/share/perl5/JpegTester.pm


Of the files above, JpegTester.pm, /usr/sbin/amavisd-new and /usr/share/amavis/conf.d/ should get deleted when amavisd-new is removed. We will need to restore those from the backups. There may be other files elsewhere that get deleted, but they do not affect how amavisd-new functions, so their absence will not hurt us. The documentation that we may loose will be provided with the original source code. Test that when you remove amavisd-new, only amavisd-new will be removed:
apt-get -s remove amavisd-new

If it is the only thing that will be removed, continue on:
/etc/init.d/amavis stop
apt-get remove amavisd-new


If you get a message like this:
"The following packages were automatically installed and are no longer required:"
Then please manually install any packages that are listed, for example:
apt-get install libperl5.10

That will set the status of those packages to "manually installed". At this point we are temporarily going to put the same version of amavisd-new back together again so we can continue to process mail. The first command below should all fit on one line:
if ! grep -q amavis /etc/passwd; then adduser --group --system --home /var/lib/amavis --shell /bin/sh amavis; fi

cd /etc/init.d
wget http://verchick.com/mecham/public_html/spam/amavis.init.squeeze.txt
mv amavis.init.squeeze.txt amavis
chmod +x amavis
mkdir -p /usr/share/amavis/conf.d
cp /usr/ambackup/usr/share/amavis/conf.d/* /usr/share/amavis/conf.d
cp /usr/ambackup/usr/sbin/amavisd-new /usr/sbin
cp /usr/ambackup/usr/sbin/amavisd-new /usr/sbin/amavisd-new-debian
cp /usr/ambackup/usr/share/perl5/JpegTester.pm /usr/share/perl5
cp /usr/ambackup/etc/cron.daily/amavisd-new /etc/cron.daily
cp /usr/ambackup/etc/cron.d/amavisd-new /etc/cron.d
cp /usr/ambackup/usr/sbin/amavisd-new-cronjob /usr/sbin
/etc/init.d/amavis start


The Debian amavisd-new package has been removed but you are still using most of the original components of the Debian package. The main difference is the /etc/init.d/amavis init script. Now it should be easy to upgrade to amavisd-new 2.6.6. We will install an amavisd.conf that simply loads in all the Debian conf files:
cd /etc/amavis
test -e amavisd.conf && mv amavisd.conf amavisd.conf-before-migration
wget http://verchick.com/mecham/public_html/spam/amavisd.conf-264.txt
mv amavisd.conf-264.txt amavisd.conf
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/amavisd.conf-modified
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/amavisd.conf-sample
ln -s /etc/amavis/amavisd.conf /etc/amavisd.conf
cd /usr/local/src
wget http://www.ijs.si/software/amavisd/amavisd-new-2.6.6.tar.gz
tar xzf amavisd-new-2.6.6.tar.gz
cd amavisd-new-2.6.6
cp amavisd amavisd.original

cp amavisd /usr/sbin/amavisd-new-2.6.6
cp amavisd /usr/sbin/amavisd-new
cp p0f-analyzer.pl /usr/sbin
test -e /usr/sbin/p0f-analyzer && mv /usr/sbin/p0f-analyzer.old
ln -s /usr/sbin/p0f-analyzer.pl /usr/sbin/p0f-analyzer
cp amavisd-nanny /usr/sbin/
cp amavisd-release /usr/sbin/
sed -i 's|/var/amavis/amavisd.sock|/var/run/amavis/amavisd.sock|' /usr/sbin/amavisd-release
sed -i 's|/var/amavis/db|/var/lib/amavis/db|' /usr/sbin/amavisd-nanny
/etc/init.d/amavis restart
cd /etc/amavis
amavisd-new -V


The amavisd.conf-modified file is an example of the amavisd.conf supplied with the source code but it has been modified for use with Debian. I suggest you read /etc/amavis/amavisd.conf.

This version of amavisd-new can treat sanesecurity and MSRBL 'virusus' as spam. If you are using the sanesecurity ClamAV signatures, download a rule set to score these as spam:
cd /etc/spamassassin
wget http://verchick.com/mecham/public_html/spam/amavis-sanesecurity_v2.cf
spamassassin --lint


If you are currently using external notification templates (you probably are since they are enabled by default in /etc/amavis/conf.d/30-template_localization), you should get the templates that match amavisd-new 2.6.6 and if necessary modify them in the same manner you have modified the currently installed templates. The only templates available are English templates. If you don't normally customize your templates you should comment out:
read_l10n_templates('en_US', '/etc/amavis');

in the 30-template_localization file.

cp -r /etc/amavis/en_US /etc/amavis/en_US-backup
cd /etc/amavis/en_US
rm charset
rm template-dsn.txt
rm template-spam-admin.txt
rm template-spam-sender.txt
rm template-virus-admin.txt
rm template-virus-recipient.txt
rm template-virus-sender.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/charset
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-dsn.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-spam-admin.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-spam-sender.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-virus-admin.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-virus-recipient.txt
wget http://verchick.com/mecham/public_html/amavisd/2.6.6/en_US/template-virus-sender.txt
/etc/init.d/amavis restart


If for some crazy reason you should want to reinstall amavisd-new from the Debian package:
apt-get update

/etc/init.d/amavis stop
mv /etc/amavis/amavisd.conf /etc/amavis/amavisd.conf-mybackup
rm /etc/amavisd.conf
apt-get install amavisd-new


apt-get install clamav clamav-daemon lha arj zoo nomarch lzop cabextract p7zip rpm unrar-free libsnmp-perl libdata-dump-perl razor pyzor libimage-info-perl libnet-cidr-lite-perl libencode-detect-perl

gpasswd -a clamav amavis
freshclam
/etc/init.d/clamav-daemon stop
/etc/init.d/clamav-daemon start

sa-update

cd /etc/amavis/conf.d/
sed -i 's/#@bypass_virus_/@bypass_virus_/' 15-content_filter_mode
sed -i 's/#   \\%bypass_vi/   \\%bypass_vi/' 15-content_filter_mode
sed -i 's/#@bypass_spam_/@bypass_spam_/' 15-content_filter_mode
sed -i 's/#   \\%bypass_s/   \\%bypass_s/' 15-content_filter_mode
cat 15-content_filter_mode
/etc/init.d/amavis restart


Note: during the time amavisd-new is restarting, mail cannot be delivered to it. Also note that amavisd-new may not be able to use the UNIX socket at /var/run/clamav/clamd.ctl until clamd has fully loaded the virus definition database. Once this server has mail flowing through it, during the time amavisd-new is reloading Postfix may complain with "connect to localhost[127.0.0.1]: Connection refused". Postfix will defer this mail (for about 15 minutes). To speed things up, an impatient person may run 'postfix flush' to flush the deferred queue, but I would not.

We will install a script that runs sa-update:
cd /usr/sbin
wget http://verchick.com/mecham/public_html/spam/sa-update1.sh.txt
mv sa-update1.sh.txt sa-update.sh
chmod +x sa-update.sh
sa-update.sh


You only need to run this script once a week. Place an entry in your crontab (on the first available blank line):
crontab -e

Replace MM with a number between 0 and 59 and HH with a number between 0 and 23:
MM HH * * 1 /usr/sbin/sa-update.sh

Now vi /etc/amavis/conf.d/50-user and insert the text below in the middle of the file (must be between "use strict;" and "1;"). You may prefer the WinSCP editor. If you are still in the WinSCP editor since the last edit, you may need to hit the refresh icon in order to see the /etc/amavis directory. Take a look at @mynetworks to see if you need to modify it. At times you may want to temporarily leave your network out of @mynetworks (so you can test sending spam to recipients):
#----------------------------------------------------------
# nice to have $log_level (1-5) available:
$log_level = 0;

# If sender matches ACL, turn debugging fully up, just for this one message
#@debug_sender_maps = ( ["test\@$mydomain"] );

# let the amavisd program use the appropriate data type in SQL commands
$sql_allow_8bit_address = 1;

# explicitly set $mydomain and $myhostname:
$mydomain = 'example.com';
$myhostname = 'msa.example.com';

# when amavisd-new sends notifications, they appear to come from here:
$mailfrom_notify_admin     = "postmaster\@$mydomain";
$mailfrom_notify_recip     = "postmaster\@$mydomain";
$mailfrom_notify_spamadmin = "postmaster\@$mydomain";
$hdrfrom_notify_sender = "amavisd-new <postmaster\@$mydomain>";

$notify_method = 'smtp:[127.0.0.1]:10035'; # where to submit notifications

# Set number of processes. Rough guide for dual processor, 1GB = 6, 2GB = 12, 4GB = 24
# You MUST also change maxproc for the smtp-amavis transport to match this number, e.g:
# smtp-amavis unix -      -       n       -       5  smtp
# However, if using MailZu to release mail on a very busy server, it may be necessary
# to have the $max_servers number 1 greater than maxproc. This will leave a spare 
# amavisd-new process waiting for release requests. This may waste some memory, but
# this may avoid some problems releasing mail from quarantine.
$max_servers = 6;

# Note: If you need to set maxproc > 20, instead use:
# smtp-amavis_destination_concurrency_limit = <number>
# in /etc/postfix/main.cf and set the smtp-amavis transport like so:
# smtp-amavis unix -      -       n       -       -  smtp

# We discard (and quarantine) viruses, discard (and quarantine) spam (>= kill_level), 
# discard (and quarantine) banned files and pass bad headers:
$final_virus_destiny      = D_DISCARD;
$final_banned_destiny     = D_DISCARD;
$final_spam_destiny       = D_DISCARD;
$final_bad_header_destiny = D_PASS;

# disable DKIM - for now
$enable_dkim_verification = 0;  # enable DKIM signatures verification
$enable_dkim_signing = 0;    # load DKIM signing code, keys defined by dkim_key

# don't quarantine bad headers (no need since we pass them all):
$bad_header_quarantine_to = undef;

# Spam gets the Subject line prepended with:
$sa_spam_subject_tag = 'Spam> ';

# We tag all headers (for 'local' domains) with X-Spam info:
$sa_tag_level_deflt = undef;

# This is the system default spam tag level that will be overridden by user's preferences in MySQL
$sa_tag2_level_deflt = 6.31;

## For relay domains not set up in MySQL you can create a static domain wide (or individual) map: 
#@spam_tag2_level_maps = (
#  { 'postmaster@example.net' => 99.0,
#    '.example.net' => 8.0,
#    '.example.org' => 6.0 },
#  \$sa_tag2_level_deflt,   # catchall default
#);

# The default is to not quarantine any spam (outside of what users get in their Spam folder), 
# so set default kill_level high. Users can choose their own kill_level however. kill_level
# will trigger quarantining (to MailZu).
$sa_kill_level_deflt = 9999;

## Once again, relay domains may want something different:
#@spam_kill_level_maps = (
#  { 'postmaster@example.net' => 99.0,
#    '.example.net' => 8.0,
#    '.example.org' => 10.0 },
#  \$sa_kill_level_deflt,   # catchall default
#);

## And some relay domains may wish to quarantine up to a certain level, then discard:
#@spam_quarantine_cutoff_level_maps = (
#  { '.example.net' => 20.5,   
#    '.example.org' => 25 },
#  \$sa_quarantine_cutoff_level,   # catchall default (currently undef)
#);

# We will quarantine viruses to /var/lib/amavis/virusmails (the default).
# We will use a cron job to automatically delete these files older than 14 days from the quarantine.
# We can use amavisd-release or MailZu to release quarantined messages. We warn the recipients
# and expect them to contact us (via the MailZu interface) if they need a banned file released.
# Each domain can have their own administrators.
@virus_admin_maps = ({
    '.example.com' => 'postmaster@example.com',
    '.example.net' => 'postmaster@example.net',
    '.' => 'postmaster@example.com',
    });
@banned_admin_maps = ({
    '.example.com' => 'postmaster@example.com',
    '.example.net' => 'postmaster@example.net',
    '.' => 'postmaster@example.com',
    });
$warnbannedrecip = 1;
$defang_banned = 1;
$defang_virus  = 1;

# list domains in an external file (created by local_domains.sh script):
@local_domains_maps = ( read_hash("$MYHOME/local_domains") );

# Since we configured SQL, we can use penpals feature:
$penpals_bonus_score = 5;
$penpals_threshold_low = 1;
$penpals_threshold_high = 18;

# We are going to create policy banks that will notify us of internally created spam
# but also let banned files out (provided they are compressed).

@mynetworks = qw( 127.0.0.0/8 [::1] [FE80::]/10 [FEC0::]/10
                   10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 );

$inet_socket_port = [10024, 10026]; 
## If using Mailzu, use this instead:
#$inet_socket_port = [10024, 10026, 9998]; 

$inet_socket_bind = '127.0.0.1';
## If using Mailzu, use this instead:
#$inet_socket_bind = undef;

## Interface to MailZu
#$interface_policy{'9998'} = 'MAILZU';
#$policy_bank{'MAILZU'} = {
#    protocol => 'AM.PDP',
#    inet_acl => [qw( 127.0.0.1 [::1] 111.111.111.111 )],
#};

# If using MailZu, store banned files and spam to MySQL if you want to give users the
# ability to read those messages in the MailZu interface:
#$banned_files_quarantine_method = 'sql:';
#$spam_quarantine_method         = 'sql:';

# We create a custom set of banned rules for the MYNETS and TRUSTED policy
# banks. See also the 'DEFAULT' $banned_filename_re settings in 20-debian_defaults
%banned_rules = (
 'BLOCK_EXE' =>  new_RE(
    # block double extensions in names:
      qr'\.[^./]*\.(exe|vbs|pif|scr|bat|cmd|com|cpl|dll)\.?$'i,
    # allow any name or type (except viruses) within an archive:
      [ qr'^\.(Z|gz|bz2|rpm|cpio|tar|zip|rar|arc|arj|zoo)$' => 0],
    # blocks MS executable file(1) types, unless allowed above:
      qr'^\.(exe|exe-ms)$',
    ),
 'DEFAULT' => $banned_filename_re,
);				   

$policy_bank{'MYNETS'} = {  # mail originating from @mynetworks
  originating => 1,
  os_fingerprint_method => undef,
  spam_admin_maps  => ["postmaster\@$mydomain"], # alert of internal spam
  spam_kill_level_maps => [8.0],
  spam_dsn_cutoff_level_maps => [9999],
  spam_dsn_cutoff_level_bysender_maps => [9999],
  banned_filename_maps => ['BLOCK_EXE'],
};

# Note about spam_kill_level_maps in policy banks: the recipient's
# spam_kill_level is overridden (ignored). This may cause unexpected
# results, such as quarantining or passing of spam. In the two policy
# banks shown here, this would only apply to mail sent from trusted users
# (who typically would not send spam).

$interface_policy{'10026'} = 'TRUSTED';
$policy_bank{'TRUSTED'} = {  # mail originating from trusted senders
  originating => 1,
  os_fingerprint_method => undef,
  spam_admin_maps  => ["postmaster\@$mydomain"], # alert of internal spam
  spam_kill_level_maps => [8.0],
  spam_dsn_cutoff_level_maps => [9999],
  spam_dsn_cutoff_level_bysender_maps => [9999],
  banned_filename_maps => ['BLOCK_EXE'],
};

# Here we set up access to MySQL data:
@lookup_sql_dsn = ( ['DBI:mysql:amavis:localhost', 'amavis', 'amavis_password'] );
@storage_sql_dsn = @lookup_sql_dsn;

# disable quarantine subdirectories
$quarantine_subdir_levels = undef;

# Note: If you quarantine items locally (the default), you might also need to create a script
# to eventually delete old quarantined items. Look to /etc/cron.daily/rmvirusquar as an example

# required because we set msgs.time_iso to type TIMESTAMP (required by MailZu)
$timestamp_fmt_mysql = 1;

# specific to the amavisnewsql SquirrelMail plugin
$sql_select_white_black_list = 'SELECT wb FROM wblist'.
  ' WHERE (rid=?) AND (wblist.email IN (%k))'.
  ' ORDER BY wblist.priority DESC';

# we may need to add items to this  
@additional_perl_modules = qw(
);  

#----------------------------------------------------------
Now we will create the MySQL schema for amavisd-new. This schema is a combination of the recommended amavisd-new schema, and the schema provided with the amavisnewsql SquirrelMail Plugin:
cd
wget http://verchick.com/mecham/public_html/spam/amavis-260-sqmail.sql.txt
sed -i "s/BY 'password'/BY 'amavis_password'/" amavis-260-sqmail.sql.txt
chmod 600 amavis-260-sqmail.sql.txt
mysql -u root -p < amavis-260-sqmail.sql.txt


You were prompted for roots_password. Secure 50-user from prying eyes (to protect the MySQL password):
chown root:amavis /etc/amavis/conf.d/50-user
chmod 640 /etc/amavis/conf.d/50-user


Amavisd-new breaks messages down into components before virus scanning. Also enable virus scanners to scan the message as a whole:
sed -i "s|# qr'^MAIL$'| qr'^MAIL$'|" /etc/amavis/conf.d/20-debian_defaults

We are going to create a script that will pull a list of our domains from a PostfixAdmin table:
vi /usr/sbin/local_domains.sh

Insert the following (this is two lines - line 2 may wrap)
#!/bin/bash
mysql -upostfix -ppfix_password postfix -B -N -e "select concat('.',domain) from domain" >/var/lib/amavis/local_domains


Save the file. Then:
chmod 700 /usr/sbin/local_domains.sh
local_domains.sh
cat /var/lib/amavis/local_domains


Our domains should be listed in the file. We will add this script to PostfixAdmin so it's updated every time we add or remove domains. We need to add another entry to /etc/sudoers so www-data can run this script:
visudo

and insert at the bottom (noting once again the hostname of the server):
www-data msa=NOPASSWD: /usr/sbin/local_domains.sh

Now patch PostfixAdmin so it uses this script:
cd /usr/share/postfixadmin
cp delete.php delete.php.original
cp create-domain.php create-domain.php.original
wget http://verchick.com/mecham/public_html/spam/domain.patch.v3.txt
patch -p0 < domain.patch.v3.txt


You should spend a minute to convince yourself this works. Remove /var/lib/amavis/local_domains and log into https://msa.example.com/postFixadminx and edit or add a domain (you have to log in as the super-user in order to edit domains). Then
cat /var/lib/amavis/local_domains to insure the file was created, then:
/etc/init.d/amavis restart

Install a couple maintenance scripts to prevent the amavis database from growing forever. Messages older than 24 days are deleted. These are not messages stored in mail boxes, they are messages that are stored in MySQL and are accessed via MailZu. If it grows forever, you or I made a mistake here:
cd /usr/sbin
wget http://verchick.com/mecham/public_html/spam/trim-amavis-msgs.txt
mv trim-amavis-msgs.txt trim-amavis-msgs
sed -i 's/Passw0rd/amavis_password/' trim-amavis-msgs
chmod 750 trim-amavis-msgs
cd /etc
wget http://verchick.com/mecham/public_html/spam/trim-amavis.sql.txt
mv trim-amavis.sql.txt trim-amavis.sql
cd /etc/cron.daily/
wget http://verchick.com/mecham/public_html/spam/trim-amavis.txt
mv trim-amavis.txt ztrim-amavis
sed -i 's/password/amavis_password/' ztrim-amavis
chmod 750 ztrim-amavis
./ztrim-amavis


Now configure postfix to use amavisd-new. I will show NEW changes to master.cf in red. Also notice there are different port number involved for the content_filter overrides. You may also want to use the WinSCP editor here:
vi /etc/postfix/master.cf
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       -       -       -       smtpd
  -o smtpd_sasl_auth_enable=no
  -o content_filter=smtp-amavis:[127.0.0.1]:10024
# -o receive_override_options=no_address_mappings
# If they want to relay, make them use port 587 (submission) or port 465 (smtps)
# If using submission port, configure client to use CRAM-MD5
submission inet n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=may
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
# -o receive_override_options=no_address_mappings
# Outlook and OE (and others) expect smtpd_tls_wrappermode,
# so have them submit here (PORT 465):
smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
# -o receive_override_options=no_address_mappings
# We will use port 4650 for clients that use STARTTLS:
4650     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
# -o receive_override_options=no_address_mappings
#628      inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
  -o content_filter=

[... other stuff is here, but does not need editing ...]

# Insert at the bottom and adjust maxproc from 5 if needed:
#
smtp-amavis unix -    -    n    -    5    smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes

127.0.0.1:10025 inet n    -    n    -    -    smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -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

# port 10035 is where amavisd-new sends notifications
127.0.0.1:10035 inet n    -    n    -    -    smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -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
When finished editing:
/etc/init.d/postfix restart

Configure pyzor:
We need to apply a small patch to pyzor:
cd /usr/bin
wget http://verchick.com/mecham/public_html/spam/pyzor.patch1.txt
cp pyzor pyzor.original
patch pyzor < pyzor.patch1.txt


pyzor discover
su amavis -c 'pyzor discover'
pyzor ping
su amavis -c 'pyzor ping'


Pyzor Ping should show 'OK'. If not, then it's possible your firewall is blocking udp replies from 82.94.255.100 or 188.40.77.236 (public.pyzor.org port 24441), or the server may simply be slow to respond (often the case).

SpamAssassin, by default, will automatically attempt to figure out which Received: headers were inserted by mail servers in your network, and which were not. However, to be safe it's best to manually configure the trust path. The IP addresses listed in internal_networks and trusted_networks should be the IP addresses (or network addresses) of hosts on you network. If you are behind a NAT box, this would include your internal network, your public network and the loopback interface. If mail is relayed to you from a trusted 3rd party (maybe you use something like Postini to filter your mail), then those servers would be added to trusted_networks (but not internal_networks).
vi /etc/spamassassin/local.cf

Here is an example of what should be inserted:
# explicitly set our internal_networks (might be the same or similar to mynetworks)
clear_internal_networks
internal_networks 222.222.222.222/24
internal_networks 10.10.10.10/24
# add the same to trusted_networks, and possibly other computers/networks whose mail we trust
clear_trusted_networks
trusted_networks 222.222.222.222/24
trusted_networks 10.10.10.10/24


Always lint SpamAssassin after modifying or adding files:
spamassassin --lint

Once you have correctly configured these settings:
/etc/init.d/amavis restart
tail -f /var/log/mail.log


Test by sending a message through and observing output from mail.log. You should see results from amavis. It can take between 1 second and 30 seconds (or even longer) to process a message.
If you get clamd errors, /etc/init.d/clamav-daemon stop , wait a moment, /etc/init.d/clamav-daemon start , and wait a minute before continuing and also make sure that the clamav user is a member of the amavis group ( gpasswd -a clamav amavis )

I quarantine viruses locally in /var/lib/amavis/virusmails for 14 days, then delete them. If you would like to accomplish this deletion task, grab the script from me:
cd /etc/cron.daily
wget http://verchick.com/mecham/public_html/spam/rmvirusquar.txt
mv rmvirusquar.txt rmvirusquar
chmod +x rmvirusquar
./rmvirusquar



Create Bayes and AWL tables in MySQL

Index Top Bottom
Placing Bayes and AWL data in MySQL will put a little more load on the MySQL server, but as long as you have been reasonably generous with innodb_buffer_pool_size and innodb_log_file_size you will greatly improve Bayes performance (do these in sections):
First, there is a bug in SpamAssassin 3.3.x that needs to be fixed:
cd /usr/share/perl5/Mail/SpamAssassin/BayesStore
grep -q 46675 MySQL.pm || wget http://verchick.com/mecham/public_html/spam/mysql-bug-46675.patch.txt
grep -q 46675 MySQL.pm || patch MySQL.pm < mysql-bug-46675.patch.txt

cd /etc/spamassassin/
wget http://verchick.com/mecham/public_html/spam/bayes2011.sql.txt
sed -i 's/paSSw0rd/sa_password/' bayes2011.sql.txt
mysql -u root -p < bayes2011.sql.txt

Enter roots_password to complete the process.

rm bayes2011.sql.txt
wget http://verchick.com/mecham/public_html/spam/local.cf-bayes-awl.v3.txt
cp local.cf local.cf-before-mysql
cat local.cf-bayes-awl.v3.txt local.cf-before-mysql > local.cf
sed -i 's/paSSw0rd/sa_password/' local.cf
chmod 0640 local.cf
chown root:amavis local.cf
spamassassin --lint
/etc/init.d/amavis restart

cd /var/lib/amavis
wget http://verchick.com/mecham/public_html/spam/sample-spam.txt
sa-learn --spam sample-spam.txt
sa-learn --dump magic
spamassassin --lint


It should show our nspam (number of spam) count is 1, and --lint should be clean. We should also create our .spamassassin directory and user_prefs file:
su amavis -c 'spamassassin <sample-spam.txt'

The warning message "warn: config: created user preferences file:" is a good thing.
We continue by adding an AWL and bayes_seen maintenance script (watch for errors):
cd /etc
wget http://verchick.com/mecham/public_html/spam/trim-awl.sql.txt
mv trim-awl.sql.txt trim-awl.sql
cd /usr/sbin
wget http://verchick.com/mecham/public_html/spam/trim-awl.txt
mv trim-awl.txt trim-awl
sed -i 's/paSSw0rd/sa_password/' trim-awl
chmod 0750 trim-awl
cd /etc/cron.weekly/
wget http://verchick.com/mecham/public_html/spam/trim-sql-awl-weekly.txt
mv trim-sql-awl-weekly.txt trim-sql-awl-weekly
chmod +x trim-sql-awl-weekly
./trim-sql-awl-weekly


Once again make sure amavisd-new is processing messages. Send at least one test message and then read the headers of the message. It should show X-Spam headers and such. It will not show any BAYES hits yet - it is required that Bayes learns at least 200 ham messages first. I use autolearn (with an occasional manual feeding to sa-learn). I do not cover using other means of feeding spam and ham to SpamAssassin in this HOWTO - see man sa-learn . The SquirrelMail Spam Buttons plugin allows users to mark messages as 'Spam' or 'Not Spam' and feed them to sa-learn as such, but in a site wide database one has to remember that "one man's garbage is another man's treasure" and "too many cooks spoil the pot". Plus, there is the additional server load to consider - sa-learn is CPU intensive.

It would be a good idea to test clamd. After temporarily disabling your desktop AV program I would send a test message through with ONLY the eicar string in the body of the message (absolutely no whitespace before or after the string - it must start and end on the very first line). You can stop amavisd-new and debug it with 'amavisd-new debug' or 'amavisd-new sa-debug' or you can raise $log_level or temporarily set @debug_sender_maps.


Configure and customize SquirrelMail

Index Top Bottom
Tell apache2 to use squirrelmail:
echo "Include /etc/squirrelmail/apache.conf" >> /etc/apache2/apache2.conf

I am going to set the SquirrelMail URL to https://msa.example.com/mail/, so (note the "allow from" IP address):
cd /etc/squirrelmail/
sed -i "s|Alias /squirrelmail|Alias /mail|" apache.conf
sed -i "s|allow from 127.0.0.1|allow from 666.666.666.666|" apache.conf

/etc/init.d/apache2 restart


The 'allow from' IP address in /etc/squirrelmail/apache.conf above is the IP address of your computer or network (as the mailserver sees it). Apache will not start if you failed to edit this document and replace the ip address with a valid address. Download some plugins:
cd /usr/share/squirrelmail/plugins/
wget http://www.squirrelmail.org/plugins/check_quota-1.4-re-1.2.7.tar.gz
tar xzf check_quota-1.4-re-1.2.7.tar.gz
cp check_quota/config.php.sample check_quota/config.php
wget http://www.squirrelmail.org/plugins/timeout_user-1.1.1-0.5.tar.gz
tar xzf timeout_user-1.1.1-0.5.tar.gz
wget http://www.squirrelmail.org/plugins/compatibility-2.0.15-1.0.tar.gz
tar xzf compatibility-2.0.15-1.0.tar.gz
wget http://www.squirrelmail.org/plugins/amavisnewsql-0.8.0-1.4.tar.gz
tar xzf amavisnewsql-0.8.0-1.4.tar.gz
mkdir /var/lib/amavis/.notstored
chown -R amavis:amavis /var/lib/amavis
sed -i 's/minutes = 120;/minutes = 20;/' timeout_user/config.php

cd amavisnewsql
cp config.php.dist config.php

sed -i 's|pgsql://postgres:@localhost|mysql://amavis:amavis_password@localhost|' config.php
sed -i 's|"yourdomain.com"|"example.com"|' config.php
sed -i 's|use_quarantine"] = true|use_quarantine"] = false|' config.php
sed -i 's|http://webmail.yourdomain.com|https://msa.example.com/mail|' config.php
sed -i 's|noreply@yourdomain.com|noreply@example.com|' config.php
sed -i 's|/htdocs/squirrel/|/usr/share/squirrelmail/|' utils/cleanquarantine.php
sed -i 's|/htdocs/squirrel/|/usr/share/squirrelmail/|' utils/generatedigest.php
sed -i 's|/htdocs/squirrel/|/usr/share/squirrelmail/|' utils/process_bsmtp.php
sed -i 's|/var/virusmails|/var/lib/amavis/virusmails|' utils/process_bsmtp.php
sed -i 's|ereg("@"|preg_match("/@/"|' amavisnewsql.class.php
sed -i 's|eregi($Email_RegExp_Match|preg_match("/$Email_RegExp_Match/i"|' setup.php
chown -R root:root /usr/share/squirrelmail/plugins/amavisnewsql
chmod 644 *
chmod 755 contrib htmlMimeMail-2.5.1 locale po utils
chmod 640 contrib/*
chmod 640 htmlMimeMail-2.5.1/*
chmod 640 utils/soap/*
chmod 640 utils/*php
chmod 640 utils/*sql
chmod 640 config.php
chown root:www-data config.php


We did some configuration of amavisnewsql. We turned off its (broken) quarantine function (and because it is broken we send spam to either a folder or MailZu). Now we put some sanity constraints on what users can enter for tag2_level and kill_level and modify some of the text that users see. I also add the ability for the users to set spam_quarantine_cutoff_level which gives them the option to discard high scoring spam. New users are added to the database when they log into SquirrelMail and go to Options->SpamAssassin Configuration.
wget http://verchick.com/mecham/public_html/spam/amavisnewsql.patch1.txt
sed -i 's/host.domain.tld/msa.example.com/' amavisnewsql.patch1.txt
cp -p functions.php functions.php.original
cp -p amavisnewsql.class.php amavisnewsql.class.php.original
cp -p amavisnewsql.php amavisnewsql.php.original
patch -p0 < amavisnewsql.patch1.txt


Now start configuration (hint: enter squ[Tab]). Navigate to the items below from the main menu:
squirrelmail-configure

1. Organization preferences.
 1. Organization Name Widgits Inc.
 7. Provider link https://msa.example.com/mail

R Return to Main Menu

4. General Options
 5. Usernames in Lowercase true
 7. Hide SM attributions true
 10. Allow server thread sort true
 11. Allow server-side sorting true

R Return to Main Menu

D. Set pre-defined settings for specific IMAP servers dovecot

R Return to Main Menu

8. Plugins
 Plugins
  Installed Plugins
   1. view_as_html
   2. amavisnewsql
   3. check_quota
   4. timeout_user

S Save data
Q Quit

You should be able to test the setup next. If unable to open this page, check /etc/squirrelmail/apache.conf for IP address access to configtest.php: https://msa.example.com/mail/src/configtest.php

If you get a warning concerning "Magic Quotes" (which you should not), then you MUST fix it with:
sed -i "s/magic_quotes_gpc = On/magic_quotes_gpc = Off/" /etc/php5/apache2/php.ini
/etc/init.d/apache2 restart


Browse to your SquirrelMail site, then log in to SquirrelMail with your own account (and the test account):
https://msa.example.com/mail/

Note that you and your users MUST log in to SquirrelMail at least once in order for the user account to be created in the amavis SQL database. Those that have not logged in yet will use the default static amavisd-new settings (for example $sa_tag2_level_deflt = 6.31; and $sa_kill_level_deflt = 9999;)

If you get an error: "Preference file, /test@example.com.pref.tmp, could not be opened." it means this user would need to go to Options and Personal Information and enter their name and email address. When you add users in PostfixAdmin, the preferences file in /var/lib/squirrelmail/data/ should automatically be created via the mail_profile.sh script. At some point you will no doubt wish to replace the SquirrelMail logo with yours. Place it in /usr/share/squirrelmail/images/ and change the size as required in the "Organization preferences" page.

Configure Razor

Index Top Bottom
cd
rm /etc/razor/razor-agent.conf
razor-admin -create
razor-admin -create
razor-admin -register


If you get an error: Error 202 while performing register, aborting., you may need to run the 'razor-admin register' command more than once. Don't worry about it if /etc/razor/razor-agent.conf does not exist. Here we disable logging and then give amavisd-new a copy of the razor files:
sed -i 's/= 3/= 0/' /root/.razor/razor-agent.conf
cp -r /root/.razor /var/lib/amavis
chown -R amavis:amavis /var/lib/amavis
cat /var/lib/amavis/.razor/razor-agent.conf | grep debuglevel


Insure debuglevel is set to 0


Install pflogsumm

Index Top Bottom
apt-get install pflogsumm

cd /etc/cron.daily
wget http://verchick.com/mecham/public_html/spam/pflogsumm.sh
sed -i 's|DAILY mail|DAILY msa mail|' pflogsumm.sh
mv pflogsumm.sh pflogsumm
chmod +x pflogsumm


Then just make sure you have a mailbox (or alias) for root. Root will get a report each morning after 06:25. Hint: cat /etc/crontab. There is one problem with the report. Any email that gets sent to amavisd-new, which includes most mail that isn't rejected at the front door, also comes back from amavisd-new. This means Postfix sees the email twice. So the report lists them twice. For those that are interested, there are a couple scripts out there that pre-process the log files to prevent reporting amavis entries: http://www.gufonero.com/postfix/prepflog.html and http://www.caspergasper.com/spam.shtml


Install BIND

Index Top Bottom
apt-get install bind9

/etc/init.d/bind9 stop

sed -i 's|"-u bind"|"-u bind -t /var/lib/named"|' /etc/default/bind9
mkdir -p /var/lib/named/etc
mkdir /var/lib/named/dev
mkdir -p /var/lib/named/var/cache/bind
mkdir -p /var/lib/named/var/run/bind/run
mkdir -p /var/lib/named/var/run/named
mv /etc/bind /var/lib/named/etc
ln -s /var/lib/named/etc/bind /etc/bind
mknod /var/lib/named/dev/null c 1 3
mknod /var/lib/named/dev/random c 1 8
chmod 666 /var/lib/named/dev/null /var/lib/named/dev/random
chown -R bind:bind /var/lib/named/var/*
chown -R bind:bind /var/lib/named/etc/bind
chown -R bind:bind /var/lib/named/var/run/named
touch /var/lib/named/var/cache/bind/managed-keys.bind
chown bind:bind /var/lib/named/var/cache/bind/managed-keys.bind
/etc/init.d/bind9 start


If you happen to be trying to use this document on Ubuntu with Apparmor enabled, I suggest you disable Apparmor and reboot:
/etc/init.d/apparmor stop
update-rc.d -f apparmor remove
aptitude remove apparmor apparmor-utils

reboot


Now test:
/etc/init.d/bind9 restart
lsof -i | grep :domain
tail -55 /var/log/syslog


The result of the last two commands should show 'named' is running without error. Now we create a new /etc/resolv.conf with the IP address of the server first and the IP address of the former primary DNS second:
cat /etc/resolv.conf
cp /etc/resolv.conf /etc/resolv.conf-old
echo "search example.com" > /etc/resolv.conf
echo "nameserver 111.111.111.111" >> /etc/resolv.conf
echo "nameserver 444.444.444.444" >> /etc/resolv.conf


If the result of "cat /etc/resolv.conf" or "cat /etc/resolv.conf.old" showed "DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN" you should instead edit the name servers in /etc/network/interfaces under the "dns-nameservers" setting (separated by spaces) and then restart the network:
/etc/init.d/networking restart

Then restart bind9. You may see some errors at this point - we still have work to do:
/etc/init.d/bind9 restart
lsof -i | grep :domain
tail -55 /var/log/syslog


So far we have set up bind9 as a local caching only name server. Here we add an additional security measure that prevents unauthorized machines from using our name server:
vi /etc/bind/named.conf.options

On the line below "directory" we want to add a line that restricts use of our name server to the network our mailserver is on. Place a [Tab] in front of the entry so it lines up with the other entries. You can add more than one network here if you like. Place a ";" (semicolon) after each network. Note that if you actually want to allow other clients to connect to our name server, as explained in the notes above you would also have to modify IP tables to allow this.
allow-query {222.222.222.222/24;};

We need to suppress some logging:
vi /etc/bind/named.conf

And insert (below the first set of comments}:
logging {
category lame-servers {null; };
category edns-disabled { null; };
};


Save and exit the file, then I would restart bind9 and check that it is running:
/etc/init.d/bind9 restart
lsof -i | grep :domain


We will get a number of 'RFC 1918 response' related entries in our log unless we include the RFC 1918 zones:
vi /etc/bind/named.conf.local

Remove the comment marks "// " from the beginning of this option:
// include "/etc/bind/zones.rfc1918";

Then restart bind9 and check for errors again:
/etc/init.d/bind9 restart
lsof -i | grep :domain
tail -55 /var/log/syslog


We added a new user to /etc/passwd so give Postfix a copy:
LINUX2

Check that we can resolve names:
dig yahoo.com

In the ANSWER SECTION you should see some A records with IP addresses and near the bottom the SERVER: should be this server.

Run a quick speed test:
cd /var/lib/amavis
wget http://verchick.com/mecham/public_html/spam/sample-spam.txt
time su amavis -c 'spamassassin <sample-spam.txt'


Typically on a machine with a decent CPU and adequate Internet bandwidth you can expect times between 2 and 6 seconds. Make a note of the 'real' time. Run it again, and average the two times:
time su amavis -c 'spamassassin <sample-spam.txt'

vim /etc/resolv.conf and temporarily comment out the IP address of the local machine (so you are using your ISP's name server).
Use dig yahoo.com to check which SERVER: is actually used.

Run the speed test again twice and average the times again:
time su amavis -c 'spamassassin <sample-spam.txt'
time su amavis -c 'spamassassin <sample-spam.txt'


If your response time is faster when using your ISP's server than it is when using your local Bind server, then you should probably vim /etc/resolv.conf again and reset your local machine as the primary DNS server - but configure Bind to forward requests to your ISP's server (or other reliable server):

Optionally configure bind9 as a forwarding server. Bind9 as we have it configured now will first query the root servers for hints when needed. You may prefer to forward queries to another DNS server instead. There are advantages and disadvantages in doing this. There are two main reasons not to use forwarding. The first is that ClamAV uses DNS to check for updates. It's possible that if you are relying on the forwarding DNS server to check the ClamAV DNS server, the information may not be up to date. The other reason has to do with spamhaus.org's DSNBLs. If you use someone else's (your ISP's) DNS server, all the queries from that DNS server are counted toward the total number of connections to the spamhaus servers, which may result in you loosing that free service. It is absolutely imperative that any name servers you use as forwarders are known to work from our mailserver. These will almost certainly be the primary and secondary servers you previously configured in /etc/resolv.conf, or your ISP's servers (not 127.0.0.1, and not the IP address of the local machine). They should point to real name servers and not a DNS proxy like a Linksys broadband router or other gateway device (unless that proxy does not allow proper access to real name servers outside your network - which is sometimes the case). Add the 'forwarders' entry just below the 'allow-query' entry we just made:
vi /etc/bind/named.conf.options
and add:
forwarders {444.444.444.444; 555.555.555.555;};
To never query the root servers, optionally add:
forward only;

Then restart bind9 and check for errors again:
/etc/init.d/bind9 restart
lsof -i | grep :domain
tail -55 /var/log/syslog


Feel free run the speed test again. If setting your local Bind server as the nameserver in /etc/resolv.conf is consistently slower than using your ISP's DNS server (even when using forwarding), then maybe you should simply use your ISP's name server.

Remember: If the result of "cat /etc/resolv.conf" or "cat /etc/resolv.conf.old" previously showed "DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN" you should instead edit the name servers in /etc/network/interfaces under the "dns-nameservers" setting (separated by spaces) and then restart the network. If you do not do it this way, changes to resolv.conf will be lost after a reboot:
/etc/init.d/networking restart

Suppress a logcheck message:
echo '^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ named\[[[:digit:]]+\]: DNS format error from' >>/etc/logcheck/ignore.d.server/bind

Here is another possibly useful test:
cd /var/lib/amavis
su amavis -c 'spamassassin -t -D <sample-spam.txt' 2>&1 | perl -MPOSIX -MTime::HiRes -n -e 'BEGIN {$|=1; $dp=0; $t0=Time::HiRes::time}; $t=Time::HiRes::time; $dt=$t-$t0; printf("%s%06.3f %4.3f %4.3f %s", POSIX::strftime("%H:%M:",localtime($t)), $t-int($t/60)*60, $dt, $dt-$dp, $_); $dp=$dt' $* >0.log 2>&1 &


Wait a full minute, then read the debug file:

less 0.log

You should be able to see where the most time is spent. The second column is how much time has past, third is how much time is spent on one particular operation.


Additional Postfix configuration

Index Top Bottom
A few of these lines wrap (considerably) so I will assume you are going to select the entire sections. If you need to edit anything, paste it into Notepad (with Word Wrap turned off) first.
postmap /etc/postfix/sender_access
postmap /etc/postfix/rbl_client_exceptions
postmap /etc/postfix/rbl_sender_exceptions
postmap /etc/postfix/rbl_recipient_exceptions
touch /etc/postfix/reject_over_quota
postmap /etc/postfix/reject_over_quota

cp /etc/postfix/main.cf /etc/postfix/main.cf-changes
postconf -e "smtpd_hard_error_limit = 10"
postconf -e "smtpd_soft_error_limit = 8"
postconf -e "smtpd_helo_required = yes"
postconf -e "smtpd_sender_restrictions = check_sender_access hash:/etc/postfix/sender_access, reject_non_fqdn_sender, reject_unknown_sender_domain"
postconf -e "smtpd_data_restrictions = reject_unauth_pipelining"
postconf -e "smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unlisted_recipient, check_recipient_access hash:/etc/postfix/reject_over_quota, check_sender_access hash:/etc/postfix/rbl_sender_exceptions, check_client_access hash:/etc/postfix/rbl_client_exceptions, check_recipient_access hash:/etc/postfix/rbl_recipient_exceptions, reject_rbl_client sbl-xbl.spamhaus.org"
postconf -e "mydestination = "
postconf -e "local_transport = error:no local mail delivery"
/etc/init.d/postfix restart
tail -f /var/log/mail.log


Send another test message to make sure we did not break something. You can use sender_access to whitelist senders that would get rejected by either reject_non_fqdn_sender or reject_unknown_sender_domain. You can use rbl_sender_exceptions and/or rbl_client_exceptions to whitelist those who would otherwise get rejected by sbl-xbl.spamhaus.org. rbl_recipient_exceptions is for those recipients that wish to 'opt out' of RBL checks (postmaster and abuse come to mind). These next settings are not related to UCE.

If (and only if) the IP address you present to the world is not the IP address of this server (you are configured to run behind a NAT firewall or a proxy server) please configure proxy_interfaces (1.2.3.4 represents the public IP address):
postconf -e "proxy_interfaces = 1.2.3.4"

At some point in the future, if you have clients on the local network that are going to use this server as their outgoing SMTP server, you may need to configure $mynetworks. Clients in mynetworks will bypass the reject_unauth_destination restriction and therefore be allowed to relay to domains other than ours. You can use mynetworks or you can use SASL auth. I have not had you configure $mynetworks until now because it is useful during testing to not be a member in $mynetworks.

Let's create a little report that runs each morning that shows us how much mail is in our queues. This could serve as an alarm if the queues are stacked up:
vi /etc/cron.d/qshape-cron

and insert these two lines:
PATH=/usr/sbin
50 6 * * * postfix /usr/sbin/qshape incoming active deferred 2>&1 |/usr/bin/mail -s "qshape msa" root


Root should get a report tomorrow at 06:50. Now we suppress a few more logcheck entries:
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ named\[[[:digit:]]+\]: clients-per-query" >>/etc/logcheck/ignore.d.server/bind
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ postfix/pickup\[[0-9]+\]: [[:alnum:]]+: uid=[0-4]+ from=<postfix>" >>/etc/logcheck/ignore.d.server/postfix
echo "dsn=2.7.0, status=sent \(254 2.7.0 Ok," >>/etc/logcheck/ignore.d.server/postfix
echo "^\w{3} [ :0-9]{11} [._[:alnum:]-]+ postfix/policyd-weight\[[[:digit:]]+\]: master: child [[:digit:]]+ exited" >>/etc/logcheck/ignore.d.server/postfix
echo "postfix/smtpd\[[0-9]+\]: timeout after END-OF-MESSAGE from localhost[127.0.0.1]" >>/etc/logcheck/ignore.d.server/postfix
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: IMAP\([...@[:alnum:]]+\): Disconnected in IDLE" >>/etc/logcheck/ignore.d.server/dovecot
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ dovecot: IMAP\([...@[:alnum:]]+\): Disconnected: Logged out bytes=[0-9]+\/[0-9]+$" >>/etc/logcheck/ignore.d.server/dovecot
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) (Blocked|Passed)" >>/etc/logcheck/ignore.d.server/amavisd-new


By default "message_size_limit = 10240000" which is 10MB. Let's change the limit to 20MB, just to show how it might be done:
postconf -e "message_size_limit = 20480000"
/etc/init.d/postfix restart


In /etc/php5/apache2/php.ini there are a couple settings which affect the maximum size of attachments (and the maximum size of a message) that can be sent in the SquirrelMail Interface. See http://www.hmailserver.com/forum/viewtopic.php?t=2254. So, for example, we may wish to change a couple items. Here I'm changing upload_max_filesize to 9MB and post_max_size to 10MB. I am also upping the max_execution_time, to 1 second less than max_input_time (which I am also increasing):
sed -i 's/upload_max_filesize = 2M/upload_max_filesize = 9M/' /etc/php5/apache2/php.ini
sed -i 's/post_max_size = 8M/post_max_size = 10M/' /etc/php5/apache2/php.ini
sed -i 's/max_execution_time = 30/max_execution_time = 119/' /etc/php5/apache2/php.ini
sed -i 's/max_input_time = 60/max_input_time = 120/' /etc/php5/apache2/php.ini
/etc/init.d/apache2 restart



Set up PostfixAdmin Vacation

Index Top Bottom
There are a lot of good reasons not to use an autoresponder. One reason is: most of the mail we receive is spam and the senders use forged addresses. You will either be sending notices to senders who did not send you mail, or you will fill up your deferred queue with undeliverable mail. There may also be additional spam saved to the MailZu quarantine. There are often other problems like multiple deliveries or the potential for infinite loops. Use presents risks.

apt-get install libdbi-perl libdbd-mysql-perl libmail-sendmail-perl libdbd-pg-perl libemail-valid-perl libmime-perl libmime-charset-perl libmime-encwords-perl

addgroup --gid 65501 vacation
useradd -c "Virtual Vacation" -d /nonexistent -u 65501 -g 65501 -s /sbin/false vacation
mkdir /var/spool/vacation
cd /var/spool/vacation/
wget http://verchick.com/mecham/public_html/spam/vacation.2211.txt
mv vacation.2211.txt vacation.pl

sed -i "s/db_password = 'password'/db_password = 'pfix_password'/" vacation.pl
chown -R vacation:vacation /var/spool/vacation
chmod 750 vacation.pl


We place an entry in /etc/postfix/transport that will send mail to the vacation script. This is a bogus (sub)domain name. It does not need to be set up in DNS:
vi /etc/postfix/transport

and insert:
autoreply.example.com   vacation:

vi /etc/postfix/master.cf

and insert (just below the dovecot transport might be a good place):
vacation  unix  -       n       n       -       -       pipe 
     flags=Rq user=vacation argv=/var/spool/vacation/vacation.pl -f ${sender} -- ${recipient}
Then:
postmap /etc/postfix/transport
postconf -e "transport_maps = hash:/etc/postfix/transport"
postconf -e "vacation_destination_recipient_limit = 1"
LINUX2


Now we configure PostfixAdmin to use the vacation feature:
sed -i "s/vacation'] = 'NO/vacation'] = 'YES/" /usr/share/postfixadmin/config.inc.php

To change their Auto Response settings, a user could log into:
https://msa.example.com/postFixadminx/users

However, we can avoid users logging into PostfixAdmin by setting up a PostfixAdmin plugin in SquirrelMail (jump ahead to the next section to set it up - then return here) https://msa.example.com/mail. As you test, do not be concerned about receiving duplicate Out of Office replies because we will fix that a little later. Test by logging into the test account and setting it up. Remember that the database records the senders it has already seen (so only one notice is sent per sender). To clear out the cache and start over, return from your vacation by choosing Auto Response->Coming Back and then Auto Response->Going Away again. Use phpMyAdmin to browse the database as you make changes.

Now that vacation is working, consider turning it off so no one can use it. First use https://msa.example.com/phpmyadmiNx to make sure there are no entries in the vacation table and no aliases in the alias table that send mail to @autoreply.example.com (in other words, no one may be using Out of Office), then:

sed -i "s/vacation'] = 'YES/vacation'] = 'NO/" /usr/share/postfixadmin/config.inc.php

Suppress a logcheck message:
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ Vacation: Orig-To: .* From: .* MessageID: .* Subject:" >>/etc/logcheck/ignore.d.server/postfix


Install postfixadmin SquirrelMail plugin

Index Top Bottom
There are several versions of this plugin out there. I picked the one from CodePoets/Pale Purple. This allows the user to change their password, edit forwarding and set Out of Office (vacation) from the SquirrelMail interface. Unlike the PostfixAdmin user interface however, users do not have the ability to prevent delivery to the local mailbox when forwarding. Once installed, you should look at config.php to familiarize yourself with the options.
pear channel-update pear.php.net
pear install MDB2-2.5.0b3
pear install MDB2_Driver_mysql-1.5.0b3

cd /usr/local/src
wget http://verchick.com/mecham/public_html/spam/squirrelmail-postfixadmin-0.4.3.gz
tar xzf squirrelmail-postfixadmin-0.4.3.gz
cp -r squirrelmail-postfixadmin-0.4.3/ /usr/share/squirrelmail/plugins/
mv /usr/share/squirrelmail/plugins/squirrelmail-postfixadmin-0.4.3 /usr/share/squirrelmail/plugins/postfixadmin
cd /usr/share/squirrelmail/plugins/

chown -R root:root postfixadmin
cd postfixadmin
cp config.php.sample config.php
sed -i "s/postgres/mysql/" config.php
sed -i "s/xxxxx/pfix_password/" config.php
sed -i "s/autoreply.my.domain.com/autoreply.example.com/" config.php
sed -i "s#if(eregi('^select'#if(preg_match('/^select/i'#" functions.inc.php
wget http://verchick.com/mecham/public_html/spam/postfixadmin.wording.patch.txt
cp postfixadmin_forward.php postfixadmin_forward.php.original
patch -p0 < postfixadmin.wording.patch.txt

squirrelmail-configure


Then install the plugin (the order in which the plugins are listed may be different):
8. Plugins
 Plugins
  Installed Plugins
   1. view_as_html
   2. amavisnewsql
   3. check_quota
   4. timeout_user
   5. postfixadmin

S Save data
Q Quit


Turn the Vacation feature on:
sed -i "s/vacation'] = 'NO/vacation'] = 'YES/" /usr/share/postfixadmin/config.inc.php
sed -i "s/Vacation = false/Vacation = true/" /usr/share/squirrelmail/plugins/postfixadmin/config.php


Now users will not need to log into PostfixAdmin in order to change their password, set forwarding, or set Out of Office. Now they have similar functions in SquirrelMail (on the Options page). https://msa.example.com/mail

If you turn off vacation in PostfixAdmin, then you also need to turn off vacation in the SquirrelMail postfixadmin plugin. You should use https://msa.example.com/phpmyadmiNx to MAKE SURE there are no entries in the vacation table that are enabled. There should also be no records in the vacation_notification table and no aliases in the alias table that send mail to @autoreply.example.com):

sed -i "s/vacation'] = 'YES/vacation'] = 'NO/" /usr/share/postfixadmin/config.inc.php
sed -i "s/Vacation = true/Vacation = false/" /usr/share/squirrelmail/plugins/postfixadmin/config.php


To turn the Vacation feature back on:
sed -i "s/vacation'] = 'NO/vacation'] = 'YES/" /usr/share/postfixadmin/config.inc.php
sed -i "s/Vacation = false/Vacation = true/" /usr/share/squirrelmail/plugins/postfixadmin/config.php



Install MailZu

Index Top Bottom
This little section represents two days of experimentation and research just to get MailZu running. All mail left in the MailZu quarantine longer than 24 days is deleted.

apt-get install php-mail-mime php-mail-mimedecode

Patch the current version of php-mail-mimedecode
sed -i "s/\&new Mail_mimeDecode/new Mail_mimeDecode/" /usr/share/php/Mail/mimeDecode.php

We are going authenticate users using a couple imap functions (but we will actually use the imap functions to authenticate via POP3), so we loaded in the mod_imap module. With the encrypted passwords we have, POP3 was the only interface I could get to work. The IMAP interface might have worked were it not for the CRAM-MD5 setting we are using. It appears to confuse the imap_open function used to connect to the server. Use of POP3 is undocumented and requires a slight change in one line of code in IMAPAuth.class.php:
cd /var/www
wget http://verchick.com/mecham/public_html/spam/MailZu_0.8RC3.tar.gz
tar xzf MailZu_0.8RC3.tar.gz
mv MailZu_0.8RC3.tar.gz /usr/local/src/
mv MailZu_0.8RC3 mailzu
cd mailzu/config
cp config.php.sample config.php
cp config.php.sample config.php.original
wget http://verchick.com/mecham/public_html/spam/mzcpatch.txt
patch -p0 < mzcpatch.txt

We need to apply one small patch:
cd /var/www/mailzu/lib/
cp AmavisdEngine.class.php AmavisdEngine.class.php.original
wget http://verchick.com/mecham/public_html/spam/AmavisdEngine.patch1.txt
patch -p0 < AmavisdEngine.patch1.txt

Note that the configuration changes below will only work if you first apply my mzcpatch.txt patch. The only thing the patch does is change the example domain names (because they conflict with this howto).
cd /var/www/mailzu/config
sed -i "s/set_magic_quotes_runtime(0);/ini_set('magic_quotes_runtime', 0);/" init.php
sed -i "s/'user'/'amavis'/" config.php
sed -i "s/'pass'/'amavis_password'/" config.php
sed -i "s/'dbname'/'amavis'/" config.php
sed -i "s/hostname.domain.tld/localhost/" config.php
sed -i "s/binquar'] = false/binquar'] = true/" config.php
sed -i "s/'auth']\['serverType'] = 'ldap'/'auth']\['serverType'] = 'imap'/" config.php
sed -i "s|imaphost.domain.tld:143|localhost:110/pop3/novalidate-cert|" config.php
sed -i "s/'imap_type'] = 'imapssl'/'imap_type'] = 'imap'/" config.php
sed -i "s/'imap_domain_name'] = 'domain.tld'/'imap_domain_name'] = ''/" config.php
sed -i "s/mailzuhost.domain.tld/msa.example.com/" config.php
sed -i "s/'emailType'] = 'mail'/'emailType'] = 'sendmail'/" config.php
sed -i "s/support@domain.tld/postmaster@example.com/" config.php
cp ../lib/IMAPAuth.class.php ../lib/IMAPAuth.class.php.original
sed -i "s/, OP_HALFOPEN//" ../lib/IMAPAuth.class.php
cp ../lib/DBEngine.class.php ../lib/DBEngine.class.php.original
sed -i 's/dbtype/dbType/' ../lib/DBEngine.class.php
sed -i 's/COUNT(content)/COUNT(msgs.content)/' ../lib/DBEngine.class.php
sed -i 's/WHERE content=/WHERE msgs.content=/' ../lib/DBEngine.class.php
touch /var/log/mailzu.log
chown www-data:www-data /var/log/mailzu.log
chmod 660 /var/log/mailzu.log
chown -R root:www-data /var/www/mailzu
chmod 640 config.php


cd /etc/logrotate.d
wget http://verchick.com/mecham/public_html/spam/mailzu.logrotate.txt
mv mailzu.logrotate.txt mailzu


In 50-user we need to change a few things.
vi /etc/amavis/conf.d/50-user

Change this:
$inet_socket_port = [10024, 10026];
## If using Mailzu, use this instead:
#$inet_socket_port = [10024, 10026, 9998];


To this (add 9998 to $inet_socket_port):
#$inet_socket_port = [10024, 10026];
## If using Mailzu, use this instead:
$inet_socket_port = [10024, 10026, 9998];


Change:
$inet_socket_bind = '127.0.0.1';
## If using Mailzu, use this instead:
#$inet_socket_bind = undef;


To (listen on all interfaces):
#$inet_socket_bind = '127.0.0.1';
## If using Mailzu, use this instead:
$inet_socket_bind = undef;


Then enable the policy bank; change:
## Interface to MailZu
#$interface_policy{'9998'} = 'MAILZU';
#$policy_bank{'MAILZU'} = {
#    protocol => 'AM.PDP',
#    inet_acl => [qw( 127.0.0.1 [::1] 111.111.111.111 )],
#};


Make sure this next entry contains our local IP address (where 111.111.111.111 is the IP address of this machine):
## Interface to MailZu
$interface_policy{'9998'} = 'MAILZU';
$policy_bank{'MAILZU'} = {
    protocol => 'AM.PDP',
    inet_acl => [qw( 127.0.0.1 [::1] 111.111.111.111 )],
};


Enable quarantine to SQL; uncomment these two lines as shown:
# If using MailZu, store banned files and spam to MySQL if you want to give users the
# ability to read those messages in the MailZu interface:

$banned_files_quarantine_method = 'sql:';
$spam_quarantine_method         = 'sql:';


Once finished editing, restart amavisd-new:
/etc/init.d/amavis restart

Quiet down logcheck:
echo "amavis\[[0-9]+\]: \(rel-.{12}\) Quarantined message release" >>/etc/logcheck/ignore.d.server/amavisd-new
echo "amavis\[[0-9]+\]: \(rel-.{12}\) Quarantine release" >> /etc/logcheck/ignore.d.server/amavisd-new
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) \(\!\)Exceeded storage quota" >>/etc/logcheck/ignore.d.server/amavisd-new
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) NOTICE: Virus scanning skipped: Exceeded storage quota" >>/etc/logcheck/ignore.d.server/amavisd-new
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) \(\!\)Inserting header field: X-Amavis-Hold: Exceeded storage quota" >>/etc/logcheck/ignore.d.server/amavisd-new
echo "^\w{3} [ :[:digit:]]{11} [._[:alnum:]-]+ amavis\[[[:digit:]]+\]: \([-[:digit:]]+\) \(\!\)NOTICE: HOLD reason: Exceeded storage quota" >>/etc/logcheck/ignore.d.server/amavisd-new


This should be enough configuration for you to log in (as a regular user). https://msa.example.com/mailzu. Now you can start experimenting. Log into SquirrelMail https://msa.example.com/mail and change the recipient's SpamAssassin settings to "All spam to quarantine". This particular policy will quarantine at a score of 8 or higher, and a spam_quarantine_cutoff_level is not in force. Send the gtube string (which will insure the message scores over 12) and then wait for it to show up in the quarantine. Note however that if you are sending spam from a network in @mynetworks (in 50-user) that amavisd-new will use the MYNETS policy bank which will override the recipient's settings. This policy bank will quarantine spam and notify postmaster@example.com. Release the quarantined message and see if it is delivered. Double check your settings in 50-user if you get an error. Review the "Amavisd-new Configuration" notes at vi /var/www/mailzu/docs/INSTALL . Make sure the hostname of the server resolves properly. If necessary, read the mailing list archives.

Now you will want to vi /var/www/mailzu/config/config.php and familiarize yourself with all the possible settings - including verbose logging. You may wish to make changes. One thing you will need to do is to create an administrator or two. See this section:
// Super Admins can do anything mail admins can plus
// change settings
$conf['auth']['s_admins'] = array ('user1', 'user2');

// Mail Admins can see other users' spam and attachments
// and can perform any action on them
$conf['auth']['m_admins'] = array ();
You will want to spend some time sending banned files and spam to your test user and having the test user release some spam and request release of banned files. You will want to spend some time as the administrator, responding to this user's requests. As Jerry Seinfeld says: "Well.. good luck with all that!".

It's possible that it took 30 seconds for you to log into Mailzu. This is because we have configured Dovecot to offer both PLAIN and CRAM-MD5 authentication methods and the imap_open PHP function (which uses c-client) tries to use CRAM-MD5 before trying PLAIN. If the CRAM-MD5 Auth fails (which it will if you have not manually entered a clear-text password in the 'mailbox.clear' field in the postfix database) then after 30 seconds it will try the next Auth method (PLAIN). PLAIN should work because we have a password in the 'mailbox.password' field. See https://bugs.php.net/bug.php?id=33500. If you want to avoid this delay, we can ask imap_open to not even try using CRAM-MD5. The only issue you may have by making this change would be if you do actually use clear-text passwords, you would have to ensure that the user also has a password in the 'mailbox.password' field. You can imagine that there could be potential issues if the two passwords differ. Regardless, if you want to turn off CRAM-MD5 Auth for MailZu in order to speed up log in, issue this command:

sed -i "s/imap_open(\$host, \$username, \$password);/imap_open(\$host, \$username, \$password, NULL, 1, array('DISABLE_AUTHENTICATOR' => 'CRAM-MD5'));/" /var/www/mailzu/lib/IMAPAuth.class.php

If you should ever want to turn revert this change:
sed -i "s/imap_open(\$host, \$username, \$password, NULL, 1, array('DISABLE_AUTHENTICATOR' => 'CRAM-MD5'));/imap_open(\$host, \$username, \$password);/" /var/www/mailzu/lib/IMAPAuth.class.php


Quota

Index Top Bottom
When we create a new mailbox, the mail_profile.sh script enters the quota information in a 'maildirsize' file located in the user's maildir. When we edit a user's mailbox, the quotaedit.sh script updates the quota information. We use Maildir++ quota control. The web page for Dovecot quota is http://wiki.dovecot.org/Quota. Each time a message comes or goes from a user's INBOX, the 'maildirsize' file is updated. Because we are using Dovecot's 'deliver' as an LDA (Local Delivery Agent), Postfix has no way of knowing how many bytes of data the recipient's mailbox contains, but 'deliver' can figure it out. In dovecot.conf I have set Dovecot to warn users when their mailbox is 70% full and once again when their mailbox is 90% full:
quota_warning = storage=90%% /usr/sbin/quota-warning.sh 90
quota_warning2 = storage=70%% /usr/bin/quota-warning.sh 70
Notice that these entries call a script that places warning messages directly into inboxes. You can edit the message text in the script as you like. Once over quota, mail to the recipient will be held on our machine in the Postfix deferred queue. This is due to our setting:
quota_full_tempfail = yes

We need to grab this script:
cd /usr/sbin
wget http://verchick.com/mecham/public_html/spam/quota-warning.sh.txt
mv quota-warning.sh.txt quota-warning.sh
chmod +x quota-warning.sh
sed -i 's/host.domain.tld/msa.example.com/' quota-warning.sh


You will need to edit the textual part of the script (notably the telephone number):
vi /usr/sbin/quota-warning.sh

To test all of this, you would first empty out all mail from the test user (I use test@example.com). https://msa.example.com/mail would be handy for this task (and to test). Then log in to https://msa.example.com/postFixadminx and edit that user's mailbox, setting the quota to 1MB. Verify maildirsize was updated:
cat /var/vmail/example.com/test/maildirsize

My system shows:
1024000S
           0            0


Find a file that will not get rejected by amavisd-new (.txt .doc .xls .html .pdf) with a size around 70KB that can be used as an attachment. We want to send a series of messages to the test user that contains this attachment. We want to trigger the quota warning message by sending a series of messages that will total over 900KB (use the Check Mail link in the SquirrelMail interface to refresh the quota usage data). Once you are over 70% or 90%, if you log out and log back in to SquirrelMail, you will see a warning message (these particular thresholds are set in /usr/share/squirrelmail/plugins/check_quota/config.php). The recipient should also get our scripted message with the Subject: "IMPORTANT: Your Mailbox is X% full!". The recipient is only warned once at 70% and once at 90%, so if you want to test again, you will have to remove some mail, then send some mail again.
Once you are satisfied with your message, send another message that puts this user over the top of their quota. If you look in mail.log you will see that the message has not been sent to the user but instead has been placed in the deferred queue (which can also be confirmed with the mailq command):
Mar 14 06:32:56 msa postfix/pipe[24553]: 0961525F51: to=<test@example.com>, relay=dovecot, delay=0.43,  delays=0.14/0.04/0/0.26, dsn=4.3.0, status=deferred (temporary failure. Command output:  Quota exceeded (mailbox for user is full) )

You will also get (expected) error messages in /var/log/dovecot-deliver.log:
2011-05-21 05:37:49 deliver(test@example.com): Error: sieve: msgid=<cf1357e15f877e57e2a3f359f77aad62.squirrel@msa.example.com>: failed to store into mailbox 'INBOX': Quota exceeded (mailbox for user is full)
2011-05-21 05:37:49 deliver(test@example.com): Error: sieve: script /var/sieve/global.sieve failed with unsuccessful implicit keep

So now this mail will be stuck in the deferred queue until the user either deletes some messages to make room for it, you increase their quota, or you wait 5 days. Logcheck will notify root of the failure to deliver. If this user is unresponsive to requests to clean up their mailbox, all new mail for this user will get deferred. Once the mail is deferred for 5 days (Postfix $maximal_queue_lifetime), a bounce will be sent to the sender and the message will be removed from the queue. If your user is unresponsive to the request to clean up their mailbox, you may want to manually intervene and start rejecting mail to this user instead of holding on to it for them (don't be tempted to delete mail from the deferred queue when this machine goes into production - it's not your mail to delete). To do so, you could add this recipient to /etc/postfix/reject_over_quota with something like this (this is all on one line):
test@example.com REJECT The recipient's mailbox is full - your message was not delivered to recipients hosted on the example.com mail server

Then of course:
postmap /etc/postfix/reject_over_quota

The major downside to this is that the entire message will get rejected. If the message is addressed to multiple recipients on your server, not a single one of them will receive the message. This can't be fair to the sender, or the other recipients, so I would consider it only in extreme cases (if at all). After you test, remove all entries from /etc/postfix/reject_over_quota and postmap it again.

As you work with squirrelmail and other programs, it's also a good idea to keep an eye on the Apache2 error log:
tail -f /var/log/apache2/error.log

Optional. In general, when mail is delayed, the sender is not notified of the delay. If you would like any sender whose mail is delayed (deferred) for any reason, you can have Postfix notify them. Here we give Postfix one hour to attempt delivery before sending a warning to the sender:
postconf -e "delay_warning_time = 1h"
postfix reload



Install Mailgraph - Optional

Index Top Bottom
Mailgraph produces daily, weekly, monthly and yearly graphs of received/sent and bounced/rejected mail.
apt-get install mailgraph

sed -i 's/IGNORE_LOCALHOST=false/IGNORE_LOCALHOST=true/' /etc/default/mailgraph
/etc/init.d/mailgraph restart


It may take a minute for this to work, but eventually if you browse to https://msa.example.com/cgi-bin/mailgraph.cgi you should get some graphs.


Install mysql-zrm

Index Top Bottom
This nice little program can perform backups of our MySQL data. We will make local backups (and keep 5 days worth). Keep an eye on free disk space once you get going, copies of your MySQL databases will take up some space. We will talk about backing up the entire system later.
apt-get install libxml-parser-perl bsd-mailx

cd /usr/local/src
wget http://www.zmanda.com/downloads/community/ZRM-MySQL/2.2/Debian/mysql-zrm_2.2.0_all.deb
dpkg -i mysql-zrm_2.2.0_all.deb

sed -i 's/#user="wikiuser"/user="root"/' /etc/mysql-zrm/mysql-zrm.conf
sed -i 's/#password="userwiki"/password="roots_password"/' /etc/mysql-zrm/mysql-zrm.conf
sed -i 's/#retention-policy=10W/retention-policy=5D/' /etc/mysql-zrm/mysql-zrm.conf
sed -i 's/#mailto="mysqldba@company.com"/mailto="root@example.com"/' /etc/mysql-zrm/mysql-zrm.conf
chown root:root /usr/share/man/man5/mysql-zrm*
chown root:root /usr/share/man/man1/mysql-zrm*
chmod 644 /usr/share/man/man5/mysql-zrm*
chmod 644 /usr/share/man/man1/mysql-zrm*

mysql-zrm-scheduler --now --backup-set dailyrun --backup-level 0

mysql-zrm-scheduler --add --interval daily --backup-set dailyrun --backup-level 0


This places entries in root's crontab. Run crontab -e and see if you have any extra newline characters separating any entries (and edit them out). The details of what just happened can be found here: http://www.howtoforge.com/mysql_zrm_debian_sarge. I suggest you save all five pages of that HOWTO to your computer (and maybe read them too). This program will send you a couple (annoying) email messages each day. You can turn them off if you like by commenting out the "mailto" line in /etc/mysql-zrm/mysql-zrm.conf.

Here is the user's manual: http://mysqlbackup.zmanda.com/index.php/Zmanda_Recovery_Manager_for_MySQL_Users_Manual

The alias issue

Index Top Bottom
At this point, we have a little problem. The problem is with aliases. When using amavisd-new with Postfix, alias expansion occurs twice; once when a message comes in the front door via one of the smtpd daemons, and once again when amavis returns the message to the Postfix reinjection port (listening on port 10025). So, address rewriting occurs both before amavisd-new, and after amavisd-new. You need to decide whether it's better to disable expansion before amavisd-new or after amavisd-new. There are advantages and disadvantages in going one way or the other.

For each user added via PostfixAdmin, there is an entry created in the postfix.alias table in the form:

test@example.com test@example.com

If a user adds a forwarder via either the SquirrelMail or PostfixAdmin interface and they choose the option to "Deliver to the local mailbox", or they have Out of Office set, the user's alias record may now look something like this:

test@example.com test@example.com,garyv@example.net

In this example, test@example.com is a real user on our system, and the garyv@example.net mailbox is hosted by someone else. So, the message will now be addressed to both test@example.com and garyv@example.net. After amavisd-new processes the message, two separate messages emerge, one to test@example.com, and one to garyv@example.net. It's the one addressed to test@example.com that is the problem. Since the reinjection port at 127.0.0.1:10025 is just another smtpd daemon (which uses the cleanup daemon to facilitate rewriting the address), Postfix addresses this message to both test@example.com and garyv@example.net (again). The result is, garyv@example.net gets two copies of the same message (test@example.com only gets one copy because the message was already addressed to test@example.com).

When I first wrote this document, I disabled address rewriting before amavisd-new (I used "-o receive_override_options=no_address_mappings" on the Internet facing smtpd daemons). There is a major advantage to doing it this way. The advantage is that only one message will be processed by amavisd-new, so if it gets blocked due to virus/spam/banned then that is the end of life for that message (this is a good thing). If it expands to all the recipients before being processed by amavisd-new, then this could result in duplicate messages in quarantine or Out of Office messages sent to people whose addresses were forged in a spam message. To disable alias expansion prior to amavisd-new, vi /etc/postfix/master.cf and uncomment the lines as show below (but please read this entire section before doing so):
smtp      inet  n       -       -       -       -       smtpd
  -o smtpd_sasl_auth_enable=no
  -o content_filter=smtp-amavis:[127.0.0.1]:10024
  -o receive_override_options=no_address_mappings
# If they want to relay, make them use port 587 (submission) or port 465 (smtps)
# If using submission port, configure client to use CRAM-MD5
submission inet n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=may
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
  -o receive_override_options=no_address_mappings  
# Outlook and OE (and others) expect smtpd_tls_wrappermode,
# so have them submit here (port 465):
smtps     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
  -o receive_override_options=no_address_mappings
# We will use port 4650 for clients that use STARTTLS:
4650     inet  n       -       -       -       -       smtpd
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject
  -o content_filter=smtp-amavis:[127.0.0.1]:10026
  -o receive_override_options=no_address_mappings
#628      inet  n       -       -       -       -       qmqpd
Then of course:
/etc/init.d/postfix restart
This works well if all mail that comes in is addressed to "hard" users (users with actual mailboxes), but if you (as the administrator) want to create an alias something like this:

test.user@example.com test@example.com

where test@example.com has a mailbox (and is in the amavis database) and test.user@example.com is created by the admin merely as a convenient way to redirect mail to the actual account, this does not work well if address rewriting only occurs after amavisd-new. The reason is test.user@example.com would end up using the statically configured spam/virus/banned settings in 50-user or amavisd.conf rather than the settings test@example.com desired (the settings stored in the amavis MySQL database). By the way, since test.user@example.com has no mailbox (in other words it is not in the PostfixAdmin postfix database), you would have to manually add this "before hand" type of address translation to the /etc/postfix/virtual hash table. So, what can we do? If we want to disable alias expansion after amavisd-new one option would be to edit the 127.0.0.1:10025 smtpd daemon and add no_address_mappings to the receive_override_options override:
127.0.0.1:10025 inet n    -    n    -    -    smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_delay_reject=no
    -o smtpd_client_restrictions=permit_mynetworks,reject
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o smtpd_data_restrictions=reject_unauth_pipelining
    -o smtpd_end_of_data_restrictions=
    -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_address_mappings,no_header_body_checks,no_unknown_recipient_checks,no_milters
If desired (not required), you can increase the flexibility of what Postfix does before and after amavisd-new by creating two cleanup services. In my setup, the service called "cleanup" is used before amavisd-new and I create another called "amavis-cleanup" that is used after amavisd-new.
vi /etc/postfix/master.cf and copy the current cleanup daemon, name the copy amavis-cleanup and add an override that adds an additional virtual alias maps table (just as an example):
cleanup   unix  n       -       -       -       0       cleanup
amavis-cleanup   unix  n       -       -       -       0       cleanup
  -o virtual_alias_maps=hash:/etc/postfix/amavis_virtual
qmgr      fifo  n       -       n       300     1       qmgr
Then, on the reinjection port 127.0.0.1:10025, tell Postfix to use this new cleanup daemon for mail that amavisd-new sends to it:
127.0.0.1:10025 inet n    -    n    -    -    smtpd
    -o content_filter=
    -o cleanup_service_name=amavis-cleanup
    -o local_recipient_maps=
	<...>
Save and exit the file, then:
/etc/init.d/postfix stop
/etc/init.d/postfix start


You can get more creative with this (if you need to). I suggest reading: http://www.amavis.org/README.postfix.html#d0e1038 and http://www.ijs.si/software/amavisd/README.postfix.html and http://www.ijs.si/software/amavisd/README.postfix.old

Note: if the user uses the PostfixAdmin interface to change the Forwarding alias(es) and they choose the "Forward to given email addresses only." option (as opposed to the "Deliver to the local mailbox." option), the postfix.alias record would look more like this:
test@example.com garyv@example.net

In this example, garyv@example.net is still a remote mailbox. Keep in mind that remote mailboxes are not going to have their email address in the amavis MySQL database (unless you manually add them), so this type of recipient will end up using the default (static) settings in the 50-user or amavisd.conf files. Now you must choose whether you do alias expansion before or after amavisd-new.

Alias Domains

Index Top Bottom
PostfixAdmin has a feature that allows you to add alias domains (Virtual List, Add Alias Domain). Let's say you have a situation where you have mailboxes in the example.com domain, but now you have registered a new domain name of example.net (which you have added to PostfixAdmin) and want all mail addressed to users @example.net (the Alias Domain) to be rewritten so that it ends up in the mailboxes for the example.com users (the Target Domain). Once you have added the domain and created the aliasing in PostFixadmin, you need to make a change to Postfix for it to work. Also, it's quite likely that you will have to allow address rewriting to occur before amavisd-new (the second option shown above in "The alias issue"). I have not tested this extensively, but on the surface it seems to work. Note: after adding any new domains in PostfixAdmin, please reload amavisd-new.

cd /etc/postfix
cp main.cf main.cf-before-alias-domains
wget http://verchick.com/mecham/public_html/spam/mysql_virtual_alias_domains_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_virtual_alias_domains_maps.cf
postconf -e "virtual_alias_maps = proxy:mysql:/etc/postfix/mysql_virtual_alias_maps.cf, proxy:mysql:/etc/postfix/mysql_virtual_alias_domains_maps.cf, hash:/etc/postfix/virtual"


/etc/init.d/postfix restart
/etc/init.d/amavis restart


Then test by sending mail to a user using both @domain addresses while using tail -f /var/log/mail.log

Acting as a relay server

Index Top Bottom
If you will be acting as relay server for at least one domain, read this entire section before proceeding. You may have domains that are not going to store their mail on this server but instead want this box to clean up their mail for them and then relay it on to them. You use relay_domains and transport_maps to configure those domains you wish to relay to. The biggest issue you have to deal with in this scenario is how to reject mail to invalid users. The downstream servers may know who their valid users are, but this server does not. Nonetheless, you must obtain this information one way or another so you can reject mail to invalid users.

Postfix offers two different ways of figuring out if a recipient's address is valid or not. In one method, you supply a list of valid recipients, and Postfix looks up the address in the table. If a recipient is not in the table, the mail is rejected. In the second method, Postfix probes the downstream server prior to accepting the message. If the downstream server rejects the attempt to send a message to a particular recipient, then Postfix will also reject the message. The second method only works when the downstream server in fact does immediately reject mail to invalid users (not all do). in my example below, I use a combination of both.

But first we need to get to the point where mail is routed to the downstream servers. Note that the current mysql_virtual_domains_maps.cf is replaced with one that is compatible with mysql_relay_domains_maps.cf:
cd /etc/postfix
test -e mysql_relay_domains_maps.cf && mv mysql_relay_domains_maps.cf mysql_relay_domains_maps.cf-old
wget http://verchick.com/mecham/public_html/spam/mysql_relay_domains_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_relay_domains_maps.cf
mv mysql_virtual_domains_maps.cf mysql_virtual_domains_maps.cf-before-relay-domains
wget http://verchick.com/mecham/public_html/spam/mysql_virtual_domains_maps.cf.v2
mv mysql_virtual_domains_maps.cf.v2 mysql_virtual_domains_maps.cf
sed -i 's/password = postfix/password = pfix_password/' mysql_virtual_domains_maps.cf
chmod 640 mysql_*
chown root:postfix mysql_*
postconf -e "relay_domains = proxy:mysql:/etc/postfix/mysql_relay_domains_maps.cf"
postconf -e "transport_maps = hash:/etc/postfix/transport"
touch /etc/postfix/transport
postmap /etc/postfix/transport


We are not going to store transport map data in MySQL (if later you want to, I leave it up to you to figure out how to do it). Each domain you relay for will need an entry in the transport map. The left hand side is the domain and the right hand side is the host name or IP address of the next hop server (with square brackets used to turn off MX lookups). This is the general format of the contents of /etc/postfix/transport (you can vi /etc/postfix/transport and enter valid data for your situation):
example.net relay:[777.777.777.777]
example.org relay:[mail.example.org]


Use https://msa.example.com/postFixadminx to add the relay domain(s). When you add the domain(s), check the "Mail server is backup MX:". The domain is now a relay domain. Using this configuration, you cannot add mailboxes to relay domains. You also cannot create aliases in relay domains. Once the domains have been added, you can reload postfix, and because we have added a domain or domains to /var/lib/amavis/local_domains we should also reload amavisd-new:
postmap /etc/postfix/transport
postfix reload
/etc/init.d/amavis restart
cat /var/lib/amavis/local_domains


Now on to rejecting mail to invalid recipients. Let's say we have added two domains, one (example.net) relays mail to a server running sendmail and the other (example.org) relays mail to a server running Microsoft Exchange 2000. Our server will accept mail addressed to any recipient in either domain. This particular sendmail server is configured to immediately reject mail to invalid users. When it rejects a message, Postfix will create a bounce notice and attempt to deliver it to the sender. If the sender is completely bogus, the message will sit in our deferred queue for days while delivery attempts are made. If the sender is faked but points to a real address, then we are spamming an innocent victim. This victim is getting "joe jobbed" - and we are facilitating it - and now we are a source of backscatter. If we send a message to the domain that forwards to the sendmail server, we can see from the bounce notice that the downstream server (at the hypothetical address of 777.777.777.777) rejected it:
<testgarbage@example.net>: host msa.example.com[111.111.111.111] said: 550
    <testgarbage@example.net>: Recipient address rejected: undeliverable
    address: host 777.777.777.777[777.777.777.777] said: 550 5.1.1
    <testgarbage@example.net>... User unknown (in reply to RCPT TO command)
    (in reply to RCPT TO command)
In this case (the case being the downstream server immediately rejects mail to invalid users) we can use either address verification or relay_recipient_maps. Basically, with address verification (reject_unverified_recipient), Postfix first checks the downstream server to see if it will accept a message to the recipient or not, prior to accepting the message. If the downstream server rejects the message (due to invalid address), so will Postfix (before it accepts the message). On the other hand, if you use relay_recipient_maps, relay_recipient_maps requires that all known good recipient addresses (for the domains listed in relay_domains) are in a lookup table. Mail addressed to a recipient whose domain is listed in relay_domains that is not also listed in the table defined in relay_recipient_maps is rejected.

If we relay a message to the Exchange server, the particular Exchange server in our example accepts the message and later generates a bounce notice which it mails to the sender. We can only use relay_recipient_maps in this case where the downstream server does not immediately reject messages addressed to invalid users. Let's continue by first setting up address verification for example.net (the domain using the sendmail server). I will illustrate this mixed setup in which both reject_unverified_recipient and relay_recipient_maps will be utilized. Keep in mind that for your setup, you will have to discover which relay domains of yours will immediately reject mail to invalid users and which will not (if you want to use reject_unverified_recipient that is). Keep in mind that use of reject_unverified_recipient is a convenience over use of relay_recipient_maps, but the ideal situation would be exclusive use of relay_recipient_maps for all your relay domains. Also keep in mind that you could set up relay_recipient_maps in an SQL table and create a query for it in the same fashion we have done for other Postfix SQL tables, but regardless of the type of table used to store the data, the question is - who will maintain the adding and deleting of email addresses?
vi /etc/postfix/verify_domains

and insert the domain(s) that use server(s) that will immediately reject mail to unknown users:
example.net reject_unverified_recipient

then postmap it:
postmap /etc/postfix/verify_domains

vi /etc/postfix/main.cf , make smtpd_recipient_restrictions pretty like this and add the red item in the position shown. Read the beginning (half dozen lines or so) of http://www.postfix.org/postconf.5.html:
smtpd_recipient_restrictions = 
    permit_mynetworks,
    permit_sasl_authenticated,
    reject_unauth_destination,
    reject_unlisted_recipient,
    check_recipient_access hash:/etc/postfix/verify_domains,
    check_recipient_access hash:/etc/postfix/reject_over_quota,
    check_sender_access hash:/etc/postfix/rbl_sender_exceptions,
    check_client_access hash:/etc/postfix/rbl_client_exceptions,
    check_recipient_access hash:/etc/postfix/rbl_recipient_exceptions,
    reject_rbl_client sbl-xbl.spamhaus.org
Also add these items to main.cf:
address_verify_map = btree:/var/lib/postfix/verify_cache
# Uncomment this next line when finished testing address verification:
#unverified_recipient_reject_code = 550


postfix reload and test the setup (by sending mail to invalid users in the relay domain and reading the log). When finished testing, uncomment the unverified_recipient_reject_code line.

With the other domain (example.org on the Exchange server) we need to set up relay_recipient_maps. We will create /etc/postfix/relay_recipients. Once relay_recipient_maps is configured in main.cf, any recipient or recipient domain of a relay domain not listed in /etc/postfix/relay_recipients will get rejected. This means /etc/postfix/relay_recipients must contain every single recipient of every single relay domain this machine accepts mail for. However, domain wildcards can be used as placeholders. In our example case, example.net is already using address verification to reject mail to invalid users, so we can use a wildcard for that domain: @example.net. For example.org we can start out with a wildcard while we are in the process of gathering valid addresses:
vi /etc/postfix/relay_recipients

@example.net 1
@example.org 1


but once we have all the address gathered, we need to get rid of the wildcard:
@example.net 1
#@example.org 1
user1@example.org 1
user2@example.org 1
user3@example.org 1
user4@example.org 1


Then of course:
postmap /etc/postfix/relay_recipients

The "1" after each entry could be something else, like "OK" or something. It's not important what the actual text is because it's not used for anything, but it must exist. To set up relay_recipient_maps:
postconf -e "relay_recipient_maps = hash:/etc/postfix/relay_recipients"
postfix reload


Don't forget to uncomment #unverified_recipient_reject_code = 550 once address verification is known to work as expected (assuming you are using reject_unverified_recipient via the verify_domains map). Then test, test, test (make sure you send some spam). You may need to configure @spam_kill_level_maps and/or @spam_tag2_level_maps in /etc/amavis/conf.g/50-user for the relay domains. While testing, you may need to temporarily remove your network from @mynetworks in /etc/amavis/conf.g/50-user so the MYNETS policy bank is not loaded for your client. If you are using Exchange, there are some HOWTOs out there that may help with pulling data out of Active Directory:
http://verchick.com/mecham/public_html/spam/postfix_exchange.html
http://www.unixwiz.net/techtips/postfix-exchange-users.html
http://postfix.state-of-mind.de/patrick.koetter/mailrelay/
http://verchick.com/mecham/public_html/spam/PostfixAddressExtract.vbs.txt


That's it for recipient validation for relay domains. I'm going to change the subject a little. Since there are no mailboxes for relay domains, we cannot log into SquirrelMail to change amavisd-new settings (actually, in a moment I explain how you can work around this). At the moment all relay domain recipients will use the static settings in amavisd.conf and/or the other configuration files in /etc/amavis/conf.d and elsewhere (settings like $sa_kill_level_deflt). You can configure per-user or per-domain static settings there, or you can manually add the policies and users to MySQL. To get additional information on the amavis SQL setup, I suggest you jump ahead to the top of page 2 and read the "Amavisd-new lookups" section, then return here.

Each user in the amavis database (a user can also be an entire domain) must be linked to a policy. The policy can define a number of settings, such as spam_tag2_level and spam_kill_level for example. Each policy or user in the amavis database must have a unique id, so each time a policy or user is updated in the SquirrelMail interface (which can happen at any moment) the policy_id_seq and the users_id_seq are updated so the application can keep track of which number it used for the last policy or user. If you have just one or two relay users you need to add, you can cheat and put our test user to good use. In phpMyAdmin edit the amavis 'users' table for the test user, replacing all the fields that need to be replaced - like email, fullname and username (but leave the id alone). The next time the test user logs in and goes to the SpamAssassin Configuration page a new record will be created. You can edit this one also and repeat the process, logging the test user in and out of SquirrelMail each time you need to create a new record.

If you need to enter a larger number of users or policies it would be easier to increment the users_id_seq (for example) to a number large enough to accommodate the number of records you are going to add - before you add them. Then make sure the id numbers in the records are both unique and within the allocated range of numbers. Here (as an example) we change users_id_seq from 15 to 19 in order to accommodate 3 new users (we left 16 unused in case someone uses it before we can change 15 to 19). Once users_id_seq is set to 19, the SquirrelMail plugin will use 20 as the next number, so 17 through 19 is ours:
USE amavis;
SELECT id FROM users_id_seq;
+----+
| 15 |
+----+
UPDATE `users_id_seq` SET `id` = '19';

INSERT INTO users (id, priority, policy_id, email, fullname, digest, username, retention)
           VALUES (17,7,6,'user1@example.net','User 1','WD','user1@example.net',14);
INSERT INTO users (id, priority, policy_id, email, fullname, digest, username, retention)
           VALUES (18,7,6,'user2@example.net','User 2','WD','user2@example.net',14);
INSERT INTO users (id, priority, policy_id, email, fullname, digest, username, retention)
           VALUES (19,7,6,'user3@example.net','User 3','WD','user3@example.net',14);
The same concept would apply to policies.


Now that you get a picture of some of the issues related to relay_domains, there is a way to act as a relay server but still allow users to log into the SquirrelMail interface to change spam settings and also quarantine spam and banned file to MailZu. Additionally, this would allow users in relay_domains to use SASL.

In this alternate setup, when adding a domain in PostfixAdmin, you would add it as a regular domain and not as a relay domain (in other words you would not check "Mail server is backup MX:"). But then you would have to stop using the MySQL database as the source for virtual_mailbox_domains and relay_domains. You would have to manually add all your current local domains to a new hash map (we will call it /etc/postfix/virtual_domains) and then change:
virtual_mailbox_domains = proxy:mysql:/etc/postfix/mysql_virtual_domains_maps.cf
to:
virtual_mailbox_domains = hash:/etc/postfix/virtual_domains

For example, add all local domains to /etc/postfix/virtual_domains in this manner:
example.com 1

and then postmap the file when finished:
postmap /etc/postfix/virtual_domains

You would have to do the same for the domains you relay to, only you need to change:
relay_domains = proxy:mysql:/etc/postfix/mysql_relay_domains_maps.cf
to:
relay_domains = hash:/etc/postfix/relay_domains


and the format of data in /etc/postfix/relay_domains would be:
example.net 1

and postmap the file:
postmap /etc/postfix/relay_domains

Be sure to /etc/init.d/restart postfix . Be sure to test. You can now add mailboxes to the relay domains. Users in relay domains will now be able to log into Mailzu and SquirrelMail. They will get items in the quarantine if their amavis policy is configured to do so. They can also change their own settings in SquirrelMail. Relay domain users should not be getting mail in SquirrelMail.

This does not change the need to reject mail to invalid users (as it applies to relay domains) as explained above. Just because a user in a relay domain is not in SQL does not mean mail to that user will be rejected. If one is willing to go to the small extra effort of manually maintaining a couple tables that PostfixAdmin would usually maintain, it appears one can reap substantial benefit in respect to maintaining relay domains. You can give relay domain users all the benefits that virtual users have without actually storing their mail.


Handy links to your site:
https://msa.example.com/mail
http://msa.example.com/cacert.example.com.crt
https://msa.example.com/phpmyadmiNx
https://msa.example.com/mailzu
https://msa.example.com/postFixadminx
https://msa.example.com/cgi-bin/mailgraph.cgi

Continue to Page 2

Index Top  Page 1  Page 2

NOV 27 2011