当前位置:Gxlcms > 数据库问题 > Using Postfix, Dovecot and MySql as Mailserver under CentOS 7

Using Postfix, Dovecot and MySql as Mailserver under CentOS 7

时间:2021-07-01 10:21:17 帮助过:5人阅读


I planed to build a mail server working with gitlab, for internal development using. The mail server is also the one running gitlab. The idea makes things simple.


1. DNS configuration

For local network, you have to use "/etc/hosts" as the alternative.

Let‘s say the host name of server is: devsrv,

Server File: /etc/hosts

# hosts for devsrv
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

# DNS configuration
192.168.1.10 mail.devsrv.lan www.devsrv.lan devsrv.lan devsrv

Client File: /etc/hosts

127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6

192.168.1.10 devsrv devsrv.lan www.devsrv.lan mail.devsrv.lan imap.devsrv.lan smtp.devsrv.lan git.devsrv.lan

2. Installation

Under CentOS, the commands cloud be:

sudo yum install -y postfix dovecot dovecot-core mariadb

3. Initializing of database

Following the instruction of the first reference, initialize the database. I also wrote some procedures to help managing account:

Create Database

create database if not exists mailserver;

create user mailserver@'localhost' identified by 'mailserver123';

grant all on mailserver.* to mailserver@localhost' identified by 'mailserver123';
Create tables and procedures

use mailserver;

delimiter //

drop procedure if exists proc_log;

create procedure proc_log(msg varchar(1024))
begin
	select ifnull(msg, '') as DEBUG;
end //

-- Procedure: proc_show_error
-- Show error message with error code
drop procedure if exists proc_log_error;

create procedure proc_log_error(err_code int, msg varchar(255))
begin
  call proc_log(concat('ERROR(', err_code, '): ', msg));
end //

-- Procedure proc_add_mail_account
-- Create a new mail account
drop procedure if exists proc_add_mail_account;

create procedure proc_add_mail_account
(uname varchar(64),
pwd varchar(64))
begin
  declare def_domain varchar(100) default 'devsrv.lan';
  declare err int default 0;
  declare num int;

  declare uname_len int default 0;
  declare at_pos int default null;
  
  declare domain varchar(100);
  declare domain_id int;
  
  declare pwd_len int;
  
  -- initialize
  set uname = lcase(ltrim(rtrim(uname)));
  set uname_len = length(uname);
  set at_pos = ifnull(instr(uname, '@'), 0);

  set pwd = ltrim(rtrim(pwd));
  set pwd_len = length(pwd);

    -- validate username
  if uname is null or uname_len = 0 then
    set err = 1;
    call proc_log_error(err, 'Empty username');
  end if;

  -- check '@'
  if err = 0 and at_pos > 0 then
      -- if username has only '@' character
      set domain = right(uname, uname_len - at_pos);
      
      if uname_len = 1 or length(domain) = 0 then
        set err = 2;
        call proc_log_error(err, 'Invalid username');
      end if
    end if;
  end if;

  -- validate domain
  if err = 0 then
        if domain is null or length(domain) = 0 then
            set domain = def_domain;
      
      -- use default domain if not specified.
            set uname = concat(uname, '@', domain);
        end if;
  end if;

  -- validate domain existence
  if err = 0 then
    select id into domain_id from virtual_domains where name = domain;

    if domain_id is null then
      set err = 4;
      call proc_log_error(err, 'Invalid domain');
    end if;
  end if;

  -- validate user existence
  if err = 0 then
    select count(*) into num from virtual_users where email=uname;

    if num > 0 then
      set err = 5;
      call proc_log_error(err, 'User exists');
    end if;
  end if;

  -- validate password
  if err = 0 then
    if pwd_len = 0 then
      set err = 6;
      call proc_log_error(err, 'Empty password');
    else
      -- validate security rules
      -- validate length
      if pwd_len < 6 then
        set err = 7;
        call proc_log_error(err, 'Password must be 6 characters long');
      end if;
      -- NOTE: following rules are ignored now.
      -- validate uppercase letters
      -- validate digit
    end if;
  end if;
  
  if err = 0 then
      -- create account
      set pwd = encrypt(pwd, concat('X7X', substring(sha(rand()), -16)));

      insert virtual_users (domain_id, password, email)
          values(domain_id, pwd, uname);

        if last_insert_id() is null then
            set err = 8;
            call proc_log_error(err, 'Failed to create account');
      else
            select 'Account has been created successfully!';
      end if;
  end if;
end //
-- end of procedure


drop procedure if exists proc_del_mail_account;

create procedure proc_del_mail_account(uname varchar(64))
begin
    set uname = lcase(ltrim(rtrim(uname)));
  
    delete from virtual_users where email=uname;
  
  if row_count() = 0 then
        call proc_log('User does not exist');
  else
        call proc_log('User account has been removed successfully!');
    end if;

end //

drop procedure if exists proc_add_mail_alias;

create procedure proc_add_mail_alias(src varchar(100), dest varchar(100))
begin
    declare err int default 0;
    declare num int;
  
  declare domain_id int;
  
  declare dest_len int;
    declare dest_at_pos int;
  declare dest_name varchar(100);
  declare dest_domain varchar(100);
  
  declare src_len int;
  declare src_at_pos int;
  declare src_name varchar(100);
  declare src_domain varchar(100);
  
  set dest = lcase(ltrim(rtrim(dest)));
  set dest_len = length(dest);
  set dest_at_pos = ifnull(instr(dest, '@'), 0);
  
  set src = lcase(ltrim(rtrim(src)));
  set src_len = length(src);
  set src_at_pos = ifnull(instr(src, '@'), 0);
  
  -- validate dest length
  if dest_len = 0 then
        set err = 1;
    call proc_log_error(err, 'Empty dest');
    else
        -- check '@'
        if dest_at_pos = 0 then
            set err = 2;
      call proc_log_error(err, 'Invalid dest');
        else
            set dest_name = left(dest, dest_at_pos - 1);
      set dest_domain = right(dest, dest_len - dest_at_pos);
      
      -- re-check dest
      if length(dest_name) = 0 or length(dest_domain) = 0 then
                set err = 3;
        call proc_log_error(err, 'Invalid dest');
            else
                -- check existence
                select count(*) into num from virtual_users where email=dest;
        
        if num = 0 then
                    set err = 4;
          call proc_log_error(err, 'dest does not exist');
        end if;
      end if;
    end if;
  end if;
  
  -- validate 
  if err = 0 then
        if src_len = 0 then
            set err = 5;
      call proc_log_error(err, 'Empty alias');
        else
            if src_at_pos = 0 then
                set err = 6;
        call proc_log_error(err, 'Invalid alias');
      else
                set src_name=left(src, src_at_pos - 1);
        set src_domain=right(src, src_len - src_at_pos);
        
        if length(src_name) = 0 or length(src_domain) = 0 then
                    set err = 7;
          call proc_log_error(err, 'Invalid alias');
        end if;
      end if;
    end if;
  end if;
  
  call proc_log(concat('Validate domains', err));
  
  -- validate both domains
  if err = 0 then
        if dest_domain != src_domain then
            set err = 8;
      call proc_log_error(err, 'Domain does not match');
        else
            call proc_log('Check domain id');
      
            select id into domain_id from virtual_domains where name=dest_domain;
            
      call proc_log(concat('Get domain id:', ifnull(domain_id, 'null')));
      
      if domain_id is null then
                set err = 9;
        call proc_log_error(err, 'Domain does not exist');
      else
                select count(*) into num from virtual_aliases where source=src and destination=dest;
        
        call proc_log(concat('num of match:', num));
        
        if num > 0 then
                    set err = 10;
          call proc_log_error(err, 'Alias already exists');
                else
                    call proc_log('Insert into...');
          
                    insert into virtual_aliases(domain_id, source, destination)
                        value(domain_id, src, dest);
                    
          call proc_log('Inserted.');
          
                    if last_insert_id() is null then
                        set err = 11;
                        call proc_log_error(err, 'Failed');
                    else
                        call proc_log('Alias has been added successfully');
          end if;
        end if;
      end if;
        end if;
  end if;
end //

drop procedure if exists proc_del_mail_alias;


create procedure proc_del_mail_alias(src varchar(100), dest varchar(100))
begin
    declare err int default 0;
  declare num int;
  
  declare src_len int;
  declare src_at_pos int;
  declare src_domain varchar(100);
  declare src_domain_len int;
  
  declare dest_len int;
  declare dest_at_pos int;
  declare dest_domain varchar(100);
  declare dest_domain_len int;
  
  declare alias_id int default null;
  
  set src = lcase(ltrim(rtrim(src)));
  set src_len = length(src);
  set src_at_pos = ifnull(instr(src, '@'), 0);
  set src_domain = right(src, src_len - src_at_pos);
  set src_domain_len = length(src_domain);
  
  set dest = lcase(ltrim(rtrim(dest)));
  set dest_len = length(dest);
  set dest_at_pos = ifnull(instr(dest, '@'), 0);
  set dest_domain = right(dest, dest_len -dest_at_pos);
  set dest_domain_len = length(dest_domain);
  
  if src_len = 0 or src_at_pos = 0 or src_domain_len = 0 then
        set err = 1;
    call proc_log_error(err, 'Invalid alias');
  end if;
    
  if err = 0 and (dest_len = 0 or dest_at_pos = 0 or dest_domain_len = 0) then
        set err = 2;
        call proc_log_error(err, 'Invalid account');
  end if;
  
  if err = 0 then
        if src_domain != dest_domain then
            set err = 3;
      call proc_log_error(err, 'Different domain');
        end if;
  end if;
  
  if err = 0 then
        select id into alias_id from virtual_aliases where source = src and destination = dest;
    
    if alias_id is null then
            set err = 4;
      call proc_log_error(err, 'Not found');
        else
            delete from virtual_aliases where id = alias_id;
      
      if row_count() = 0 then
                set err = 5;
        call proc_log_error(err, 'Failed to remove record');
            else
                select 'Alias has been removed successfully';
            end if;
      
    end if;
  end if;
end //

NOTE: Change the default domain to your favorite one before run the script.

Copy and run the sql code in query window of MySql Workbench.

Or save the code to a file and run mysql command:

[igame@devsrv:~]$mysql -u root -u < untitled.sql

Account maintenance

Under mysql command prompt, run statements to maintenance mail account:

$mysql -u mailserver -p

Create user

call proc_add_mail_account('igame', '123456');

Create alias

call proc_add_mail_alias('postmaster@devsrv.lan', 'igame@devsrv.lan');

4. Postfix configuration

Following the reference above to configure postfix, but some small differences :

File: /etc/postfix/main.cf

myhostname = mail.devsrv.lan
mydomain = lan

mydestination = $myhostname, localhost.$mydomain, localhost

# restrict in intranet, for internet, should be 0.0.0.0/0
mynetworks = 192.168.1.0/24, 127.0.0.0/8
NOTE: CentOS stores dovecot certs in different path, if you use the default dovecot certs, keep that in mind:

smtpd_tls_cert_file=/etc/pki/dovecot/certs/dovecot.pem
smtpd_tls_key_file=/etc/pki/dovecot/private/dovecot.pem

NOTE: If you have problems when sending mail, try make unset mydestination

mydestination =

The reference above doesn‘t talk about "/etc/postfix/master.cf" which is crucially important, here is how:

File: /etc/postfix/master.cf

# Postfix master process configuration file.  For details on the format
# of the file, see the master(5) manual page (command: "man 5 master").
#
# Do not forget to execute "postfix reload" after editing this file.
#
# ==========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
smtp      inet  n       -       n       -       -       smtpd
#smtp      inet  n       -       n       -       1       postscreen
#smtpd     pass  -       -       n       -       -       smtpd
#dnsblog   unix  -       -       n       -       0       dnsblog
#tlsproxy  unix  -       -       n       -       0       tlsproxy
submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
#  -o smtpd_client_restrictions=$mua_client_restrictions
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
smtps     inet  n       -       n       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_reject_unlisted_recipient=no
#  -o smtpd_client_restrictions=$mua_client_restrictions
#  -o smtpd_helo_restrictions=$mua_helo_restrictions
#  -o smtpd_sender_restrictions=$mua_sender_restrictions
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING
#628       inet  n       -       n       -       -       qmqpd

# NOTE: do not forget dovecot
dovecot   unix  -       n       n       -       -       pipe
    flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f ${sender} -d ${recipient}

5. Dovecot configuration

Again, the most occurring error is the path of ssl certs, for default dovecot certs, it should be:

File: /etc/dovecot/dovecot.conf

ssl_cert = </etc/pki/dovecot/certs/dovecot.pem
ssl_key = </etc/pki/dovecot/private/dovecot.pem
Under CentOS 7, there is no *.protocol files for dovecot.conf, so this following line coming from the first reference has no meaning:

 !include_try /usr/share/dovecot/protocols.d/*.protocol 

6. Create Account

In query dialog of "MySql Workbench Community" or mysql command line prompt, run the following sql statement:

use mailserver;

call proc_add_mail_account('test', '123456');

7. Thunderbird Configuration

The Thunderbird is not smart enough if you choose to let it detect the configuration from mail server. The faster way is manually configuring:

Add new account:

技术分享

Use manual config

技术分享

NOTE: Remove the "." before server hostname which is a mistake. Configure the port, SSL and authentication like the following picture:

技术分享

Re-test

Now the Thunderbird shows off how clever it is.

技术分享

Add Security Exception

Probably Thunderbird warns about security exception, choose "Confirm Security Exception".

技术分享

Overview of Account Settings

When all done, we can check the settings of account.

技术分享

Server Settings:

技术分享

Outgoing Server(SMTP) Settings:

技术分享

Edit the smtp setting:

技术分享

Congratulations! All done, now we can send and receive mail. Enjoy!

Good luck!

Using Postfix, Dovecot and MySql as Mailserver under CentOS 7

标签:

人气教程排行