Petit tutoriel pas à pas pour s'installer un serveur de mails virtuel sous FreeBSD. Les utilisateurs et les domaines seront stockés dans une base de donnée MySQL ; les protocoles POP3 et IMAP4 (avec ou sans SSL) seront supportés, l'authentification pour l'envoi de mail également.

Pour commencer, nous allons créer un certificat SSL. Méthode gruik'n'dirty pour les besoins du tutoriel : pour quelquechose de plus propre, se reporter à http://httpd.apache.org/docs/2.2/ssl/ssl_faq.html#selfcert par exemple.

mail# cd /etc/ssl/
mail# openssl req -new -x509 -nodes -out server.crt -keyout server.key

On installe d'abord MySQL :

mail# cd /usr/ports/databases/mysql50-server/
mail# make install clean

On le démarre :

mail# vi /etc/rc.conf
mysql_enable="YES"
mail# /usr/local/etc/rc.d/mysql-server start
Starting mysql.

On vérifie qu'il est bien là :

mail# netstat -anf inet
tcp4       0      0  *.3306    *.*                    LISTEN

Au tour d'apache :

mail# cd /usr/ports/www/apache22/
mail# make install clean

PHP avec le support de MySQL :

mail# cd /usr/ports/lang/php5
mail# make install clean
[X] APACHE     Build Apache module
mail# cd /usr/ports/lang/php5-extensions
mail# make install clean
[X] MYSQL       MySQL database support
[X] SESSION    session support 

On configure apache :

mail# vi /usr/local/etc/apache22/httpd.conf
ServerAdmin you@example.com
...
<IfModule dir_module>
    DirectoryIndex index.html
</IfModule>
...
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
...
#Include etc/apache22/extra/httpd-ssl.conf

en

ServerAdmin viny@gcu.info
...
<IfModule dir_module>
    DirectoryIndex index.html index.php
</IfModule>
...
    AddType application/x-compress .Z
    AddType application/x-gzip .gz .tgz
    AddType application/x-httpd-php .php
    AddType application/x-httpd-php-source .phps
...
Include etc/apache22/extra/httpd-ssl.conf
mail# vi /usr/local/etc/apache22/extra/httpd-ssl.conf
SSLCertificateFile /usr/local/etc/apache22/server.crt
...
SSLCertificateKeyFile /usr/local/etc/apache22/server.key
...
ServerName www.example.com:443
ServerAdmin you@example.com

en

SSLCertificateFile /etc/ssl/server.crt
...
SSLCertificateKeyFile /etc/ssl/server.key
...
ServerName mail.gcu.info:443
ServerAdmin viny@gcu.info

Un p'tit fichier pour tester :

mail# echo "<?php phpinfo(); ?>" > /usr/local/www/apache22/data/index.php

C'est parti !

mail# vi /etc/rc.conf
apache22_enable="YES"
mail# /usr/local/etc/rc.d/apache22 start
Performing sanity check on apache22 configuration:
Syntax OK
Starting apache22.
mail# netstat -anf inet
tcp4       0      0  *.443     *.*                    LISTEN
tcp4       0      0  *.80      *.*                    LISTEN

On teste avec un navigateur en allant sur http://mail.gcu.info/index.php et https://mail.gcu.info/index.php. Vérifier la présence des modules MySQL et session.

On passe ensuite à l'installation de dovecot :

mail# cd /usr/ports/mail/dovecot/
mail# make install clean
[X] MYSQL     MySQL support
[X] LDA       LDA support

:!: Auparavant, l'utilisation d'un utilisateur non dédié pour le démon dovecot-auth n'entraînait qu'un simple avertissement. Avec les versions plus récentes, l'authentification refusera tout simplement de marcher. Le remède est simple :

mail# vipw

Ajouter la ligne :

dovecot-auth:*:144:144::0:0:Dovecot authentication user:/nonexistent:/usr/sbin/nologin

Puis :

echo "dovecot-auth:*:144:" >> /etc/group
mkdir -p /var/run/dovecot/login
chown dovecot-auth:dovecot-auth /var/run/dovecot/login

Postfix :

mail# cd /usr/ports/mail/postfix
mail# make install clean
[X] DOVECOT   Dovecot SASL authentication method
[X] TLS       Enable SSL and TLS support
[X] MYSQL     MySQL maps (choose version with WITH_MYSQL_VER)

On met à jour le rc.conf :

 mail# vi /etc/rc.conf
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"
postfix_enable="YES" dovecot_enable="YES"

Tant qu'on y est, vu que nous n'utiliserons plus sendmail :

mail# vi /etc/periodic.conf
daily_clean_hoststat_enable="NO"
daily_status_mail_rejects_enable="NO"
daily_status_include_submit_mailq="NO"
daily_submit_queuerun="NO"

Manque encore postfixadmin :

mail# cd /usr/ports/mail/postfixadmin
mail# make install clean
[X] MYSQL   MySQL back-end (use mysql PHP extension)

On met à jour la configuration d'apache :

mail# vi /usr/local/etc/apache22/httpd.conf
DocumentRoot "/usr/local/www/apache22/data"
...
<Directory "/usr/local/www/apache22/data">
...
AllowOverride None

en

DocumentRoot "/usr/local/www/postfixadmin"
...
<Directory "/usr/local/www/postfixadmin">
...
AllowOverride AuthConfig

Tant qu'à faire :

mail# vi /usr/local/etc/apache22/extra/httpd-ssl.conf
DocumentRoot "/usr/local/www/apache22/data"

en

DocumentRoot "/usr/local/www/postfixadmin"

Le contrôle de l'accès à l'interface d'administration se fait à l'aide d'un .htaccess (d'où le AllowOverride AuthConfig), dont il est préférable de changer tout au moins le mot de passe, voire le couple nom d'utilisateur / mot de passe :

mail# cd /usr/local/www/postfixadmin/admin
mail# htpasswd -cs .htpasswd viny

Il est nécessaire d'appliquer un patch au fichier functions.inc.php :

mail# vi /root/patch-functions.inc.php
--- functions.inc.php-old   Tue Apr  3 10:01:36 2007
+++ functions.inc.php      Tue Apr  3 10:05:32 2007
@@ -633,11 +633,6 @@
       $password = md5crypt ($pw, $salt);
    }
 
-   if ($CONF['encrypt'] == 'md5')
-   {
-      $password = md5($pw);
-   }
-
    if ($CONF['encrypt'] == 'system')
    {
       if (ereg ("\$1\$", $pw_db))
mail# cd /usr/local/www/postfixadmin
mail# patch -p0 < /root/patch-functions.inc.php

On redémarre apache :

mail# apachectl restart

Les bases étant posées, nous allons pouvoir retrousser nos manches et passer à la configuration du serveur de mails à proprement parler. Pour commencer, copions le fichier qui va nous servir à créer la base de donnée, et changeons les mots de passe par défaut un peu trop triviaux :

mail# cp /usr/local/www/postfixadmin/DATABASE_MYSQL.TXT /root/
mail# vi /root/DATABASE_MYSQL.TXT
INSERT INTO user (Host, User, Password) VALUES ('localhost','postfix',password('postfix'));
...
INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixadmin',password('postfixadmin'));

en

INSERT INTO user (Host, User, Password) VALUES ('localhost','postfix',password('riri'));
...
INSERT INTO user (Host, User, Password) VALUES ('localhost','postfixadmin',password('fifi'));

Maintenant que nous avons mis des mots de passe beaucoup moins triviaux, nous pouvons l'importer :

mail# mysql -u root < /root/DATABASE_MYSQL.TXT

Rajoutons l'utilisateur dovecot qui permettra à… dovecot (comment avez-vous deviner ?) d'accéder à la base de donnés :

mail# mysql -u root
mysql> grant SELECT on postfix.* to 'dovecot'@'localhost' IDENTIFIED by 'loulou';
mysql> flush privileges;
mysql> exit

Créons le répertoire dans lequel seront stockés les mails :

mail# mkdir -p /usr/vmail
mail# chmod 770 /usr/vmail
mail# chown dovecot:dovecot /usr/vmail

Le fichier de configuration de dovecot :

mail# vi /usr/local/etc/dovecot.conf
ssl_cert_file = /etc/ssl/server.crt
ssl_key_file = /etc/ssl/server.key
mail_location = maildir:/usr/vmail/%d/%u
mail_extra_groups = mail
first_valid_uid = 143
last_valid_uid = 143
maildir_copy_with_hardlinks = yes
protocols = imap pop3 imaps pop3s
disable_plaintext_auth = no
login_user = dovecot
pop3_uidl_format = %08Xu%08X
protocol imap {
  mail_plugins = quota imap_quota
  imap_client_workarounds = outlook-idle delay-newmail
}
protocol pop3 {
  mail_plugins = quota
  pop3_client_workarounds = outlook-no-nuls oe-ns-eoh
}
protocol lda {
  postmaster_address = postmaster@gcu.info
  mail_plugins = quota
  log_path = /var/log/dovecot-deliver.log
  info_log_path = /var/log/dovecot-deliver.log
}
auth default {
  mechanisms = plain
  passdb sql {
    args = /usr/local/etc/dovecot-sql.conf
  }
  userdb sql {
    args = /usr/local/etc/dovecot-sql.conf
  }
  userdb prefetch {
  }
  user = nobody
  socket listen {
    master {
      path = /var/run/dovecot/auth-master
      mode = 0660
      user = dovecot
      group = mail
    }
    client {
      path = /var/spool/postfix/private/auth
      mode = 0660
      user = postfix
      group = mail
    }
  }
}
dict {
}
plugin {
  quota = maildir:storage=10240:messages=1000
  acl = vfile:/usr/local/etc/dovecot-acls
  trash = /usr/local/etc/dovecot-trash.conf
}

Le fichier de logs n'existant pas, nous le créons et lui donnons les bons droits :

mail# touch /var/log/dovecot-deliver.log
mail# chmod 660 /var/log/dovecot-deliver.log
mail# chgrp mail /var/log/dovecot-deliver.log

Nous expliquons à dovecot comment parler à MySQL :

mail# vi /usr/local/etc/dovecot-sql.conf
driver = mysql
default_pass_scheme = MD5-CRYPT
connect = host=localhost dbname=postfix user=dovecot password=loulou
user_query = SELECT concat('/usr/vmail/', maildir) as home, concat('maildir:/usr/vmail/', maildir) as mail, 143 AS uid, 6 AS gid, concat('dirsize:storage=', quota) AS quota FROM mailbox WHERE username = '%u' AND active = '1'
password_query = SELECT username as user, password, concat('/usr/vmail/', maildir) as userdb_home, concat('maildir:/usr/vmail/', maildir) as userdb_mail, 143 as userdb_uid, 6 as userdb_gid FROM mailbox WHERE username = '%u' AND active = '1'

Précisons enfin ce que l'on veut dans notre poubelle :

mail# vi /usr/local/etc/dovecot-trash.conf
1 Spam
2 Trash

Avant dernière partie, la configuration de Postfix. D'abord le master.cf, à qui nous allons dire d'utiliser dovecot comme LDA. L'intérêt est que les mails seront indexés à la réception et non à la lecture lors de la connexion d'un utilisateur.

mail# vi /usr/local/etc/postfix/master.cf
...
# Dovecot LDA
dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=dovecot:mail argv=/usr/local/libexec/dovecot/deliver -d ${recipient}

Quelques fichiers permettant à postfix de retrouver ses petits :

mail# vi /usr/local/etc/postfix/mysql_virtual_alias_maps.cf
user            = postfix
password        = riri
hosts           = localhost
dbname          = postfix
query           = SELECT goto FROM alias WHERE address='%s' AND active = '1'
mail# vi /usr/local/etc/postfix/mysql_virtual_domains_maps.cf
user            = postfix
password        = riri
hosts           = localhost
dbname          = postfix
#query          = SELECT domain FROM domain WHERE domain='%s'
#optional query to use when relaying for backup MX
query           = SELECT domain FROM domain WHERE domain='%s' AND backupmx = '0' AND active = '1'
mail# vi /usr/local/etc/postfix/mysql_virtual_mailbox_limit_maps.cf
user            = postfix
password        = riri
hosts           = localhost
dbname          = postfix
query           = SELECT quota FROM mailbox WHERE username='%s' AND active = '1'
mail# vi /usr/local/etc/postfix/mysql_virtual_mailbox_maps.cf
user            = postfix
password        = riri
hosts           = localhost
dbname          = postfix
query           = SELECT CONCAT(domain,'/',maildir) FROM mailbox WHERE username='%s' AND active = '1'

Le main.cf :

mail# vi /usr/local/etc/postfix/main.cf
queue_directory = /var/spool/postfix
command_directory = /usr/local/sbin
daemon_directory = /usr/local/libexec/postfix
mail_owner = postfix
unknown_local_recipient_reject_code = 550
debug_peer_level = 2
debugger_command =
         PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
         xxgdb $daemon_directory/$process_name $process_id & sleep 5
sendmail_path = /usr/local/sbin/sendmail
newaliases_path = /usr/local/bin/newaliases
mailq_path = /usr/local/bin/mailq
setgid_group = maildrop
html_directory = no
manpage_directory = /usr/local/man
config_directory = /usr/local/etc/postfix
sample_directory = /usr/local/etc/postfix
readme_directory = no

# --------------- local settings ------------------
myhostname                      = mail.gcu.info
inet_interfaces                 = $myhostname
mynetworks                      = $config_directory/mynetworks
mydestination                   = localhost.$mydomain, localhost, $myhostname
#relay_clientcerts               = hash:$config_directory/relay_clientcerts
address_verify_map              = btree:/var/spool/postfix/address_verify
# ---------------------- VIRTUAL DOMAINS START ----------------------
virtual_mailbox_domains         = mysql:$config_directory/mysql_virtual_domains_maps.cf
virtual_mailbox_base            = /var/vmail
virtual_mailbox_maps            = mysql:$config_directory/mysql_virtual_mailbox_maps.cf
virtual_alias_maps              = mysql:$config_directory/mysql_virtual_alias_maps.cf
virtual_minimum_uid             = 143
virtual_uid_maps                = static:143
virtual_gid_maps                = static:6
virtual_transport               = dovecot
dovecot_destination_recipient_limit = 1
# ---------------------- VIRTUAL DOMAINS END ----------------------
# ---------------------- SASL PART START ----------------------
smtpd_sasl_auth_enable          = yes
#smtpd_sasl_local_domain        = $myhostname
smtpd_sasl_exceptions_networks  = $mynetworks
smtpd_sasl_security_options     = noanonymous
broken_sasl_auth_clients        = yes
smtpd_sasl_type                 = dovecot
# Can be an absolute path, or relative to $queue_directory
smtpd_sasl_path                 = private/auth
# ---------------------- SASL PART END ----------------------
# ---------------------- TLS PART START ----------------------
#smtp_tls_CAfile                 = /etc/pki/tls/certs/cert.pem
smtp_tls_cert_file              = /etc/ssl/server.crt
smtp_tls_key_file               = /etc/ssl/server.key
smtp_tls_session_cache_database = btree:/var/spool/postfix/smtp_tls_session_cache
smtp_tls_security_level = may
#smtpd_tls_CAfile                = /etc/pki/tls/certs/cert.pem
smtpd_tls_cert_file             = /etc/ssl/server.crt
smtpd_tls_key_file              = /etc/ssl/server.key
smtpd_tls_session_cache_database = btree:/var/spool/postfix/smtpd_tls_session_cache
smtpd_tls_security_level        = may
smtpd_tls_received_header       = yes
smtpd_tls_ask_ccert             = yes
smtpd_tls_loglevel              = 1
tls_random_source               = dev:/dev/urandom
# ---------------------- TLS PART END ----------------------
smtpd_helo_required             = yes
disable_vrfy_command            = yes
non_fqdn_reject_code            = 450
invalid_hostname_reject_code    = 450
maps_rbl_reject_code            = 450
smtpd_recipient_restrictions =
        permit_mynetworks
        permit_sasl_authenticated
        permit_tls_clientcerts
        reject_unauth_destination
        reject_invalid_helo_hostname
        warn_if_reject reject_non_fqdn_helo_hostname
        warn_if_reject reject_unknown_helo_hostname
        warn_if_reject reject_unknown_client
        reject_non_fqdn_sender
        reject_non_fqdn_recipient
        reject_unknown_sender_domain
        reject_unknown_recipient_domain
        warn_if_reject reject_unverified_sender
        reject_unverified_recipient
        reject_rbl_client cbl.abuseat.org
        reject_rbl_client list.dsbl.org
        reject_rbl_client opm.blitzed.org
        reject_rbl_client sbl.spamhaus.org
        reject_rbl_client bl.spamcop.net
        reject_rbl_client dnsbl.sorbs.net=127.0.0.2
        reject_rbl_client dnsbl.sorbs.net=127.0.0.3
        reject_rbl_client dnsbl.sorbs.net=127.0.0.4
        reject_rbl_client dnsbl.sorbs.net=127.0.0.5
        reject_rbl_client dnsbl.sorbs.net=127.0.0.7
        reject_rbl_client dnsbl.sorbs.net=127.0.0.9
        reject_rbl_client dnsbl.sorbs.net=127.0.0.11
        reject_rbl_client dnsbl.sorbs.net=127.0.0.12
        warn_if_reject reject_rhsbl_sender dsn.rfc-ignorant.org
        warn_if_reject reject_rhsbl_sender abuse.rfc-ignorant.org
        warn_if_reject reject_rhsbl_sender whois.rfc-ignorant.org 
        warn_if_reject reject_rhsbl_sender bogusmx.rfc-ignorant.org 
        warn_if_reject reject_rhsbl_sender postmaster.rfc-ignorant.org
        permit 
smtpd_data_restrictions = 
        reject_unauth_pipelining, 
        reject_multi_recipient_bounce,
        permit

Un petit coup de newaliases pour s'assurer que le fichier aliases.db est bien créé :

mail# newaliases

Et il ne nous reste plus que la configuration de postfixadmin, ce qui va nous prendre, oh, au moins 30 secondes :

mail# vi /usr/local/www/postfixadmin/config.inc.php
<?php
if (ereg ("config.inc.php", $_SERVER['PHP_SELF']))
{
   header ("Location: login.php");
   exit;
}
$CONF['postfix_admin_url'] = 'http://mail.gcu.info/admin';
$CONF['postfix_admin_path'] = '';
$CONF['default_language'] = 'en';
$CONF['database_type'] = 'mysql';
$CONF['database_host'] = 'localhost';
$CONF['database_user'] = 'postfixadmin';
$CONF['database_password'] = 'fifi';
$CONF['database_name'] = 'postfix';
$CONF['database_prefix'] = '';
$CONF['database_tables'] = array (
   'admin' => 'admin',
   'alias' => 'alias',
   'domain' => 'domain',
   'domain_admins' => 'domain_admins',
   'log' => 'log',
   'mailbox' => 'mailbox',
   'vacation' => 'vacation'
);
$CONF['admin_email'] = 'postmaster@gcu.info';
$CONF['smtp_server'] = 'localhost';
$CONF['smtp_port'] = '25';
$CONF['encrypt'] = 'md5';
$CONF['generate_password'] = 'YES';
$CONF['show_password'] = 'NO';
$CONF['page_size'] = '10';
$CONF['default_aliases'] = array (
        'abuse' => 'abuse@gcu.info',
        'hostmaster' => 'hostmaster@gcu.info',
        'postmaster' => 'postmaster@gcu.info',
        'webmaster' => 'webmaster@gcu.info'
);
$CONF['domain_path'] = 'YES';
$CONF['domain_in_mailbox'] = 'NO';
$CONF['aliases'] = '10'
$CONF['mailboxes'] = '10';
$CONF['maxquota'] = '10';
$CONF['quota'] = 'YES';
$CONF['quota_multiplier'] = '1024000';
$CONF['transport'] = 'NO';
$CONF['transport_options'] = array (
   'virtual',  // for virtual accounts
   'local',    // for system accounts
   'relay'     // for backup mx
);
$CONF['transport_default'] = 'virtual';
$CONF['vacation'] = 'NO';
$CONF['vacation_domain'] = 'autoreply.gcu.info';
$CONF['vacation_control'] ='YES';
$CONF['vacation_control_admin'] = 'YES';
$CONF['alias_control'] = 'NO';
$CONF['alias_control_admin'] = 'YES';
$CONF['special_alias_control'] = 'YES';
$CONF['alias_goto_limit'] = '0';
$CONF['backup'] = 'YES';
$CONF['sendmail'] = 'YES';
$CONF['logging'] = 'YES';
$CONF['show_header_text'] = 'NO';
$CONF['header_text'] = ':: Postfix Admin ::';
$CONF['show_footer_text'] = 'YES';
$CONF['footer_text'] = 'Return to mail.gcu.info';
$CONF['footer_link'] = 'http://mail.gcu.info/';
$CONF['welcome_text'] = 'Hi, welcome to your new account.';
$CONF['emailcheck_resolve_domain']='YES';
$CONF['create_mailbox_subdirs']=array('Spam','Trash');
$CONF['create_mailbox_subdirs_host']='localhost';
?>

Plus qu'à… Lancer ce qui n'a pas encore été lancé :

mail# /usr/local/etc/rc.d/dovecot start
Starting dovecot.
mail# /usr/local/etc/rc.d/postfix start
Starting postfix.

Lancez votre navigateur préféré, allez sur https://mail.gcu.info/. Si vous avez suivi ce tutoriel de A à Z, une page devrait s'afficher vous confirmant que tout est OK et vous invitant à supprimer le fichier setup.php. Vous serez ensuite redirigé vers https://mail.gcu.info/admin/ où vous pourrez enfin savourer les fruits d'un dur labeur.

En cas de problème, scruter attentivement les fichiers /var/log/messages, /var/log/maillog et /var/log/dovecot-deliver.log qui devraient vous donner de précieux indices.

Fortement inspiré de http://freebsd.ntut.idv.tw/document/postfix_postfixadmin_mysql.html (ouais, j'ai pris taïwainais troisième langue, trop facile) et surtout de http://wiki.dovecot.org/DovecotLDAPostfixAdminMySQL.

freebsd/mail_virtuel.txt · Last modified: 2010/01/12 13:29 (external edit)