Nastavení mail serveru

Z ZděchovNET
Skočit na navigaci Skočit na vyhledávání

Návod popisuje instalace poštovního serveru s virtuálními účty v MySQL databázi.

  • Budou použity následují komponenty:
    • postfix - SMTP server
    • dovecot - IMAP server
    • roundcubemail - web mail klient
    • postfixadmin - správa schránek


Instalační skript

Většina návodů je provedena jako dlouhý popis postupu s mnoha útržkovými částmi příkazů, které je potřeba provést. To je zdlouhavý proces kopírovat jednotlivé části povelů a po částech je provádět. Místo toho bychom rádi měli jednoduchou instalaci, kterou prostě spustíme, chvíli počkáme a máme hotovo. Zde je předložen skript, který se toto dle tohoto návodu snaží docílit.

<?php

$MysqlRootPassword = 'MysqlRootPassword';
$MysqlUserPassword = 'MysqlUserPassword';
$PostfixAdminName = 'PostfixAdminName';
$PostfixSetupPassword = 'PostfixSetupPassword';
$DomainName = 'domain.com';
$ServerDomain = 'mail.'.$DomainName;

function Execute($Command)
{
  echo('Executing: '.$Command."\n");
  passthru($Command);
}

function InstallPackages()
{
  // LAMP web server
  Execute('apt-get update');
  Execute('apt-get upgrade --assume-yes');
  Execute('apt-get install --assume-yes lamp-server^');

  Execute('apt-get install --assume-yes \
  php-apc \
  php7.0-mcrypt \
  php7.0-memcache \
  php7.0-curl \
  php7.0-gd \
  php-xml-parser');

  Execute('ln -sf ../../mods-available/mcrypt.ini /etc/php5/apache2/conf.d/20-mcrypt.ini');

  // Configure apache
  Execute('a2enmod rewrite ssl');
  Execute('a2ensite default-ssl');

  Execute('service apache2 restart');

  // Memcached
  Execute('apt-get install --assume-yes memcached');
 
  // Mail packages
  Execute('apt-get install --assume-yes mail-server^');

  Execute('apt-get install --assume-yes \
  postfix-mysql \
  dovecot-mysql \
  dovecot-imapd \
  dovecot-pop3d \
  postgrey \
  amavis \
  clamav \
  clamav-daemon \
  spamassassin \
  php7.0-imap');

  Execute('ln -sf ../../mods-available/imap.ini /etc/php5/apache2/conf.d/20-imap.ini');

  Execute('service apache2 restart');

  Execute('apt-get install --assume-yes \
  pyzor \
  razor \
  arj \
  cabextract \
  lzop \
  nomarch \
  p7zip-full \
  ripole \
  rpm2cpio \
  tnef \
  unzip \
  unrar-free \
  zip \
  zoo');
}

function PrepareDatabase()
{
  global $MysqlUserPassword, $MysqlRootPassword;
  // Mail database

  Execute('mysql -uroot -p'.$MysqlRootPassword.' -Bse \
"drop database mail; create database mail;
grant all on mail.* to \'mail\'@\'localhost\' identified by \''.$MysqlUserPassword.'\';"');
}

function UpdatePHPConfigFile($FileName, $BaseVariable, $Parameters)
{
  if(!file_exists($FileName.'_backup'))
    file_put_contents($FileName.'_backup', file_get_contents($FileName));    
  $Content = explode("\n", file_get_contents($FileName.'_backup'));
  foreach($Parameters as $Parameter)
  {
    
    if(is_array($Parameter['name'])) {      
      foreach($Content as $Num => $Line)
      {
        $Needle = '$'.$BaseVariable.'['."'".$Parameter['name'][0]."'] = array (";      
        if(substr($Line, 0, strlen($Needle)) == $Needle)
        {          
          echo('Found group: '.$Parameter['name'][0]."\n");
          $Needle = "    '".$Parameter['name'][1]."' => ";      
          $I = 1;
          while((($Num + $I) < count($Content)) and ($Content[$Num + $I] != ');')) 
          {
            if(substr($Content[$Num + $I], 0, strlen($Needle)) == $Needle) {   
              echo('Found group: '.$Parameter['name'][1]."\n");
              $Content[$Num + $I] = $Needle.$Parameter['value'].',';              
            }
            $I++;
          }
        }
      }
    } else {
      foreach($Content as $Num => $Line)
      {
        $Needle = '$'.$BaseVariable.'['."'".$Parameter['name']."'] = ";      
        if(substr($Line, 0, strlen($Needle)) == $Needle)
        {
          echo('Found: '.$Parameter['name']."\n");
          $Content[$Num] = $Needle.$Parameter['value'].';';
        }
      }
    }
  }
  file_put_contents($FileName, implode("\n", $Content));
  
}

function generate_setup_password_salt() 
{
  $salt = time().'*'.mt_rand(0,60000);
  $salt = md5($salt);
  return $salt;
}

function encrypt_setup_password($password, $salt) 
{
  return $salt . ':' . sha1($salt . ':' . $password);
}

function InstallPostfixAdmin()
{
  global $ServerDomain, $PostfixAdminName, $MysqlUserPassword, $PostfixSetupPassword, $DomainName;
  
  Execute('wget "http://downloads.sourceforge.net/project/postfixadmin/postfixadmin/'.
    $PostfixAdminName.'/'.$PostfixAdminName.'.tar.gz?r=&ts=1417890779&use_mirror=netcologne" -O '.
    $PostfixAdminName.'.tar.gz');
  Execute('tar -xf '.$PostfixAdminName.'.tar.gz');
  Execute('rm -f '.$PostfixAdminName.'.tar.gz');
  Execute('mv '.$PostfixAdminName.' /var/www/html/postfixadmin');
  Execute('chown -R www-data:www-data /var/www/html/postfixadmin');

  UpdatePHPConfigFile('/var/www/html/postfixadmin/config.inc.php', 'CONF', array(
    array('name' => 'setup_password', 'value' => "'".encrypt_setup_password($PostfixSetupPassword, generate_setup_password_salt())."'"),
    array('name' => 'configured', 'value' => 'true'),
    array('name' => 'postfix_admin_url', 'value' => "'https://".$ServerDomain."/postfixadmin'"),
    array('name' => 'database_type', 'value' => "'mysql'"),
    array('name' => 'database_host', 'value' => "'localhost'"),
    array('name' => 'database_user', 'value' => "'mail'"),
    array('name' => 'database_password', 'value' => "'".$MysqlUserPassword."'"),
    array('name' => 'database_name', 'value' => "'mail'"),
    array('name' => 'admin_email', 'value' => "'admin@".$DomainName."'"),
    array('name' => 'user_footer_link', 'value' =>  "'https://".$ServerDomain."/main.php'"),
    array('name' => 'show_footer_text', 'value' => "'YES'"),
    array('name' => 'footer_text', 'value' => "'Return to ".$ServerDomain."'"),
    array('name' => 'footer_link', 'value' => "'https://".$ServerDomain."'"),
    array('name' => array('default_aliases', 'abuse'), 'value'  => "'abuse@".$DomainName."'"),
    array('name' => array('default_aliases', 'hostmaster'), 'value' => "'hostmaster@".$DomainName."'"),
    array('name' => array('default_aliases', 'postmaster'), 'value' => "'postmaster@".$DomainName."'"),
    array('name' => array('default_aliases', 'webmaster'), 'value' => "'webmaster@".$DomainName."'"),
  ));

}

function VirtualMailUser()
{
  Execute('useradd -r -u 150 -g mail -d /var/vmail -s /sbin/nologin -c "Virtual maildir handler" vmail');
  Execute('mkdir /var/vmail');
  Execute('chmod 770 /var/vmail');
  Execute('chown vmail:mail /var/vmail');
}

function UpdateDovecotConfigFile($FileName, $Parameters)
{
  if(!file_exists($FileName.'_backup'))
    file_put_contents($FileName.'_backup', file_get_contents($FileName));    
  $Content = explode("\n", file_get_contents($FileName.'_backup'));
  foreach($Parameters as $Parameter)
  { 
    if(!is_array($Parameter['name'])) 
      $Parameter['name'] = array($Parameter['name']);
    // First find subgroup
    $Num = 0;
    $NestingLevel = 0;
    while(count($Parameter['name']) > 1)  
    {
      $Found = false;
      $Needle = '#'.array_values($Parameter['name'])[0].' {';
      $I = 0;
      $StartNestingLevel = $NestingLevel;     
      while((($Num + $I) < count($Content)) and ($NestingLevel >= $StartNestingLevel)) 
      {
        if(substr(trim($Content[$Num + $I]), -1, 1) == '{') $NestingLevel++;
        if(substr(trim($Content[$Num + $I]), -1, 1) == '}') $NestingLevel--;
        if((trim(substr(trim($Content[$Num + $I]), 0, strlen($Needle))) == $Needle)
        or (trim(substr(trim($Content[$Num + $I]), 0, strlen(substr($Needle, 1)))) == substr($Needle, 1)))
        {
          $Content[$Num + $I] = array_values($Parameter['name'])[0].' {';
          $Num = $Num + $I + 1;
          $Found = true;
          echo('Found group: '.array_values($Parameter['name'])[0]."\n");
          array_shift($Parameter['name']);
          break;
        }
        $I++;
      }
      if(!$Found) break;
    } 
    // Find and replace value
    if(count($Parameter['name']) == 1)
    {
      if(!array_key_exists('value', $Parameter)) 
      {
        $Needle = array_values($Parameter['name'])[0];      
        $Parameter['value'] = '';
      } else 
        $Needle = array_values($Parameter['name'])[0]." =";      
      $I = 0;
      $StartNestingLevel = $NestingLevel;
      $Found = false;
      while((($Num + $I) < count($Content)) and ($NestingLevel >= $StartNestingLevel))
      {
        if(substr(trim($Content[$Num + $I]), -1, 1) == '{') $NestingLevel++;
        if(substr(trim($Content[$Num + $I]), -1, 1) == '}')
        {
          if($NestingLevel == $StartNestingLevel) $Content[$Num + $I] = '}';
          $NestingLevel--;
        }
        if((trim(substr(trim($Content[$Num + $I]), 0, strlen($Needle))) == $Needle)
        or ((substr(trim($Content[$Num + $I]), 0, 1) == '#') and (substr(trim(substr(trim($Content[$Num + $I]), 1)), 0, strlen($Needle)) == $Needle)))
        {
          echo('Found: '.array_values($Parameter['name'])[0]."\n");
          $Content[$Num + $I] = $Needle.' '.$Parameter['value'];
          $Found = true;
          break;
        }
        $I++;
      }
      // If not found then add new value
      if($Found == false)
      {
        echo('Added new: '.array_values($Parameter['name'])[0]."\n");
        array_splice($Content, $Num + $I - 1, 0, $Needle.' '.$Parameter['value']);
      }
    }
  }
  file_put_contents($FileName, implode("\n", $Content));
}

function ConfigureDovecot()
{
  global $MysqlUserPassword, $DomainName;
  
  UpdateDovecotConfigFile('/etc/dovecot/dovecot-sql.conf.ext', array(
    array('name' => 'driver', 'value' => 'mysql'),
    array('name' => 'connect', 'value' => 'host=localhost dbname=mail user=mail password='.$MysqlUserPassword),
    array('name' => 'default_pass_scheme', 'value' => 'MD5-CRYPT'),
    array('name' => 'password_query', 'value' => "SELECT username as user, password, '/var/vmail/%d/%n' as userdb_home, \\
  'maildir:/var/vmail/%d/%n' as userdb_mail, 150 as userdb_uid, 8 as userdb_gid \\
  FROM mailbox WHERE username = '%u' AND active = '1'"),
    array('name' => 'user_query', 'value' => "SELECT '/var/vmail/%d/%n' as home, 'maildir:/var/vmail/%d/%n' as mail, \\
  150 AS uid, 8 AS gid, concat('dirsize:storage=', quota) AS quota \\
  FROM mailbox WHERE username = '%u' AND active = '1'"),
  ));
  UpdateDovecotConfigFile('/etc/dovecot/conf.d/10-auth.conf', array(
    array('name' => 'disable_plaintext_auth', 'value' => 'yes'),
    array('name' => 'auth_mechanisms', 'value' => 'plain login'),
    array('name' => '!include auth-sql.conf.ext'),
  ));
  UpdateDovecotConfigFile('/etc/dovecot/conf.d/10-mail.conf', array(
    array('name' => 'mail_location', 'value' => 'maildir:/var/vmail/%d/%n'),
    array('name' => 'mail_uid', 'value' => 'vmail'),
    array('name' => 'mail_gid', 'value' => 'mail'),
    array('name' => 'first_valid_uid', 'value' => '150'),
    array('name' => 'last_valid_uid', 'value' => '150'),
  ));
  UpdateDovecotConfigFile('/etc/dovecot/conf.d/10-ssl.conf', array(
    array('name' => 'ssl_cert', 'value' => '</etc/ssl/certs/'.$DomainName.'.crt'),
    array('name' => 'ssl_key', 'value' => '</etc/ssl/private/'.$DomainName.'.key'),
    array('name' => 'ssl_ca', 'value' => '</etc/ssl/certs/ca-bundle.crt'),
    array('name' => 'ssl_cipher_list', 'value' => 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS'),
  ));
  UpdateDovecotConfigFile('/etc/dovecot/conf.d/10-master.conf', array(
    array('name' => array('service auth', 'unix_listener auth-userdb', 'mode'), 'value' => '0666'),
    array('name' => array('service auth', 'unix_listener auth-userdb', 'user'), 'value' => 'vmail'),
    array('name' => array('service auth', 'unix_listener auth-userdb', 'group'), 'value' => 'mail'),
    array('name' => array('service auth', 'unix_listener /var/spool/postfix/private/auth', 'mode'), 'value' => '0666'),
    array('name' => array('service auth', 'unix_listener /var/spool/postfix/private/auth', 'user'), 'value' => 'postfix'),
    array('name' => array('service auth', 'unix_listener /var/spool/postfix/private/auth', 'group'), 'value' => 'postfix'),
  ));
  UpdateDovecotConfigFile('/etc/dovecot/conf.d/15-lda.conf', array(
    array('name' => 'postmaster_address', 'value' => 'postmaster@'.$DomainName),
  ));
  
  Execute('chown -R vmail:dovecot /etc/dovecot');
  Execute('chmod -R o-rwx /etc/dovecot');
}

function UpdateAmavisConfigFile($FileName, $Parameters)
{
  if(!file_exists($FileName.'_backup'))
    file_put_contents($FileName.'_backup', file_get_contents($FileName));    
  $Content = explode("\n", file_get_contents($FileName.'_backup'));
  if(count($Content) > 0)
  {
    // Remove ending part
    $I = count($Content) - 1;
    while(($I >= 0))
    {  
      if($Content[$I] != '#------------ Do not modify anything below this line -------------')
        array_pop($Content);
        else {
          array_pop($Content);
          break;
        }
      $I--;
    }
  }
  
  foreach($Parameters as $Parameter)
  { 
    $Parameter['name'] = array($Parameter['name']);
    $Num = 0;
    if(!array_key_exists('value', $Parameter)) 
    {
      $Needle = array_values($Parameter['name'])[0];      
      $Parameter['value'] = '';
    } else 
      $Needle = array_values($Parameter['name'])[0]." =";      
    $I = 0;
    $Found = false;
    while((($Num + $I) < count($Content)))
    {
      if((trim(substr(trim($Content[$Num + $I]), 0, strlen($Needle))) == $Needle)
      or ((substr(trim($Content[$Num + $I]), 0, 1) == '#') and (substr(trim(substr(trim($Content[$Num + $I]), 1)), 0, strlen($Needle)) == $Needle)))
      {
        echo('Found: '.array_values($Parameter['name'])[0]."\n");
        $Content[$Num + $I] = $Needle.' '.$Parameter['value'];
        $Found = true;
        break;
      }
      $I++;
    }
    // If not found then add new value
    if($Found == false)
    {
      echo('Added new: '.array_values($Parameter['name'])[0]."\n");
      array_splice($Content, $Num + $I - 1, 0, $Needle.' '.$Parameter['value']);
    }  
  }
  $Content[] = '#------------ Do not modify anything below this line -------------';
  $Content[] = '1;  # ensure a defined return';
  file_put_contents($FileName, implode("\n", $Content));
}

function UpdateDefaultConfigFile($FileName, $Parameters)
{
  if(!file_exists($FileName.'_backup'))
    file_put_contents($FileName.'_backup', file_get_contents($FileName));    
  $Content = explode("\n", file_get_contents($FileName.'_backup'));
  foreach($Parameters as $Parameter)
  { 
    if(!is_array($Parameter['name'])) 
      $Parameter['name'] = array($Parameter['name']);
    $Num = 0;
    // Find and replace value
    if(count($Parameter['name']) == 1)
    {
      if(!array_key_exists('value', $Parameter)) 
      {
        $Needle = array_values($Parameter['name'])[0];      
        $Parameter['value'] = '';
      } else 
        $Needle = array_values($Parameter['name'])[0]."=";
      $I = 0;
      $Found = false;
      while((($Num + $I) < count($Content)))
      {
        if((trim(substr(trim($Content[$Num + $I]), 0, strlen($Needle))) == $Needle)
        or ((substr(trim($Content[$Num + $I]), 0, 1) == '#') and (substr(trim(substr(trim($Content[$Num + $I]), 1)), 0, strlen($Needle)) == $Needle)))
        {
          echo('Found: '.array_values($Parameter['name'])[0]."\n");
          $Content[$Num + $I] = $Needle.$Parameter['value'];
          $Found = true;
          break;
        }
        $I++;
      }
      // If not found then add new value
      if($Found == false)
      {
        echo('Added new: '.array_values($Parameter['name'])[0]."\n");
        array_splice($Content, $Num + $I - 1, 0, $Needle.' '.$Parameter['value']);
      }
    }
  }
  file_put_contents($FileName, implode("\n", $Content));  
}

function SpamVirus()
{
  Execute('adduser clamav amavis');
  Execute('adduser amavis clamav');
  UpdateAmavisConfigFile('/etc/amavis/conf.d/15-content_filter_mode', array(
    array('name' => '@bypass_virus_checks_maps', 'value' => '(\%bypass_virus_checks, \@bypass_virus_checks_acl, \$bypass_virus_checks_re);'),
    array('name' => '@bypass_spam_checks_maps', 'value' => '(\%bypass_spam_checks, \@bypass_spam_checks_acl, \$bypass_spam_checks_re);'),
  ));
  UpdateDefaultConfigFile('/etc/default/spamassassin', array(
    array('name' => 'ENABLED', 'value' => '1'),
    array('name' => 'CRON', 'value' => '1'),
  ));
  UpdateAmavisConfigFile('/etc/amavis/conf.d/50-user', array(
    array('name' => '$max_servers', 'value' => '3;'),
    array('name' => '$sa_tag_level_deflt', 'value' => '-9999;'),
    array('name' => '@lookup_sql_dsn', 'value' => "(
    ['DBI:mysql:database=mail;host=127.0.0.1;port=3306',
     'mail',
     'mailpassword']);"),     
    array('name' => '$sql_select_policy', 'value' => "'SELECT domain from domain WHERE CONCAT(\"@\",domain) IN (%k)';"),
  ));
  Execute('freshclam');
  Execute('service clamav-daemon restart');
  Execute('service amavis restart');
  Execute('service spamassassin restart');  
}

function ConfigurePostfix()
{
  file_put_contents('/etc/postfix/mysql_virtual_alias_domainaliases_maps.cf', 
  "user = mail
password = mailpassword
hosts = 127.0.0.1
dbname = mail
query = SELECT goto FROM alias,alias_domain
  WHERE alias_domain.alias_domain = '%d'
  AND alias.address=concat('%u', '@', alias_domain.target_domain)
  AND alias.active = 1");
  file_put_contents('/etc/postfix/mysql_virtual_alias_maps.cf', 
  "user = mail
password = mailpassword
hosts = 127.0.0.1
dbname = mail
table = alias
select_field = goto
where_field = address
additional_conditions = and active = '1'");
  file_put_contents('/etc/postfix/mysql_virtual_domains_maps.cf', 
  "user = mail
password = mailpassword
hosts = 127.0.0.1
dbname = mail
table = domain
select_field = domain
where_field = domain
additional_conditions = and backupmx = '0' and active = '1'");
  file_put_contents('/etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf',
  "user = mail
password = mailpassword
hosts = 127.0.0.1
dbname = mail
query = SELECT maildir FROM mailbox, alias_domain
  WHERE alias_domain.alias_domain = '%d'
  AND mailbox.username=concat('%u', '@', alias_domain.target_domain )
  AND mailbox.active = 1");
  file_put_contents('/etc/postfix/mysql_virtual_mailbox_maps.cf',
  "user = mail
password = mailpassword
hosts = 127.0.0.1
dbname = mail
table = mailbox
select_field = CONCAT(domain, '/', local_part)
where_field = username
additional_conditions = and active = '1'");
  file_put_contents('/etc/postfix/header_checks',
  "/^Received:/                 IGNORE
/^User-Agent:/               IGNORE
/^X-Mailer:/                 IGNORE
/^X-Originating-IP:/         IGNORE
/^x-cr-[a-z]*:/              IGNORE
/^Thread-Index:/     
        IGNORE");
  if(!file_exists('/etc/postfix/main.cf_backup'))
    file_put_contents('/etc/postfix/main.cf_backup', file_get_contents('/etc/postfix/main.cf'));
  file_put_contents('/etc/postfix/main.cf',
  '# See /usr/share/postfix/main.cf.dist for a commented, more complete version
 
# The first text sent to a connecting process.
smtpd_banner = $myhostname ESMTP $mail_name
biff = no
# appending .domain is the MUA\'s job.
append_dot_mydomain = no
readme_directory = no
 
# SASL parameters
# ---------------------------------
 
# Use Dovecot to authenticate.
smtpd_sasl_type = dovecot
# Referring to /var/spool/postfix/private/auth
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
broken_sasl_auth_clients = yes
smtpd_sasl_security_options = noanonymous
smtpd_sasl_local_domain =
smtpd_sasl_authenticated_header = yes
 
# TLS parameters
# ---------------------------------
 
# The default snakeoil certificate. Comment if using a purchased
# SSL certificate.
smtpd_tls_cert_file=/etc/ssl/certs/ssl-cert-snakeoil.pem
smtpd_tls_key_file=/etc/ssl/private/ssl-cert-snakeoil.key
 
# Uncomment if using a purchased SSL certificate.
# smtpd_tls_cert_file=/etc/ssl/certs/example.com.crt
# smtpd_tls_key_file=/etc/ssl/private/example.com.key
 
# The snakeoil self-signed certificate has no need for a CA file. But
# if you are using your own SSL certificate, then you probably have
# a CA certificate bundle from your provider. The path to that goes
# here.
# smtpd_tls_CAfile=/etc/ssl/certs/ca-bundle.crt
 
# Ensure we\'re not using no-longer-secure protocols.
smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
 
smtp_tls_note_starttls_offer = yes
smtpd_tls_loglevel = 1
smtpd_tls_received_header = yes
smtpd_tls_session_cache_timeout = 3600s
tls_random_source = dev:/dev/urandom
#smtpd_tls_session_cache_database = btree:${data_directory}/smtpd_scache
#smtp_tls_session_cache_database = btree:${data_directory}/smtp_scache
 
# Note that forcing use of TLS is going to cause breakage - most mail servers
# don\'t offer it and so delivery will fail, both incoming and outgoing. This is
# unfortunate given what various governmental agencies are up to these days.
#
# Enable (but don\'t force) all incoming smtp connections to use TLS.
smtpd_tls_security_level = may
# Enable (but don\'t force) all outgoing smtp connections to use TLS.
smtp_tls_security_level = may
 
# See /usr/share/doc/postfix/TLS_README.gz in the postfix-doc package for
# information on enabling SSL in the smtp client.
 
# SMTPD parameters
# ---------------------------------
 
# Uncomment the next line to generate \"delayed mail\" warnings
#delay_warning_time = 4h
# will it be a permanent error or temporary
unknown_local_recipient_reject_code = 450
# how long to keep message on queue before return as failed.
# some have 3 days, I have 16 days as I am backup server for some people
# whom go on holiday with their server switched off.
maximal_queue_lifetime = 7d
# max and min time in seconds between retries if connection failed
minimal_backoff_time = 1000s
maximal_backoff_time = 8000s
# how long to wait when servers connect before receiving rest of data
smtp_helo_timeout = 60s
# how many address can be used in one message.
# effective stopper to mass spammers, accidental copy in whole address list
# but may restrict intentional mail shots.
smtpd_recipient_limit = 16
# how many error before back off.
smtpd_soft_error_limit = 3
# how many max errors before blocking it.
smtpd_hard_error_limit = 12
 
# This next set are important for determining who can send mail and relay mail
# to other servers. It is very important to get this right - accidentally producing
# an open relay that allows unauthenticated sending of mail is a Very Bad Thing.
#
# You are encouraged to read up on what exactly each of these options accomplish.
 
# Requirements for the HELO statement
smtpd_helo_restrictions = permit_mynetworks, warn_if_reject reject_non_fqdn_hostname, reject_invalid_hostname, permit
# Requirements for the sender details
smtpd_sender_restrictions = permit_sasl_authenticated, permit_mynetworks, warn_if_reject reject_non_fqdn_sender, reject_unknown_sender_domain, reject_unauth_pipelining, permit
# Requirements for the connecting server
smtpd_client_restrictions = reject_rbl_client sbl.spamhaus.org, reject_rbl_client blackholes.easynet.nl, reject_rbl_client dnsbl.njabl.org
# Requirement for the recipient address. Note that the entry for
# \"check_policy_service inet:127.0.0.1:10023\" enables Postgrey.
smtpd_recipient_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit
smtpd_data_restrictions = reject_unauth_pipelining
# This is a new option as of Postfix 2.10, and is required in addition to
# smtpd_recipient_restrictions for things to work properly in this setup.
smtpd_relay_restrictions = reject_unauth_pipelining, permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unknown_recipient_domain, reject_unauth_destination, check_policy_service inet:127.0.0.1:10023, permit
 
# require proper helo at connections
smtpd_helo_required = yes
# waste spammers time before rejecting them
smtpd_delay_reject = yes
disable_vrfy_command = yes
 
# General host and delivery info
# ----------------------------------
 
myhostname = mail.example.com
myorigin = /etc/hostname
# Some people see issues when setting mydestination explicitly to the server
# subdomain, while leaving it empty generally doesn\'t hurt. So it is left empty here.
# mydestination = mail.example.com, localhost
mydestination =
# If you have a separate web server that sends outgoing mail through this
# mailserver, you may want to add its IP address to the space-delimited list in
# mynetworks, e.g. as 10.10.10.10/32.
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mailbox_size_limit = 0
recipient_delimiter = +
inet_interfaces = all
mynetworks_style = host
 
# This specifies where the virtual mailbox folders will be located.
virtual_mailbox_base = /var/vmail
# This is for the mailbox location for each user. The domainaliases
# map allows us to make use of Postfix Admin\'s domain alias feature.
virtual_mailbox_maps = mysql:/etc/postfix/mysql_virtual_mailbox_maps.cf, mysql:/etc/postfix/mysql_virtual_mailbox_domainaliases_maps.cf
# and their user id
virtual_uid_maps = static:150
# and group id
virtual_gid_maps = static:8
# This is for aliases. The domainaliases map allows us to make
# use of Postfix Admin\'s domain alias feature.
virtual_alias_maps = mysql:/etc/postfix/mysql_virtual_alias_maps.cf, mysql:/etc/postfix/mysql_virtual_alias_domainaliases_maps.cf
# This is for domain lookups.
virtual_mailbox_domains = mysql:/etc/postfix/mysql_virtual_domains_maps.cf
 
# Integration with other packages
# ---------------------------------------
 
# Tell postfix to hand off mail to the definition for dovecot in master.cf
virtual_transport = dovecot
dovecot_destination_recipient_limit = 1
 
# Use amavis for virus and spam scanning
content_filter = amavis:[127.0.0.1]:10024
 
# Header manipulation
# --------------------------------------
 
# Getting rid of unwanted headers. See: https://posluns.com/guides/header-removal/
header_checks = regexp:/etc/postfix/header_checks
# getting rid of x-original-to
enable_original_recipient = no');

  if(!file_exists('/etc/postfix/master.cf_backup'))
    file_put_contents('/etc/postfix/master.cf_backup', file_get_contents('/etc/postfix/master.cf'));
  file_put_contents('/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 on port 25, unencrypted.
smtp      inet  n       -       -       -       -       smtpd
#smtp      inet  n       -       -       -       1       postscreen
#smtpd     pass  -       -       -       -       -       smtpd
#dnsblog   unix  -       -       -       -       0       dnsblog
#tlsproxy  unix  -       -       -       -       0       tlsproxy
 
# SMTP with TLS on port 587. Currently commented.
#submission inet n       -       -       -       -       smtpd
#  -o syslog_name=postfix/submission
#  -o smtpd_tls_security_level=encrypt
#  -o smtpd_sasl_auth_enable=yes
#  -o smtpd_enforce_tls=yes
#  -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
#  -o smtpd_sasl_tls_security_options=noanonymous
 
# SMTP over SSL on port 465.
smtps     inet  n       -       -       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_client_restrictions=permit_sasl_authenticated,reject_unauth_destination,reject
  -o smtpd_sasl_security_options=noanonymous,noplaintext
  -o smtpd_sasl_tls_security_options=noanonymous
 
#628       inet  n       -       -       -       -       qmqpd
pickup    fifo  n       -       -       60      1       pickup
  -o content_filter=
  -o receive_override_options=no_header_body_checks
cleanup   unix  n       -       -       -       0       cleanup
qmgr      fifo  n       -       n       300     1       qmgr
#qmgr     fifo  n       -       n       300     1       oqmgr
tlsmgr    unix  -       -       -       1000?   1       tlsmgr
rewrite   unix  -       -       -       -       -       trivial-rewrite
bounce    unix  -       -       -       -       0       bounce
defer     unix  -       -       -       -       0       bounce
trace     unix  -       -       -       -       0       bounce
verify    unix  -       -       -       -       1       verify
flush     unix  n       -       -       1000?   0       flush
proxymap  unix  -       -       n       -       -       proxymap
proxywrite unix -       -       n       -       1       proxymap
smtp      unix  -       -       -       -       -       smtp
relay     unix  -       -       -       -       -       smtp
#       -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
showq     unix  n       -       -       -       -       showq
error     unix  -       -       -       -       -       error
retry     unix  -       -       -       -       -       error
discard   unix  -       -       -       -       -       discard
local     unix  -       n       n       -       -       local
virtual   unix  -       n       n       -       -       virtual
lmtp      unix  -       -       -       -       -       lmtp
anvil     unix  -       -       -       -       1       anvil
scache    unix  -       -       -       -       1       scache
#
# ====================================================================
# Interfaces to non-Postfix software. Be sure to examine the manual
# pages of the non-Postfix software to find out what options it wants.
#
# Many of the following services use the Postfix pipe(8) delivery
# agent.  See the pipe(8) man page for information about ${recipient}
# and other message envelope options.
# ====================================================================
#
# maildrop. See the Postfix MAILDROP_README file for details.
# Also specify in main.cf: maildrop_destination_recipient_limit=1
#
maildrop  unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient}
#
# ====================================================================
#
# Recent Cyrus versions can use the existing \"lmtp\" master.cf entry.
#
# Specify in cyrus.conf:
#   lmtp    cmd=\"lmtpd -a\" listen=\"localhost:lmtp\" proto=tcp4
#
# Specify in main.cf one or more of the following:
#  mailbox_transport = lmtp:inet:localhost
#  virtual_transport = lmtp:inet:localhost
#
# ====================================================================
#
# Cyrus 2.1.5 (Amos Gouaux)
# Also specify in main.cf: cyrus_destination_recipient_limit=1
#
#cyrus     unix  -       n       n       -       -       pipe
#  user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
#
# ====================================================================
# Old example of delivery via Cyrus.
#
#old-cyrus unix  -       n       n       -       -       pipe
#  flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
#
# ====================================================================
#
# See the Postfix UUCP_README file for configuration details.
#
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
#
# Other external delivery methods.
#/var/www/html/.htaccess
ifmail    unix  -       n       n       -       -       pipe
  flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
bsmtp     unix  -       n       n       -       -       pipe
  flags=Fq. user=bsmtp argv=/usr/lib/bsmtp/bsmtp -t$nexthop -f$sender $recipient
scalemail-backend unix  -       n       n       -       2       pipe
  flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store ${nexthop} ${user} ${extension}
mailman   unix  -       n       n       -       -       pipe
  flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
  ${nexthop} ${user}
 
# The next two entries integrate with Amavis for anti-virus/spam checks.
amavis      unix    -       -       -       -       3       smtp
  -o smtp_data_done_timeout=1200
  -o smtp_send_xforward_command=yes
  -o disable_dns_lookups=yes
  -o max_use=20
127.0.0.1:10025 inet    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
 
# Integration with Dovecot - hand mail over to it for local delivery, and
# run the process under the vmail user and mail group.
dovecot      unix   -        n      n       -       -   pipe
  flags=DRhu user=vmail:mail argv=/usr/lib/dovecot/dovecot-lda -d $(recipient)');
}

function RestartAll()
{
  Execute('service postfix restart');
  Execute('service spamassassin restart');
  Execute('service clamav-daemon restart');
  Execute('service amavis restart');
  Execute('service dovecot restart');
}

function InstallRoundCube()
{
  Execute('apt-get install --assume-yes \
  roundcube \
  roundcube-plugins \
  roundcube-plugins-extra');
  UpdatePHPConfigFile('/etc/roundcube/main.inc.php', 'rcmail_config', array(
    array('name' => 'default_host', 'value' => "'localhost'"),
    array('name' => 'force_https', 'value' => 'true'),
    array('name' => 'imap_cache', 'value' => "'memcache'"),
    array('name' => 'session_storage', 'value' => "'memcache'"),
    array('name' => 'memcache_hosts', 'value' => "array('localhost:11211')"),
  ));
  Execute('ln -s /var/lib/roundcube /var/www/html/roundcube');
  if(file_exists('/var/www/html/index.html'))
    Execute('mv /var/www/html/index.html /var/www/html/index.bak.html');
  file_put_contents('/var/www/html/.htaccess', 'RewriteEngine On
 
# Redirect all HTTP traffic to HTTPS.
RewriteCond %{HTTPS} !=on
RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
 
# Send / to /roundcube.
RewriteRule ^/?$ /roundcube [L]');
}

function InstallMonit()
{
  Execute('apt-get install --assume-yes monit');
  file_put_contents('/etc/monit/conf.d/amavis', 
  'check process amavisd with pidfile /var/run/amavis/amavisd.pid
  every 5 cycles
  group mail
  start program = "/etc/init.d/amavis start"
  stop  program = "/etc/init.d/amavis stop"
  if failed port 10024 protocol smtp then restart
  if 5 restarts within 25 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/apache2', 
  'check process apache2 with pidfile /var/run/apache2/apache2.pid
  group www
  start program = "/etc/init.d/apache2 start"
  stop program = "/etc/init.d/apache2 stop"
  if failed host localhost port 80 protocol http
    with timeout 10 seconds
    then restart
  if 5 restarts within 5 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/dovecot',
  'check process dovecot with pidfile /var/run/dovecot/master.pid
  group mail
  start program = "/sbin/start dovecot"
  stop program = "/sbin/stop dovecot"
  group mail
  # We\'d like to use this line, but see:
  # http://serverfault.com/questions/610976/monit-failing-to-connect-to-dovecot-over-ssl-imap
  #if failed port 993 type tcpssl sslauto protocol imap for 5 cycles then restart
  if failed port 993 for 5 cycles then restart
  if 5 restarts within 25 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/mysql',
  'check process mysqld with pidfile /var/run/mysqld/mysqld.pid
  group database
  start program = "/etc/init.d/mysql start"
  stop program = "/etc/init.d/mysql stop"
  if failed host localhost port 3306 protocol mysql then restart
  if 5 restarts within 5 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/memcached',
  'check process memcached with pidfile /var/run/memcached.pid
  group www
  start program = "/etc/init.d/memcached start"
  stop program = "/etc/init.d/memcached stop"
  if failed host localhost port 11211 then restart
  if 5 restarts within 5 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/postfix',
  'check process postfix with pidfile /var/spool/postfix/pid/master.pid
  group mail
  start program = "/etc/init.d/postfix start"
  stop  program = "/etc/init.d/postfix stop"
  if failed port 25 protocol smtp then restart
  if 5 restarts within 5 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/spamassassin',
  'check process spamassassin with pidfile /var/run/spamd.pid
  group mail
  start program = "/etc/init.d/spamassassin start"
  stop  program = "/etc/init.d/spamassassin stop"
  if 5 restarts within 5 cycles then timeout');
  file_put_contents('/etc/monit/conf.d/sshd',
  'check process sshd with pidfile /var/run/sshd.pid
  start program "/etc/init.d/ssh start"
  stop program "/etc/init.d/ssh stop"
  if failed host 127.0.0.1 port 22 protocol ssh then restart
   if 5 restarts within 5 cycles then timeout');
  Execute('service monit restart');
}

InstallPackages();
PrepareDatabase();
InstallPostfixAdmin();
VirtualMailUser();
ConfigureDovecot();
SpamVirus();
ConfigurePostfix();
RestartAll();
InstallMonit();
InstallRoundCube();


Vnější odkazy