The aim is to provide a mail server that serves any number of domains, and integrates access from MTAs and web browsers. The environment uses Gentoo Linux, Exim, Courier IMAP and SquirrelMail. The target users are clubs, societies, small companies and other smallish, relatively low load setups.
The server I have built has no other services running on it. Exim is configured to check for Viruses and spam. Virus infected mails are dropped, spam is flagged, but delivered. MySQL is used to hold the list of domains and users. All components use the database to obtain authentication and authorisation data.
USE="amavis apache2 exim exiscan exiscan-acl filter imap maildir mbox mysql mysqli php server spell spf srs syslog threads vhosts xml -fortran ssl"and then do 'emerge -n world' using this world file:
app-admin/procinfo www-servers/apache sys-libs/gpm net-mail/courier-imap mail-filter/spamassassin www-apache/mod_auth_mysql mail-client/squirrelmail dev-db/mysql app-admin/apache-tools app-admin/sudo mail-client/mailx net-misc/rdate dev-libs/openssl app-portage/gentoolkit app-portage/mirrorselect dev-lang/php sys-process/vixie-cron app-editors/nano dev-perl/Archive-Zip sys-apps/xinetd dev-perl/IP-Country sys-boot/grub sys-apps/parted app-editors/vim net-analyzer/netcat mail-filter/razor dev-python/pyzor app-admin/sysklogd dev-perl/Mail-ClamAV net-misc/whois net-analyzer/nmap dev-perl/DBD-mysql mail-mta/exim sys-kernel/vanilla-sources sys-process/cronbase net-misc/openssh sys-apps/module-init-tools net-dns/bind app-portage/ufed app-text/rcs www-apache/mod_perl dev-perl/Mail-SPF-Query net-firewall/iptables sys-apps/less sys-apps/baselayout net-misc/ntp app-antivirus/clamav net-nds/portmap dev-perl/MIME-tools net-analyzer/tcpdump
Use rc-update to configure your startup scripts as follows:
apache2 | default
bootmisc | boot
checkfs | boot
checkroot | boot
clamd | default
clock | boot
consolefont | boot
courier-authlib | default
courier-imapd | default
courier-imapd-ssl | default
courier-pop3d-ssl | default
exim | default
hostname | boot
iptables | default
keymaps | boot
local | default nonetwork
localmount | boot
modules | boot
mysql | default
net.lo | boot
netmount | default
nfs | default
ntpd | default
rmnologin | boot
spamd | default
sshd | default
sysklogd | boot
urandom | boot
vixie-cron | default
xinetd | default
The mail is going to live in /virtual. If this is a separate disk partition you can increase system safety by adding
mount -o ro,remount /to /etc/conf.d/local.start.
Here is my iptables setup, as stored in /etc/conf.d/local.start:
The variable LOCAL_NET covers the set of IP adresses that I own and that are visible on the internet. PRIVATE_NET covers those IP addresses that can only be used internally to my network.PRIVATE_NET=172.16.0.0/12 LOCAL_NET=62.49.238.224/28 IPTABLES="iptables " $IPTABLES -F INPUT $IPTABLES -A INPUT -p udp -m multiport --dports 1024:65535 -j ACCEPT $IPTABLES -A INPUT -p tcp -m tcp --dport domain -j ACCEPT $IPTABLES -A INPUT -p udp -m multiport --dports domain,ntp -j ACCEPT $IPTABLES -A INPUT -i lo -j ACCEPT $IPTABLES -A INPUT -p tcp -m multiport \ --dports ident,ssh,smtp,http,ntp,imap,pop3,https,pop3s,imaps \ -j ACCEPT $IPTABLES -A INPUT -p tcp -s $LOCAL_NET -j ACCEPT # This next line of for Microsoft software, which is unable to # use the standard ports. $IPTABLES -A INPUT -p tcp -m multiport --dports 465,587 -j ACCEPT $IPTABLES -A INPUT -p icmp -s $PRIVATE_NET -j ACCEPT $IPTABLES -A INPUT -p tcp -s $PRIVATE_NET -j ACCEPT $IPTABLES -A INPUT -p icmp -s $LOCAL_NET -j ACCEPT $IPTABLES -A INPUT -p tcp -s $LOCAL_NET -j ACCEPT $IPTABLES -A INPUT -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT $IPTABLES -P INPUT DROP
Setting 'server pool.ntp.org' in /etc/ntp.conf should be adequate. If you have a private network with your own NTP server you may prefer to have all your other machines pointing to that private server.
You only need clamd and freshclam, not amavisd.
Clamd can talk over either a Unix or a TCP socket. Do not try to use the Unix socket, you will get all sorts of permissions problems. Instead, use the TCP socket: it may be slightly less efficient, but it works with no problems. Ensure that your exim.conf matches whatever you configure here.
You might get away with using the Unix socket if you make user 'mail' part of the 'clamav' group, and make user 'clamav' part of the 'mail' group, but I have not tested this configuration. Failing this you have to make 'clamd' run as user 'mail, and ensure that all its files are owned by 'mail'.
First, set a root password in MySQL. Next, create the necessary DB and table structure:
create database Mail;
use Mail;
-- Which domains do we service
create table Domains (domain varchar(255)) max_rows=100
if not exists Domains;
-- The users in each domain
create table if not exists Users(
user varchar(255) primary key,-- $user@$domain
password varchar(255) not null, -- clear text password
or 'cryptpwd'
forward varchar(1024), -- where to
forward all mail
expiry bigint not null default 0,
quota varchar(64),
homedir varchar(1024) not null,
maildir varchar(1024) not null,
uid int not null default 8, -- User 'mail'
gid int not null default 12,-- Group 'mail'
realName varchar(255),
force_change_pwd smallint default 1 -- New users must
change their passwords
)
max_rows=1000;
grant select on Mail.*
to mail@localhost identified by 'MailPassword';
grant select on Mail.*
to mail@127.0.0.1 identified by 'MailPassword';
grant update (password,force_change_pwd) on Mail.Users
to mail@localhost identified by 'MailPassword';
grant update (password,force_change_pwd) on Mail.Users
to mail@127.0.0.1 identified by 'MailPassword';
We store passwords in clear, but encourage users to use a form of authentication that does not transmit clear-text passwords over the net, either CRAM-MD5 or SSL/TLS. CRAM-MD5 requires that we store clear-text passwords. For PLAIN and LOGIN authentication we can use either plain-text or encrypted passwords.
I shall deal here only with creating a self-signed certificate. This is adequate for small setups so long as your users are aware that they will get a warning message the first time that they connect to your system and that they need to explictly accept the certificate.
These commands will generate a private key in privkey.pem and a self-signed certificate valid for 10 years (3650 days) in cacert.pem. You can copy these files to wherever your configuration files require you to have them. They will be needed for Apache SSL, Exim SSL/TLS, IMAPS (IMAP with SSL/TLS) and for POP3 with SSL/TLS.openssl genrsa -out privkey.pem 2048 openssl req -new -x509 -key privkey.pem -out cacert.pem -days 3650
As you generate the files, openssl will prompt you for various data. The CN (common name) should be the true name of your server, most of the other values are obvious but their actual values are not terribly important. Just make them sensible so that you do not confuse your users.
The local host list is hard coded in exim.conf, but the local domain list is obtained from a DB query. Authentication of users is done with the DB.
Each domain may have an alias file that is checked before we check for local user names. Aliases are private to each domain; an alias in one domain does not interfere with an identical alias in another domain.
Ensure that communication with Clamd for virus checking is done over the TCP socket.
SMTP Send requires that the user be authenticated. CRAM-MD5 authentication is permittted over an unencrypted channel; PLAIN and LOGIN authentication require encryption, either SSL or TLS.
The Exim configuration file is too large to include here verbatim. Instead, here is the diff between my exim.conf and the original distribution file:
59,76d58
< hide mysql_servers = localhost/myDatabase/myUser/myPassword
<
< # Exim only cares whether this query succeeds, not what it returns
< DOMAIN_LIST = select domain from Domains where domain='$domain'
<
< # In these cases we *do* want the actual data
< USER_QUERY = select forward, homedir from Users \
< where user = \
< concat(lcase('$local_part'), '@', lcase('$domain'))
< PLAINAUTH_QUERY = select 1 from Users \
< where user = lcase('$auth2') \
< and password='$auth3'
< LOGINAUTH_QUERY = select 1 from Users \
< where user = lcase('$auth1') \
< and password='$auth2'
< CRAMMD5AUTH_QUERY = select password from Users \
< where user = lcase('$auth1')
<
78d59
< domainlist virtual_domains = mysql;DOMAIN_LIST
81d61
< #hostlist relay_from_hosts = 127.0.0.1 : 172.16.0.0/12 : 62.49.238.224/28
142,145c122
< # av_scanner=clamd:/var/run/clamav/clamd.sock
< av_scanner = clamd:127.0.0.1 3310
< # for sophos scanner
< # av_scanner = cmdline: /usr/local/bin/sweep -all -archive -ss %s: found:'(.+)'
---
> # av_scanner = clamd:/tmp/clamd
153c130
< spamd_address = 127.0.0.1 783
---
> # spamd_address = 127.0.0.1 783
166c143
< tls_advertise_hosts = *
---
> # tls_advertise_hosts = *
174,175c151,152
< tls_certificate = /etc/ssl/exim.crt
< tls_privatekey = /etc/ssl/exim.pem
---
> # tls_certificate = /etc/ssl/exim.crt
> # tls_privatekey = /etc/ssl/exim.pem
186,187c163,164
< daemon_smtp_ports = 25 : 465 : 587
< tls_on_connect_ports = 465
---
> # daemon_smtp_ports = 25 : 465 : 587
> # tls_on_connect_ports = 465
314c291
< split_spool_directory = true
---
> # split_spool_directory = true
363c340
< domains = +local_domains : +virtual_domains
---
> domains = +local_domains
378c355
< domains = !+local_domains : !+virtual_domains
---
> domains = !+local_domains
386c363
< domains = +local_domains :!+virtual_domains
---
> domains = +local_domains
427c404
< domains = +local_domains : +relay_to_domains : +virtual_domains
---
> domains = +local_domains : +relay_to_domains
443,445c420,422
< deny message = rejected because $sender_host_address is in a blacklist at $dnslist_domain\n$dnslist_text
< dnslists = dnsbl.sorbs.net : relays.ordb.org : spam.dnsrbl.net
<
---
> # deny message = rejected because $sender_host_address is in a black list at $dnslist_domain\n$dnslist_text
> # dnslists = black.list.example
> #
481,482c458,459
< deny malware = *
< message = This message contains a virus ($malware_name).
---
> # deny malware = *
> # message = This message contains a virus
($malware_name).
488,499c465,469
<
< # reject spam at high scores (> 8)
< deny spam = nobody:true
< condition = ${if >{$spam_score_int}{8}{1}{0}}
< message = This message scored $spam_score spam points.
<
<
< warn spam = nobody:true
< add_header = X-Spam_score: $spam_score\n\
< X-Spam_score_int: $spam_score_int\n\
< X-Spam_bar: $spam_bar\n\
< X-Spam_report: $spam_report
---
> # warn spam = nobody
> # add_header = X-Spam_score: $spam_score\n\
> # X-Spam_score_int: $spam_score_int\n\
> # X-Spam_bar: $spam_bar\n\
> # X-Spam_report: $spam_report
547c517
< domains = ! +local_domains : !+virtual_domains
---
> domains = ! +local_domains
551a522
>
555,571d525
< virtual_user:
< driver = accept
< domains = +virtual_domains
< router_home_directory=${extract {homedir}{${lookup mysql{USER_QUERY}}}{$value}fail}
< condition = ${if gt{$home}{}}
< transport = virtual_delivery
<
< virtual_alias:
< driver = redirect
< domains = +virtual_domains
< local_part_suffix = +*
< local_part_suffix_optional
< data = ${if exists{/virtual/${domain}/mail/aliases}{${lookup{$local_part}lsearch*{/virtual/${domain}/mail/aliases}}}}
< qualify_preserve_domain
< file_transport = address_file
< pipe_transport = address_pipe
<
597,598c551
< domains = +local_domains
< data = ${lookup{$local_part}lsearch*{/etc/mail/aliases}}
---
> data = ${lookup{$local_part}lsearch{/etc/mail/aliases}}
687,699d639
< # Courier-imap assumes that the delivery will be to $homedir/Maildir
< virtual_delivery:
< driver = appendfile
< directory=${extract {homedir}{${lookup mysql{USER_QUERY}}}{$value}fail}/Maildir
< delivery_date_add
< envelope_to_add
< return_path_add
< user = mail
< maildir_format
< maildir_use_size_file
< quota = ${lookup mysql{select quota from Domains where domain='$domain'}{$value}{20M}}
< quota_warn_threshold = 75%
<
762,765c702
< # Immediate bounce of over-quota messages
< * quota
< #Catchall rule for other errors
< * * F,2h,15m; G,16h,1h,1.5; F,4d,6h
---
> * * F,2h,15m; G,16h,1h,1.5; F,4d,6h
808,813c745,750
< PLAIN:
< driver = plaintext
< server_set_id = $2
< server_prompts = :
< server_condition = ${lookup mysql{PLAINAUTH_QUERY}{$value}fail}
< server_advertise_condition = ${if def:tls_cipher }
---
> #PLAIN:
> # driver = plaintext
> # server_set_id = $auth2
> # server_prompts = :
> # server_condition = Authentication is not yet configured
> # server_advertise_condition = ${if def:tls_cipher }
820,830c757,762
< LOGIN:
< driver = plaintext
< server_set_id = $1
< server_prompts = <| Username: | Password:
< server_condition = ${lookup mysql{LOGINAUTH_QUERY}{$value}fail}
< server_advertise_condition = ${if def:tls_cipher }
<
< lookup_cram:
< driver = cram_md5
< public_name = CRAM-MD5
< server_secret = ${lookup mysql{CRAMMD5AUTH_QUERY}{$value}fail}
---
> #LOGIN:
> # driver = plaintext
> # server_set_id = $auth1
> # server_prompts = <| Username: | Password:
> # server_condition = Authentication is not yet configured
> # server_advertise_condition = ${if def:tls_cipher }
832d763
< server_set_id = $1
First, configure /etc/courier-imap/imapd. Ensure that IMAP_CAPABILITY contains 'AUTH=CRAM-MD5'. I ensure that rubbish does not lie around for too long by including
IMAP_EMPTYTRASH=Trash:7,Sent:30,Spam:14The MAILDIR and MAILDIRPATH are both set to 'Maildir'. A common alternative is '.maildir', but you must ensure that courier-imap, courier-pop3, exim and the database all specify the same name.
For SSL you have to alter /etc/courier-imap/imapd-ssl. About all you need to do is to alter TLS_TRUSTCERTS to
TLS_TRUSTCERTS=/etc/ssl/certs/which is where openssl has put all the standard certificates and, again, to set up MAILDIR and MAILDIRPATH.
Copy the private key and certificate files that you generated to wherever your config file says they need to go.
In /etc/courier/authlib ensure that you have
authmodulelist="authmysql "Then in authmysqlrc you need
MYSQL_SERVER localhost MYSQL_USERNAME mail MYSQL_PASSWORD MailPassword # The socket address varies from installation to installation, # check that it is right for your system. MYSQL_SOCKET /var/run/mysqld/mysqld.sock MYSQL_PORT 3306 MYSQL_DATABASE Mail MYSQL_USER_TABLE Users MYSQL_CLEAR_PWFIELD password MYSQL_UID_FIELD uid MYSQL_GID_FIELD gid MYSQL_LOGIN_FIELD user MYSQL_HOME_FIELD homedir MYSQL_NAME_FIELD realName MYSQL_MAILDIR_FIELD maildirThese lines are scatterd though the file. You have to find them and edit them appropriately. We do not actually care about the uid and gid fields, but Courier-imap insists on having them.
Include
POP3AUTH="CRAM-MD5" POP3AUTH_ORIG="PLAIN LOGIN CRAM-MD5 CRAM-SHA1 CRAM-SHA256" POP3AUTH_TLS_ORIG="LOGIN PLAIN" POP3DSTART=YES MAILDIRPATH=Maildir MAILDIR=Maildirat the appropriate places in /etc/courier-imap/pop3d.
The configuration you set up for IMAP will also apply for POP3; there is no need to do anything extra.
Apart from making sure that the certificate file exists and is enabled in /etc/courier-imap/pop3d-ssl (copy the files you made earlier) there is almost nothing to do. You can decide for yourself whether to force TLS for all users, or to allow also-non-encrypted sessions. This is configured with POP3_TLS_REQUIRED.
As always, make sure you have MAILDIR and MAILDIRPATH properly configured.
Run /usr/share/webapps/squirrelmail/$version/htdocs/config/conf.pl to configure your system.
You will need the administrator, compatibility and change_sqlpass plugins. The last two are not part of the standard Squirrelmail package and you will have to download them separately from the plugins repository. You may also decide to install other plugins such as calendar, squirrelspell and filters. 'Filters' allows users to set up rules to filter their mail and move it to private folders or perform other actions on it.
For security, http protocol is redirected to https, however for initial testing I did everything on http. Note that all my domains are named explictly in the configuration blocks.
<VirtualHost mail.mankin.org.uk:80>
ServerName mail.mankin.org.uk
Redirect / https://mail.mankin.org.uk/
</VirtualHost>
<IfDefine SSL>
<IfDefine SSL_DEFAULT_VHOST>
<IfModule ssl_module>
# see bug #178966 why this is in here
# When we also provide SSL we have to listen to the HTTPS port
# Note: Configurations that use IPv6 but not IPv4-mapped addresses need two
# Listen directives: "Listen [::]:443" and "Listen 0.0.0.0:443"
<VirtualHost mail.mankin.org.uk:443>
ServerName mail.mankin.org.uk
ScriptAlias /cgi-bin /usr/share/webapps/squirrelmail/1.4.10a-r2/hostroot/cgi-bin
Alias /icons /usr/share/webapps/squirrelmail/1.4.10a-r2/hostroot/icons
DocumentRoot /usr/share/webapps/squirrelmail/1.4.10a-r2/htdocs/
<Location /cgi-bin>
Order deny,allow
Deny from all
Allow from 62.49.238.224/28
Allow from 172.16.0.0/12
Allow from 127.0.0.1
</Location>
<Location />
Options Indexes
DirectoryIndex index.php
Order allow,deny
Allow from All
</Location>
<Location /po>
Order deny,allow
Deny from all
</Location>
<Location /config>
Order deny,allow
Deny from all
</Location>
ErrorLog /var/log/apache2/ssl_error_log
<IfModule log_config_module>
TransferLog /var/log/apache2/ssl_access_log
</IfModule>
## SSL Engine Switch:
# Enable/Disable SSL for this virtual host.
SSLEngine on
## SSL Cipher Suite:
# List the ciphers that the client is permitted to negotiate.
# See the mod_ssl documentation for a complete list.
SSLCipherSuite ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP:+eNULL
## Server Certificate:
# Point SSLCertificateFile at a PEM encoded certificate. If the certificate
# is encrypted, then you will be prompted for a pass phrase. Note that a
# kill -HUP will prompt again. Keep in mind that if you have both an RSA
# and a DSA certificate you can configure both in parallel (to also allow
# the use of DSA ciphers, etc.)
SSLCertificateFile /etc/apache2/ssl/server.crt
#SSLCertificateFile /etc/apache2/ssl/server-dsa.crt
## Server Private Key:
# If the key is not combined with the certificate, use this directive to
# point at the key file. Keep in mind that if you've both a RSA and a DSA
# private key you can configure both in parallel (to also allow the use of
# DSA ciphers, etc.)
SSLCertificateKeyFile /etc/apache2/ssl/server.key
#SSLCertificateKeyFile /etc/apache2/ssl/server-dsa.key
## Server Certificate Chain:
# Point SSLCertificateChainFile at a file containing the concatenation of
# PEM encoded CA certificates which form the certificate chain for the
# server certificate. Alternatively the referenced file can be the same as
# SSLCertificateFile when the CA certificates are directly appended to the
# server certificate for convinience.
#SSLCertificateChainFile /etc/apache2/ssl/ca.crt
## Certificate Authority (CA):
# Set the CA certificate verification path where to find CA certificates
# for client authentication or alternatively one huge file containing all
# of them (file must be PEM encoded).
# Note: Inside SSLCACertificatePath you need hash symlinks to point to the
# certificate files. Use the provided Makefile to update the hash symlinks
# after changes.
#SSLCACertificatePath /etc/apache2/ssl/ssl.crt
#SSLCACertificateFile /etc/apache2/ssl/ca-bundle.crt
## Certificate Revocation Lists (CRL):
# Set the CA revocation path where to find CA CRLs for client authentication
# or alternatively one huge file containing all of them (file must be PEM
# encoded).
# Note: Inside SSLCARevocationPath you need hash symlinks to point to the
# certificate files. Use the provided Makefile to update the hash symlinks
# after changes.
#SSLCARevocationPath /etc/apache2/ssl/ssl.crl
#SSLCARevocationFile /etc/apache2/ssl/ca-bundle.crl
## Client Authentication (Type):
# Client certificate verification type and depth. Types are none, optional,
# require and optional_no_ca. Depth is a number which specifies how deeply
# to verify the certificate issuer chain before deciding the certificate is
# not valid.
#SSLVerifyClient require
#SSLVerifyDepth 10
## Access Control:
# With SSLRequire you can do per-directory access control based on arbitrary
# complex boolean expressions containing server variable checks and other
# lookup directives. The syntax is a mixture between C and Perl. See the
# mod_ssl documentation for more details.
#<Location />
# #SSLRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \
# and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \
# and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \
# and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \
# and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \
# or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/
#</Location>
## SSL Engine Options:
# Set various options for the SSL engine.
## FakeBasicAuth:
# Translate the client X.509 into a Basic Authorisation. This means that the
# standard Auth/DBMAuth methods can be used for access control. The user
# name is the `one line' version of the client's X.509 certificate.
# Note that no password is obtained from the user. Every entry in the user
# file needs this password: `xxj31ZMTZzkVA'.
## ExportCertData:
# This exports two additional environment variables: SSL_CLIENT_CERT and
# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the server
# (always existing) and the client (only existing when client
# authentication is used). This can be used to import the certificates into
# CGI scripts.
## StdEnvVars:
# This exports the standard SSL/TLS related `SSL_*' environment variables.
# Per default this exportation is switched off for performance reasons,
# because the extraction step is an expensive operation and is usually
# useless for serving static content. So one usually enables the exportation
# for CGI and SSI requests only.
## StrictRequire:
# This denies access when "SSLRequireSSL" or "SSLRequire" applied even under
# a "Satisfy any" situation, i.e. when it applies access is denied and no
# other module can change it.
## OptRenegotiate:
# This enables optimized SSL connection renegotiation handling when SSL
# directives are used in per-directory context.
#SSLOptions +FakeBasicAuth +ExportCertData +StrictRequire
<FilesMatch "\.(cgi|shtml|phtml|php)$">
SSLOptions +StdEnvVars
</FilesMatch>
<Directory "/var/www/localhost/cgi-bin">
SSLOptions +StdEnvVars
</Directory>
## SSL Protocol Adjustments:
# The safe and default but still SSL/TLS standard compliant shutdown
# approach is that mod_ssl sends the close notify alert but doesn't wait
# for the close notify alert from client. When you need a different
# shutdown approach you can use one of the following variables:
## ssl-unclean-shutdown:
# This forces an unclean shutdown when the connection is closed, i.e. no
# SSL close notify alert is send or allowed to received. This violates the
# SSL/TLS standard but is needed for some brain-dead browsers. Use this when
# you receive I/O errors because of the standard approach where mod_ssl
# sends the close notify alert.
## ssl-accurate-shutdown:
# This forces an accurate shutdown when the connection is closed, i.e. a
# SSL close notify alert is send and mod_ssl waits for the close notify
# alert of the client. This is 100% SSL/TLS standard compliant, but in
# practice often causes hanging connections with brain-dead browsers. Use
# this only for browsers where you know that their SSL implementation works
# correctly.
# Notice: Most problems of broken clients are also related to the HTTP
# keep-alive facility, so you usually additionally want to disable
# keep-alive for those clients, too. Use variable "nokeepalive" for this.
# Similarly, one has to force some clients to use HTTP/1.0 to workaround
# their broken HTTP/1.1 implementation. Use variables "downgrade-1.0" and
# "force-response-1.0" for this.
<IfModule setenvif_module>
BrowserMatch ".*MSIE.*" \
nokeepalive ssl-unclean-shutdown \
downgrade-1.0 force-response-1.0
</IfModule>
## Per-Server Logging:
# The home of a custom SSL log file. Use this when you want a compact
# non-error SSL logfile on a virtual host basis.
<IfModule log_config_module>
CustomLog /var/log/apache2/ssl_request_log \
"%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b"
</IfModule>
</VirtualHost>
</IfModule>
</IfDefine>
</IfDefine>
# vim: ts=4 filetype=apache
Copy the private key and certificate files that you generated to wherever your config file says they need to go.
If you are going to permit access to these scripts from outside your local network you will have to change the Allow directives for /cgi-bin in the Apache configuration.
#! /usr/bin/perl
# Author: Raphael Mankin
use strict;
use DBD::mysql;
use File::Path;
use constant DB => "Mail";
use constant HOST => "localhost";
use constant USER => "root";
use constant PWD => "RootPassword";
use constant MAIL => "mail";
my ($login, $passwd, $uid, $gid) = getpwnam(MAIL) or
die "MAIL user not found";;
$| = 1;
my $dsn = "DBI:mysql:database=".DB.";host=".HOST;
my $dbh = DBI->connect($dsn, USER, PWD)
or die "Cannot connect to database";
print "Domain: ";
my $domain = <>;
chomp $domain;
$domain = lc $domain;
my $sth = $dbh->prepare("select domain from Domains where domain='$domain'");
$sth->execute();
my $res;
while (my $ref = $sth->fetchrow_hashref()) {
$res = $ref->{domain};
}
die "Domain $domain already exists" if $res;
my $domainDir = "/virtual/$domain/mail";
mkpath $domainDir unless -d $domainDir;
my $aliasFile = "$domainDir/aliases";
unless (-f $aliasFile) {
open(FH, ">", $aliasFile) or die "Cannot create alias file: $aliasFile";
print FH "#Empty alias file for $domain\n";
close FH;
}
my $sql = qq[insert into Domains (domain)
values ( '$domain')
];
$dbh->do($sql) or die "Cannot insert domain into DB";
print "Created $domain\n";
$dbh->disconnect();
exit 0;
#! /usr/bin/perl
# Author: Raphael Mankin
use strict;
use DBD::mysql;
use File::Path;
use constant DB => "Mail";
use constant HOST => "localhost";
use constant USER => "root";
use constant PWD => "RootPassword";
use constant MAIL => "mail";
my ($login, $passwd, $uid, $gid) = getpwnam(MAIL) or
die "MAIL user not found";;
$| = 1;
my $dsn = "DBI:mysql:database=".DB.";host=".HOST;
my $dbh = DBI->connect($dsn, USER, PWD)
or die "Cannot connect to database";
print "Real name of User: ";
my $realName = <>;
chomp $realName;
my @realName = split(/\s+/, $realName);
my $userName = join('.', @realName);
print "Domain: ";
my $domain = <>;
chomp $domain;
$domain = lc $domain;
my $sth = $dbh->prepare("select domain from Domains where domain='$domain'");
$sth->execute();
my $res;
while (my $ref = $sth->fetchrow_hashref()) {
$res = $ref->{domain};
}
die "No such domain: $domain" unless $res;
$userName = lc $userName;
print "Password:";
my $password = <>;
chomp $password;
print "Real Name: $realName\n";
print "Login name: $userName\n";
my $homedir = "/virtual/$domain/mail/$userName";
my $maildir = "$homedir/Maildir";
# the login name is the full user@domain.
$userName .= "\@$domain";
mkpath $homedir unless -d $homedir;
system("/usr/bin/maildirmake $maildir") unless -d $maildir;
system ("chown -R mail:mail $homedir");
my $sql = qq[insert into Users (user, password, homedir, maildir, realName)
values (
'$userName',
'$password',
'$homedir',
'$maildir',
'$realName'
)
];
$dbh->do($sql) or die "Cannot insert user into DB";
print "Created $userName\n";
$dbh->disconnect();
exit 0;
#! /usr/bin/perl
# Author: Raphael Mankin
use strict;
use DBD::mysql;
use File::Path;
use constant DB => "Mail";
use constant HOST => "localhost";
use constant USER => "root";
use constant PWD => "RootPassword";
use constant MAIL => "mail";
my ($login, $passwd, $uid, $gid) = getpwnam(MAIL) or
die "MAIL user not found";;
$| = 1;
my $dsn = "DBI:mysql:database=".DB.";host=".HOST;
my $dbh = DBI->connect($dsn, USER, PWD)
or die "Cannot connect to database";
print "Real name of User: ";
my $realName = <>;
chomp $realName;
my @realName = split(/\s+/, $realName);
my $userName = join('.', @realName);
print "Domain: ";
my $domain = <>;
chomp $domain;
$domain = lc $domain;
my $sth = $dbh->prepare("select domain from Domains where domain='$domain'");
$sth->execute();
my $res;
while (my $ref = $sth->fetchrow_hashref()) {
$res = $ref->{domain};
}
die "No such domain: $domain" unless $res;
$userName = lc $userName;
print "Real Name: $realName\n";
print "Login name: $userName\n";
my $homedir = "/virtual/$domain/mail/$userName";
# the login name is the full user@domain.
$userName .= "\@$domain";
my $sql = qq[delete Users where userName= '$userName') ];
$dbh->do($sql) or die "Cannot delete user from DB";
rmtree $homedir if -d $homedir;
print "Deleted $userName\n";
$dbh->disconnect();
exit 0;
Several people have set up similar systems to this, however, I did not find their descriptions either quite matched what I was trying to do, or entirely explicit: there were gaps in their documents. I hope that this document fills in some of the gaps, but I am also providing links to sources that I found useful.
Configuring Clam AV: http://www200.pair.com/mecham/spam/clamav-amavisd-new.html
Exim 4 Documentation: http://exim.org/exim-html-4.50/doc/html/"
OpenSSL - Documents, Misc: http://www.openssl.org/docs/
Courier Mail Server: http://www.courier-mta.org/
Installing and configuring courier IMAP http://edseek.com/~jasonb/articles/exim4_courier/courierimap.html
Virtual Domains with Exim + Courier-IMAP + MySQL: http://www.tty1.net/virtual_domains_en.html
A Complete Virtual System - Web Access - Gentoo Linux Wiki: Complete Virtual Mail Server with Gentoo This is a link into the Gentoo wiki; it changes from time to time as the article gets edited. You may have to search for it, rather than following this link.
System -> Postfix+ Courier-IMAP +MySQL for multiple domains HOWTO: http://www.linuxhowtos.org/System/postfix.htm
SSL Certificate HOWTO: http://www.gtlib.cc.gatech.edu/pub/linux/docs/HOWTO/other-formats/html_single/SSL-Certificates-HOWTO.html