Difference between revisions of "Linux Mail Server: Exim4"

From RHS Wiki
Jump to navigation Jump to search
Tag: visualeditor
Tag: visualeditor
 
(15 intermediate revisions by the same user not shown)
Line 100: Line 100:
 
/etc/apache2/sites-available/webmail.conf
 
/etc/apache2/sites-available/webmail.conf
 
  <nowiki><VirtualHost *:80>
 
  <nowiki><VirtualHost *:80>
                  ServerAdmin rafael@herrerosolis.com
+
                                  ServerAdmin rafael@herrerosolis.com
                  Redirect permanent / https://webmail.herrerosolis.com/
+
                                  Redirect permanent / https://webmail.herrerosolis.com/
              #    DocumentRoot /var/www/rafael
+
                              #    DocumentRoot /var/www/rafael
                  ServerName webmail.herrerosolis.com
+
                                  ServerName webmail.herrerosolis.com
                  ServerAlias webmail.herrerosolis.com
+
                                  ServerAlias webmail.herrerosolis.com
                  ErrorLog /var/log/apache2/webmail/error.log
+
                                  ErrorLog /var/log/apache2/webmail/error.log
             
+
                             
                  # Posible values include: debug, info, notice, warn, error, crit.
+
                                  # Posible values include: debug, info, notice, warn, error, crit.
                  # alert, emerg.
+
                                  # alert, emerg.
             
+
                             
                  CustomLog /var/log/apache2/webmail/access.log combined
+
                                  CustomLog /var/log/apache2/webmail/access.log combined
              </virtualHost>
+
                              </virtualHost>
             
+
                             
              <VirtualHost *:443>
+
                              <VirtualHost *:443>
                  ServerAdmin rafael@herrerosolis.com
+
                                  ServerAdmin rafael@herrerosolis.com
                  DocumentRoot /var/lib/roundcube
+
                                  DocumentRoot /var/lib/roundcube
                  ServerName webmail.herrerosolis.com
+
                                  ServerName webmail.herrerosolis.com
                  ServerAlias webmail.herrerosolis.com
+
                                  ServerAlias webmail.herrerosolis.com
             
+
                             
                  ErrorLog /var/log/apache2/webmail/error.log
+
                                  ErrorLog /var/log/apache2/webmail/error.log
             
+
                             
                  # Posible values include: debug, info, notice, warn, error, crit.
+
                                  # Posible values include: debug, info, notice, warn, error, crit.
                  # alert, emerg.
+
                                  # alert, emerg.
                  CustomLog /var/log/apache2/webmail/access.log combined
+
                                  CustomLog /var/log/apache2/webmail/access.log combined
                  SSLEngine on
+
                                  SSLEngine on
                  SSLCertificateFile /etc/ssl/certs/herrerosolis.crt
+
                                  SSLCertificateFile /etc/ssl/certs/herrerosolis.crt
                  SSLCertificateKeyFile /etc/ssl/private/herrerosolis.key
+
                                  SSLCertificateKeyFile /etc/ssl/private/herrerosolis.key
              </virtualHost>
+
                              </virtualHost>
              </nowiki>
+
                              </nowiki>
  
===OPTIONAL: Remove TinyMCE===
+
====OPTIONAL: Remove TinyMCE====
 
TinyMCE is a WYSIWYG text-editor for HTML emails. I hate it. It had a long history of being insecure, buggy, slow, and hard to use. So I disable it:
 
TinyMCE is a WYSIWYG text-editor for HTML emails. I hate it. It had a long history of being insecure, buggy, slow, and hard to use. So I disable it:
  
Line 136: Line 136:
  
 
  <nowiki>Comment out these lines:
 
  <nowiki>Comment out these lines:
              #<Directory "/usr/share/tinymce/www/">
+
                              #<Directory "/usr/share/tinymce/www/">
              #      Options Indexes MultiViews FollowSymLinks
+
                              #      Options Indexes MultiViews FollowSymLinks
              #      AllowOverride None
+
                              #      AllowOverride None
              #      Order allow,deny
+
                              #      Order allow,deny
              #      allow from all
+
                              #      allow from all
              #</Directory>
+
                              #</Directory>
              </nowiki>
+
                              </nowiki>
  
 
===Create Databases===
 
===Create Databases===
mysql -u root -p
+
<syntaxhighlight lang="bash">
 +
mysql -u root -p
 +
</syntaxhighlight>
 +
 +
<syntaxhighlight lang="mysql">
 +
CREATE DATABASE email_accounts;
 +
USE email_accounts;
 +
CREATE TABLE mailboxes (
 +
    id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 +
    domain_id INT(10) NOT NULL,
 +
    local_part VARCHAR(250) NOT NULL,
 +
    password VARCHAR(100) NULL,
 +
    description VARCHAR(250) NULL,
 +
    active TINYINT(1) NOT NULL DEFAULT 0,
 +
    created TIMESTAMP NOT NULL DEFAULT NOW(),
 +
    modified TIMESTAMP NULL
 +
);
  
CREATE DATABASE email_accounts;
+
CREATE TABLE aliases (
 +
  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 +
  domain_id INT(10) NOT NULL,
 +
  local_part VARCHAR(250) NOT NULL,
 +
  goto VARCHAR(250) NOT NULL,
 +
  description VARCHAR(250) NULL,
 +
  active TINYINT(1) NOT NULL DEFAULT 0,
 +
  created TIMESTAMP NOT NULL DEFAULT NOW(),
 +
  modified TIMESTAMP NULL
 +
);
  
USE email_accounts;
+
CREATE TABLE vacations (
 +
  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 +
  mailbox_id INT(10) NOT NULL,
 +
  subject VARCHAR(250) NOT NULL,
 +
  body TEXT NOT NULL,
 +
  description VARCHAR(250) NULL,
 +
  active TINYINT(1) NOT NULL DEFAULT 0,
 +
  created TIMESTAMP NOT NULL DEFAULT NOW(),
 +
  modified TIMESTAMP NULL
 +
);
  
<nowiki>CREATE TABLE mailboxes (
+
CREATE TABLE domains (
                  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+
  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
                  domain_id INT(10) NOT NULL,
+
  fqdn VARCHAR(250) NOT NULL,
                  local_part VARCHAR(250) NOT NULL,
+
  type ENUM('local','relay') NOT NULL DEFAULT 'local',
                  password VARCHAR(100) NULL,
+
  description VARCHAR(250) NULL,
                  description VARCHAR(250) NULL,
+
  active TINYINT(1) NOT NULL DEFAULT 0,
                  active TINYINT(1) NOT NULL DEFAULT 0,
+
  created TIMESTAMP NOT NULL DEFAULT NOW(),
                  created TIMESTAMP NOT NULL DEFAULT NOW(),
+
  modified TIMESTAMP NULL
                  modified TIMESTAMP NULL
+
);
              );
+
</syntaxhighlight>
              CREATE TABLE aliases (
+
                  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 
                  domain_id INT(10) NOT NULL,
 
                  local_part VARCHAR(250) NOT NULL,
 
                  goto VARCHAR(250) NOT NULL,
 
                  description VARCHAR(250) NULL,
 
                  active TINYINT(1) NOT NULL DEFAULT 0,
 
                  created TIMESTAMP NOT NULL DEFAULT NOW(),
 
                  modified TIMESTAMP NULL
 
              );
 
              CREATE TABLE vacations (
 
                  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 
                  mailbox_id INT(10) NOT NULL,
 
                  subject VARCHAR(250) NOT NULL,
 
                  body TEXT NOT NULL,
 
                  description VARCHAR(250) NULL,
 
                  active TINYINT(1) NOT NULL DEFAULT 0,
 
                  created TIMESTAMP NOT NULL DEFAULT NOW(),
 
                  modified TIMESTAMP NULL
 
              );
 
             
 
              CREATE TABLE domains (
 
                  id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 
                  fqdn VARCHAR(250) NOT NULL,
 
                  type ENUM('local','relay') NOT NULL DEFAULT 'local',
 
                  description VARCHAR(250) NULL,
 
                  active TINYINT(1) NOT NULL DEFAULT 0,
 
                  created TIMESTAMP NOT NULL DEFAULT NOW(),
 
                  modified TIMESTAMP NULL
 
              );
 
              </nowiki>
 
  
 
===Create your first email account and domain===
 
===Create your first email account and domain===
 
====Domain====
 
====Domain====
<nowiki>INSERT INTO domains VALUES(NULL,'herrerosolis.com','local','My personal domain',1,NOW(),NOW());</nowiki>
+
<syntaxhighlight lang="mysql">
 +
INSERT INTO domains VALUES(NULL,'herrerosolis.com','local','My personal domain',1,NOW(),NOW());
 +
</syntaxhighlight>
 +
  
 
====User====
 
====User====
<nowiki>INSERT INTO mailboxes VALUES(NULL,1,'joe',MD5('password - choose a good one'),'My account for joe@herrerosolis.com',1,NOW(),NOW());</nowiki>
+
<syntaxhighlight lang="mysql">
 +
INSERT INTO mailboxes VALUES(NULL,1,'joe',MD5('password - choose a good one'),'My account for joe@herrerosolis.com',1,NOW(),NOW());
 +
</syntaxhighlight>
 +
  
 
====Alias====
 
====Alias====
<nowiki>insert into aliases values (null, 1, 'rafa', 'rafael@herrerosolis.com', 'Redirect to me', 1, NOW(), NOW() );</nowiki>
+
<syntaxhighlight lang="mysql">
 +
insert into aliases values (null, 1, 'rafa', 'rafael@herrerosolis.com', 'Redirect to me', 1, NOW(), NOW() );
 +
</syntaxhighlight>
 +
  
 
===Create a database-account to access the database===
 
===Create a database-account to access the database===
<nowiki>grant ALL on email_accounts.* to 'email'@'localhost' identified by 'password';
+
<syntaxhighlight lang="mysql">
              flush privileges;
+
grant ALL on email_accounts.* to 'cartero'@'localhost' identified by 'password';
              </nowiki>
+
flush privileges;
 +
</syntaxhighlight>
 +
 +
                     
 
http://bradthemad.org/tech/notes/exim_cheatsheet.php
 
http://bradthemad.org/tech/notes/exim_cheatsheet.php
  
Line 307: Line 323:
 
                   domains.active=1}}
 
                   domains.active=1}}
  
</syntaxhighlight>
+
</syntaxhighlight>  
 
 
                
 
                
  
 
====Transports====
 
====Transports====
CREATE / OVERWRITE the file /etc/exim4/conf.d/transport/30_exim4-config_dovecot
+
CREATE / OVERWRITE the file /etc/exim4/conf.d/transport/30_exim4-config_dovecot<syntaxhighlight lang="text">
 +
### transport/30_exim4-config_dovecot
 +
#################################
  
<nowiki>### transport/30_exim4-config_dovecot
+
dovecot_delivery:
              #################################
+
    driver = appendfile
             
+
    maildir_format = true
              #
+
    directory = /var/spool/mail/$domain/$local_part
             
+
    create_directory = true
              dovecot_delivery:
+
    directory_mode = 0770
                  driver = appendfile
+
    mode_fail_narrower = false
                  maildir_format = true
+
    message_prefix =
                  directory = /var/spool/mail/$domain/$local_part
+
    message_suffix =
                  create_directory = true
+
    delivery_date_add
                  directory_mode = 0770
+
    envelope_to_add
                  mode_fail_narrower = false
+
    return_path_add
                  message_prefix =
+
    user = mail
                  message_suffix =
+
    group = mail
                  delivery_date_add
+
    mode = 0660
                  envelope_to_add
+
</syntaxhighlight>  
                  return_path_add
+
             
                  user = mail
 
                  group = mail
 
                  mode = 0660
 
              </nowiki>
 
  
 
====Auth====
 
====Auth====
CREATE the file /etc/exim4/conf.d/auth/20_exim4-config_mysql-authenticator
+
CREATE the file /etc/exim4/conf.d/auth/20_exim4-config_mysql-authenticator<syntaxhighlight lang="text">
 +
### AUTHENTICATIOR SECTION
 +
 
 +
auth_plain:
 +
    driver = plaintext
 +
    public_name = PLAIN
 +
    server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
 +
                      mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',1) AND \
 +
                      mailboxes.password=MD5('${quote_mysql:$auth3}') AND \
 +
                      mailboxes.active=1 AND \
 +
                      mailboxes.domain_id=domains.id AND \
 +
                      domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',-1) AND \
 +
                      domains.active=1}{yes}{no}}
 +
    server_prompts = :
 +
    server_set_id = $auth2
 +
 
 +
auth_login:
 +
    driver = plaintext
 +
    public_name = LOGIN
 +
    server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
 +
                      mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',1) AND \
 +
                      mailboxes.password=MD5('${quote_mysql:$auth2}') AND \
 +
                      mailboxes.active=1 AND \
 +
                      mailboxes.domain_id=domains.id AND \
 +
                      domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',-1) AND \
 +
                      domains.active=1}{yes}{no}}
 +
    server_prompts = Username:: : Password::
 +
    server_set_id = $auth1
  
<nowiki>### AUTHENTICATIOR SECTION
+
</syntaxhighlight>  
             
 
              auth_plain:
 
                  driver = plaintext
 
                  public_name = PLAIN
 
                  server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
 
                                    mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',1) AND \
 
                                    mailboxes.password=MD5('${quote_mysql:$auth3}') AND \
 
                                    mailboxes.active=1 AND \
 
                                    mailboxes.domain_id=domains.id AND \
 
                                    domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',-1) AND \
 
                                    domains.active=1}{yes}{no}}
 
                  server_prompts = :
 
                  server_set_id = $auth2
 
             
 
              auth_login:
 
                  driver = plaintext
 
                  public_name = LOGIN
 
                  server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
 
                                    mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',1) AND \
 
                                    mailboxes.password=MD5('${quote_mysql:$auth2}') AND \
 
                                    mailboxes.active=1 AND \
 
                                    mailboxes.domain_id=domains.id AND \
 
                                    domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',-1) AND \
 
                                    domains.active=1}{yes}{no}}
 
                  server_prompts = Username:: : Password::
 
                  server_set_id = $auth1</nowiki>
 
  
  
Line 421: Line 435:
 
  cat /etc/passwd | grep mail
 
  cat /etc/passwd | grep mail
  
ADD to the file /etc/dovecot/dovecot.conf
+
ADD to the file /etc/dovecot/dovecot.conf<syntaxhighlight lang="text">
<nowiki>protocols = imap
+
protocols = imap  
              listen = *, ::</nowiki>
+
listen = *, ::
 +
</syntaxhighlight>
 +
  
 
Add to the file /etc/dovecot/conf.d/10-mail.conf
 
Add to the file /etc/dovecot/conf.d/10-mail.conf
Line 431: Line 447:
 
  <nowiki>!include auth-sql.conf.ext</nowiki>
 
  <nowiki>!include auth-sql.conf.ext</nowiki>
  
ADD to the file /etc/dovecot/dovecot-sql.conf.ext
+
ADD to the file /etc/dovecot/dovecot-sql.conf.ext<syntaxhighlight lang="text">
<nowiki>driver = mysql
+
driver = mysql
              connect = host=127.0.0.1 dbname=email_accounts user=email password=password
+
 
              default_pass_scheme = MD5
+
connect = host=127.0.0.1 dbname=[MYSQL DATABASE] user=[MYSQL user] password=[MYSQL mailbox PASSWORD plain text]
             
+
default_pass_scheme = MD5
              password_query = SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) as `user`, mailboxes.password AS `password`,'/var/spool/mail/%d/%n' AS `userdb_home`, [YOUR UID] AS `userdb_uid`, [YOUR GID] AS `userdb_gid` FROM `mailboxes`, `domains` WHERE mailboxes.local_part = '%n' AND mailboxes.active = 1 AND mailboxes.domain_id = domains.id AND domains.fqdn = '%d' AND domains.active = 1
+
 
             
+
password_query = SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) as `user`, mailboxes.password AS `password`,'/var/spool/mail/%d/%n' AS `userdb_home`, [YOUR mail user GID] AS `userdb_uid`, [YOUR mail user GID] AS `userdb_gid` FROM `mailboxes`, `domains` WHERE mailboxes.local_part = '%n' AND mailboxes.active = 1 AND mailboxes.domain_id = domains.id AND domains.fqdn = '%d' AND domains.active = 1
              user_query = SELECT '/var/spool/mail/%d/%n' AS `home`, [YOUR UID] AS `uid`, [YOUR GID] AS `gid`</nowiki>
+
 
 +
user_query = SELECT '/var/spool/mail/%d/%n' AS `home`, [YOUR mail USER UID] AS `uid`, [YOUR mail user GID] AS `gid`
 +
 
 +
</syntaxhighlight>
 +
  
 
====SSL====
 
====SSL====
 
- Get certificates ej: Lets Encrypt
 
- Get certificates ej: Lets Encrypt
 
Edit: /etc/dovecot/conf.d/10-ssl.conf
 
Edit: /etc/dovecot/conf.d/10-ssl.conf
  <nowiki>ssl = required
+
  ssl = required
              # Preferred permissions: root:root 0444
+
              ssl_cert = </etc/ssl/certs/dovecot.pem
+
ssl_key = </etc/letsencrypt/live/webmail.herrerosolis.com/privkey.pem
              # Preferred permissions: root:root 0400
+
ssl_cert = </etc/letsencrypt/live/webmail.herrerosolis.com/fullchain.pem
              ssl_key = </etc/ssl/private/dovecot.pem</nowiki>
 
  
 
====Ports====
 
====Ports====
Line 466: Line 485:
 
Add to /etc/roundcube/config.inc.php
 
Add to /etc/roundcube/config.inc.php
 
  <nowiki>$config['imap_conn_options'] = array(
 
  <nowiki>$config['imap_conn_options'] = array(
                'ssl'        => array(
+
                                'ssl'        => array(
                  'verify_peer'  => false,
+
                                  'verify_peer'  => false,
                  'verify_peer_name'  => false,
+
                                  'verify_peer_name'  => false,
                  'allow_self_signed' => true,
+
                                  'allow_self_signed' => true,
                ),
+
                                ),
              );
+
                              );
              </nowiki>
+
                              </nowiki>
  
 
====Enable Password Plugin====
 
====Enable Password Plugin====
Line 502: Line 521:
 
Then edit your local composer.json file and add the "vendor/plugin" names to the "require" section of the JSON structure. Don't forget to specify the version constraint:
 
Then edit your local composer.json file and add the "vendor/plugin" names to the "require" section of the JSON structure. Don't forget to specify the version constraint:
 
  <nowiki>"require" : {
 
  <nowiki>"require" : {
                  ...,
+
                                  ...,
                  "roundcube/rcsample": ">=0.2.0"
+
                                  "roundcube/rcsample": ">=0.2.0"
              }</nowiki>
+
                              }</nowiki>
  
 
After every change to composer.json run
 
After every change to composer.json run
 
  php composer.phar install
 
  php composer.phar install
  
==Restart Everithing==  
+
==Restart Everithing==
<nowiki>/etc/init.d/apache2 restart
+
<syntaxhighlight lang="text">
              /etc/init.d/exim4 restart
+
/etc/init.d/apache2 restart  
              /etc/init.d/dovecot restart</nowiki>
+
/etc/init.d/exim4 restart  
 
+
/etc/init.d/dovecot restart
Exim may output a “paniclog”. If so, read it, fix it – and then manually delete the paniclog file, or else you’ll keep getting fake warnings every time you restart exim.
+
</syntaxhighlight>Exim may output a “paniclog”. If so, read it, fix it – and then manually delete the paniclog file, or else you’ll keep getting fake warnings every time you restart exim.
 
 
 
 
 
 
 
 
  
  
Line 563: Line 578:
  
 
  mailq | grep frozen | awk '{print $3}' | xargs exim -v -M
 
  mailq | grep frozen | awk '{print $3}' | xargs exim -v -M
 +
 +
====Exim usefull commands====
 +
https://hostpapasupport.com/list-useful-commands-manage-exim-mail-server/
 +
 +
====Spam filters filtering your mails====
 +
https://www.mail-tester.com/
  
  
Line 588: Line 609:
 
1. In the file /etc/dovecot/conf.d/auth-sql-conf.ext uncomment driver and set it to mysql
 
1. In the file /etc/dovecot/conf.d/auth-sql-conf.ext uncomment driver and set it to mysql
 
2. /etc/dovecot/conf.d/10-mail.conf uncomment first_valid_uid and set it to [your_uid] (ie. 8). If you need to do the same for first_valid_gid
 
2. /etc/dovecot/conf.d/10-mail.conf uncomment first_valid_uid and set it to [your_uid] (ie. 8). If you need to do the same for first_valid_gid
 +
 +
https://pasztor.at/blog/the-big-exim-tutorial
 
=Adding new Domains=
 
=Adding new Domains=
  
 
#Add the new domain to to MySQL email_accounts.domains
 
#Add the new domain to to MySQL email_accounts.domains
 
#Start creating accounts with the new domain id
 
#Start creating accounts with the new domain id
 +
 +
=Auxiliary Scripts=
 +
 +
===Create email account===
 +
<syntaxhighlight lang="bash">
 +
#!/bin/bash
 +
PASSWORD1="hola"
 +
PASSWORD2="mundo"
 +
DOMAIN="herrerosolis.com"
 +
echo "******************************* MAIL ACCOUNT INFORMATION *****************************************"
 +
 +
read -p "Username (without@herrerosolis.com): " USERNAME
 +
 +
while [[ "$PASSWORD1" != "$PASSWORD2" ]]; do
 +
    read -s -p "Password: " PASSWORD1
 +
    echo
 +
    read -s -p "Confirm password: " PASSWORD2
 +
    if [ "$PASSWORD1" != "$PASSWORD2" ]; then
 +
        echo "Passwords dont match, try again..."
 +
    fi
 +
done
 +
echo
 +
echo
 +
echo "Input MySQL database password for user mail"
 +
 +
mysql -h 127.0.0.1 -u cartero -p -D email_accounts -e "INSERT INTO mailboxes VALUES(NULL,1,'$USERNAME',MD5('$PASSWORD1'),'Mail account for $USERNAME@${DOMAIN}',1,NOW(),NOW());"
 +
</syntaxhighlight>
 +
 +
===Create email alias===
 +
<syntaxhighlight lang="bash">
 +
#!/bin/bash
 +
 +
echo "******************************* MAIL ACCOUNT INFORMATION *****************************************"
 +
 +
read -p "Alias name (without _@redactate.com): " ALIAS
 +
 +
read -p "Destiny account (full email address): " GOTO
 +
 +
echo
 +
echo
 +
echo "Input MySQL database password for user mail"
 +
 +
mysql -h 127.0.0.1 -u cartero -p -D email_accounts -e "INSERT INTO aliases VALUES (null, 2, '$ALIAS', '$GOTO', 'Redirecting $ALIAS@ to $GOTO', 1, NOW(),NOW());"
 +
 +
</syntaxhighlight>

Latest revision as of 12:37, 21 August 2019

Whispers Mail Server

Stack

  • Web server (Apache)
  • Database server (MySQL)
  • Email server (MTA) (Exim4)
  • IMAP server (Dovecot)
  • Webmail server (Roundcube)

Install

  • apt-get install apache2-mpm-prefork
    • (Some of these email servers require PHP; PHP is crappy and requires mpm-prefork (the ‘slow’ version of Apache))
  • apt-get install mysql-client
    • (should auto-install something like: mysql-common + mysql-client-5.5)
  • apt-get install mysql-server
    • (should auto-install something like: mysql-server-5.5 + mysql-server-core-5.5)
  • apt-get install exim4
  • apt-get install exim4-base
  • apt-get install exim4-config
  • apt-get install exim4-daemon-heavy
    • (there’s an “exim4-mysql” that might be sufficient to replace this, but I gave up: there are way too many exim4 packages, and no help for installing the “correct” set, so … just pick this and get the lot!)
  • apt-get install dovecot-core
  • apt-get install dovecot-imapd
  • apt-get install dovecot-mysql
  • apt-get install roundcube
  • apt-get install roundcube-core
  • apt-get install roundcube-mysql
  • apt-get install roundcube-plugins

Setup

DNS

DNS Registries required by Mail Server
Type Host Points to TTL
A @ 54.154.227.190 1h
CNAME webmail @ 1h
MX @ webmail.herrerosolis.com 1h

You need an “MX” record on your DNS server, and it needs to point to your main server where you’ll run your email, web, etc.


Remove MX registries from godaddy email forwarder:
MX:
Host: @; Apunta a:smtp.secureserver.net; Prioridad:30; TTL:1h
Host: @; Apunta a:mailstore1.secureserver.net; Prioridad:50; TTL:1h

SPF
DNS SPF Registry
Type Host TXT Value
TXT @ v=spf1 mx ip4:54.154.227.190 ~all
DMARC
DNS DMARC Registry
Type Host TXT Value
TXT _dmarc v=DMARC1; p=none  Verification details:      mail-tester.com; dmarc=none header.from=herrerosolis.com     From Domain: herrerosolis.com     DKIM Domain:MIIBIjANBg[...]qCwPwIDAQAB


DKIM
DNS DKIM Registry
Type Host TXT Value
TXT webmail._domainkey v=DKIM1; k=rsa; p=MIIBIjAN[...]IDAQAB
  • Note: DKIM Host must be {selector}._domainkey where {selector} = DKIM_SELECTOR variable from /etc/exim4/conf.d/main/000_localmacros

Apache

/etc/apache2/sites-available/webmail.conf

<VirtualHost *:80>
                                  ServerAdmin rafael@herrerosolis.com
                                  Redirect permanent / https://webmail.herrerosolis.com/
                              #    DocumentRoot /var/www/rafael
                                  ServerName webmail.herrerosolis.com
                                  ServerAlias webmail.herrerosolis.com
                                  ErrorLog /var/log/apache2/webmail/error.log
                              
                                  # Posible values include: debug, info, notice, warn, error, crit.
                                  # alert, emerg.
                              
                                  CustomLog /var/log/apache2/webmail/access.log combined
                              </virtualHost>
                              
                              <VirtualHost *:443>
                                  ServerAdmin rafael@herrerosolis.com
                                  DocumentRoot /var/lib/roundcube
                                  ServerName webmail.herrerosolis.com
                                  ServerAlias webmail.herrerosolis.com
                              
                                  ErrorLog /var/log/apache2/webmail/error.log
                              
                                  # Posible values include: debug, info, notice, warn, error, crit.
                                  # alert, emerg.
                                  CustomLog /var/log/apache2/webmail/access.log combined
                                  SSLEngine on
                                  SSLCertificateFile /etc/ssl/certs/herrerosolis.crt
                                  SSLCertificateKeyFile /etc/ssl/private/herrerosolis.key
                              </virtualHost>
                              

OPTIONAL: Remove TinyMCE

TinyMCE is a WYSIWYG text-editor for HTML emails. I hate it. It had a long history of being insecure, buggy, slow, and hard to use. So I disable it:

Edit /etc/roundcube/apache.conf:

Comment out these lines:
                              	#<Directory "/usr/share/tinymce/www/">
                              	#      Options Indexes MultiViews FollowSymLinks
                              	#      AllowOverride None
                              	#      Order allow,deny
                              	#      allow from all
                              	#</Directory>
                              

Create Databases

mysql -u root -p
CREATE DATABASE email_accounts;
USE email_accounts;
CREATE TABLE mailboxes (
    id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    domain_id INT(10) NOT NULL,
    local_part VARCHAR(250) NOT NULL,
    password VARCHAR(100) NULL,
    description VARCHAR(250) NULL,
    active TINYINT(1) NOT NULL DEFAULT 0,
    created TIMESTAMP NOT NULL DEFAULT NOW(),
    modified TIMESTAMP NULL
);

CREATE TABLE aliases (
   id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
   domain_id INT(10) NOT NULL,
   local_part VARCHAR(250) NOT NULL,
   goto VARCHAR(250) NOT NULL,
   description VARCHAR(250) NULL,
   active TINYINT(1) NOT NULL DEFAULT 0,
   created TIMESTAMP NOT NULL DEFAULT NOW(),
   modified TIMESTAMP NULL
);

CREATE TABLE vacations (
   id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
   mailbox_id INT(10) NOT NULL,
   subject VARCHAR(250) NOT NULL,
   body TEXT NOT NULL,
   description VARCHAR(250) NULL,
   active TINYINT(1) NOT NULL DEFAULT 0,
   created TIMESTAMP NOT NULL DEFAULT NOW(),
   modified TIMESTAMP NULL
);

CREATE TABLE domains (
   id INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
   fqdn VARCHAR(250) NOT NULL,
   type ENUM('local','relay') NOT NULL DEFAULT 'local',
   description VARCHAR(250) NULL,
   active TINYINT(1) NOT NULL DEFAULT 0,
   created TIMESTAMP NOT NULL DEFAULT NOW(),
   modified TIMESTAMP NULL
);


Create your first email account and domain

Domain

INSERT INTO domains VALUES(NULL,'herrerosolis.com','local','My personal domain',1,NOW(),NOW());


User

INSERT INTO mailboxes VALUES(NULL,1,'joe',MD5('password - choose a good one'),'My account for joe@herrerosolis.com',1,NOW(),NOW());


Alias

insert into aliases values (null, 1, 'rafa', 'rafael@herrerosolis.com', 'Redirect to me', 1, NOW(), NOW() );


Create a database-account to access the database

grant ALL on email_accounts.* to 'cartero'@'localhost' identified by 'password';
flush privileges;


http://bradthemad.org/tech/notes/exim_cheatsheet.php

Exim Configuration

  • TODO: review daemon ports @/etc/exim4/conf.d/main/9_exim4-config_daemon


dpkg-reconfigure exim4-config

Creates the file: /etc/exim4/update-exim4.conf.conf

Should look like:

# /etc/exim4/update-exim4.conf.conf
#
# Edit this file and /etc/mailname by hand and execute update-exim4.conf # yourself or use 'dpkg-reconfigure exim4-config'
#
# Please note that this is _not_ a dpkg-conffile and that automatic changes
# to this file might happen. The code handling this will honor your local
# changes, so this is usually fine, but will break local schemes that mess
# around with multiple versions of the file.
#
# update-exim4.conf uses this file to determine variable values to generate
# exim configuration macros for the configuration file.
#
# Most settings found in here do have corresponding questions in the
# Debconf configuration, but not all of them.
#
# This is a Debian specific file

dc_eximconfig_configtype='internet'
dc_other_hostnames='[YOUR DOMAIN 1];[YOUR DOMAIN 2]'
dc_local_interfaces='127.0.0.1;[PUT YOUR SERVER's IP ADDRESS HERE]'
dc_readhost=''
dc_relay_domains=''
dc_minimaldns='false'
dc_relay_nets=''
dc_smarthost=''
CFILEMODE='644'
dc_use_split_config='true'
dc_hide_mailname=''
dc_mailname_in_oh='true'
dc_localdelivery='maildir_home'

Macros

ADD the following to /etc/exim4/conf.d/main/000_localmacros

MAIN_LOCAL_DOMAINS = @:localhost:dsearch;/etc/exim4/virtual:${lookup mysql{SELECT fqdn AS domain FROM domains WHERE fqdn='${quote_mysql:$domain}' AND type='local' AND active=1}}

ADD the following to /etc/exim4/conf.d/main/01_exim4-config_listmacrosdefs

# List of domains considered local for exim. Domains not listed here
# need to be deliverable remotely.

domainlist local_domains = MAIN_LOCAL_DOMAINS

# MySQL because exim4 on Debian doesn't always add this:

MYSQL_SERVER=127.0.0.1
MYSQL_DB=email_accounts
MYSQL_USER=email
MYSQL_PASSWORD=password
hide mysql_servers = MYSQL_SERVER/MYSQL_DB/MYSQL_USER/MYSQL_PASSWORD


Routers

CREATE the file /etc/exim4/conf.d/router/360_exim4-config_mysqlusers

dovecot_user:
      driver = accept
        condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) AS goto FROM domains,mailboxes WHERE \
                   mailboxes.local_part='${quote_mysql:$local_part}' AND \
                   mailboxes.active=1 AND \
                   mailboxes.domain_id=domains.id AND \
                   domains.fqdn='${quote_mysql:$domain}' AND \
                   domains.active=1}{yes}{no}}
     transport = dovecot_delivery


Either DELETE this file, or comment-out all lines /etc/exim4/conf.d/router/400_exim4-config_system_aliases

CREATE this file /etc/exim4/conf.d/router/401_exim4-config_mysql_aliases

### router/401_exim4-config_mysql_aliases
#################################

# ADAM: This router handles aliasing using the proprietary mysql setup
#
# c.f. http://alex.mamchenkov.net/2010/06/24/exim-dovecot-and-mysql/
#

system_aliases:
     driver = redirect
     allow_fail
     allow_defer
     data = ${lookup mysql{SELECT aliases.goto AS goto FROM domains,aliases WHERE \
                   (aliases.local_part='${quote_mysql:$local_part}' OR aliases.local_part='@') AND \
                   aliases.active=1 AND \
                   aliases.domain_id=domains.id AND \
                   domains.fqdn='${quote_mysql:$domain}' AND \
                   domains.active=1}}


Transports

CREATE / OVERWRITE the file /etc/exim4/conf.d/transport/30_exim4-config_dovecot

### transport/30_exim4-config_dovecot
#################################

dovecot_delivery:
     driver = appendfile
     maildir_format = true
     directory = /var/spool/mail/$domain/$local_part
     create_directory = true
     directory_mode = 0770
     mode_fail_narrower = false
     message_prefix =
     message_suffix =
     delivery_date_add
     envelope_to_add
     return_path_add
     user = mail
     group = mail
     mode = 0660


Auth

CREATE the file /etc/exim4/conf.d/auth/20_exim4-config_mysql-authenticator

### AUTHENTICATIOR SECTION

auth_plain:
     driver = plaintext
     public_name = PLAIN
     server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
                       mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',1) AND \
                       mailboxes.password=MD5('${quote_mysql:$auth3}') AND \
                       mailboxes.active=1 AND \
                       mailboxes.domain_id=domains.id AND \
                       domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth2}','@',-1) AND \
                       domains.active=1}{yes}{no}}
     server_prompts = :
     server_set_id = $auth2

auth_login:
     driver = plaintext
     public_name = LOGIN
     server_condition = ${lookup mysql{SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) FROM mailboxes,domains WHERE \
                       mailboxes.local_part=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',1) AND \
                       mailboxes.password=MD5('${quote_mysql:$auth2}') AND \
                       mailboxes.active=1 AND \
                       mailboxes.domain_id=domains.id AND \
                       domains.fqdn=SUBSTRING_INDEX('${quote_mysql:$auth1}','@',-1) AND \
                       domains.active=1}{yes}{no}}
     server_prompts = Username:: : Password::
     server_set_id = $auth1


Ports

CREATE the file /etc/exim4/conf.d/main/9_exim4-config_daemon

daemon_smtp_ports = 25 : 465 : 587

http://www.exim.org/exim-html-current/doc/html/spec_html/ch-main_configuration.html
On amazon AWS port 25 might be blocked: https://aws.amazon.com/forms/ec2-email-limit-rdns-request?catalog=true&isauthcode=true

DKIM

Generate DKIM private & public keys:

sudo mkdir /etc/exim4/dkim && cd /etc/exim4/dkim
sudo openssl genrsa -out webmail.herrerosolis.com.dkim.pem 2048
sudo openssl rsa -in webmail.herrerosolis.com.dkim.pem -pubout |sudo tee webmail.herrerosolis.com.dkim.pub

Configure your exim to sign using the DKIM key

Edit /etc/exim4/conf.d/main/000_localmacros and add at the beggining:

DKIM_DOMAIN = ${lc:${domain:$h_from:}}
DKIM_KEY_FILE = /etc/exim4/dkim/webmail.herrerosolis.com.dkim.pem
DKIM_PRIVATE_KEY = ${if exists{DKIM_KEY_FILE}{DKIM_KEY_FILE}{0}}
DKIM_SELECTOR = webmail

DKIM Multiple Domains (TODO)

TODO: Handle multiple domains: https://debian-administration.org/article/718/DKIM-signing_outgoing_mail_with_exim4

DKIM_CANON = relaxed
DKIM_SELECTOR = 20150726

# Get the domain from the outgoing mail.
DKIM_DOMAIN = ${sg{${lc:${domain:$h_from:}}}{^www\.}{}}

# The file is based on the outgoing domain-name in the from-header.
DKIM_FILE = /etc/exim4/dkim/{DKIM_DOMAIN}.pem

# If key exists then use it, if not don't.
DKIM_PRIVATE_KEY = ${if exists{DKIM_FILE}{DKIM_FILE}{0}}


Then run:

update-exim4.conf
service exim4 restart

Dovecot

Make sure you choose split files, can be fixed with:

dpkg-reconfigure dovecot-core

Find mail user uid & gid:

cat /etc/passwd | grep mail

ADD to the file /etc/dovecot/dovecot.conf

protocols = imap 
listen = *, ::


Add to the file /etc/dovecot/conf.d/10-mail.conf

mail_location = maildir:~

ADD to the file /etc/dovecot/conf.d/10-auth.conf

!include auth-sql.conf.ext

ADD to the file /etc/dovecot/dovecot-sql.conf.ext

driver = mysql

connect = host=127.0.0.1 dbname=[MYSQL DATABASE] user=[MYSQL user] password=[MYSQL mailbox PASSWORD plain text]
default_pass_scheme = MD5

password_query = SELECT CONCAT(mailboxes.local_part,'@',domains.fqdn) as `user`, mailboxes.password AS `password`,'/var/spool/mail/%d/%n' AS `userdb_home`, [YOUR mail user GID] AS `userdb_uid`, [YOUR mail user GID] AS `userdb_gid` FROM `mailboxes`, `domains` WHERE mailboxes.local_part = '%n' AND mailboxes.active = 1 AND mailboxes.domain_id = domains.id AND domains.fqdn = '%d' AND domains.active = 1

user_query = SELECT '/var/spool/mail/%d/%n' AS `home`, [YOUR mail USER UID] AS `uid`, [YOUR mail user GID] AS `gid`


SSL

- Get certificates ej: Lets Encrypt Edit: /etc/dovecot/conf.d/10-ssl.conf

ssl = required

ssl_key = </etc/letsencrypt/live/webmail.herrerosolis.com/privkey.pem
ssl_cert = </etc/letsencrypt/live/webmail.herrerosolis.com/fullchain.pem

Ports

Open ports: IMAP 143 & SSL IMAP 993

Roundcube

EDIT the file /etc/roundcube/main.inc.php

$rcmail_config['default_host'] = '[YOUR MX RECORD]';

Note: replace “[YOUR MX RECORD]” with the MX address you put on your DNS server at the very start. e.g. “mail.my-domain.com”.

In that file, there are instructions on how to make it automatically calculate the address using %n, %d, etc. If your MX records for your different domains follow the same pattern (e.g. they are all “mail.my-domain.com”), and your webmail login addresses all follow the same pattern (e.g. “wemail.my-domain.com”), you can put one string here and it will automatically log people into the right server in every case, based on the URL they visited.

TLS

@/etc/roundcube/config.inc.php

$config['default_host'] = "tls://webmail.herrerosolis.com";

Allow dovecot self-signed certificate

TODO: Lets Encrypt Certificate CA Add to /etc/roundcube/config.inc.php

$config['imap_conn_options'] = array(
                                'ssl'         => array(
                                   'verify_peer'  => false,
                                   'verify_peer_name'  => false,
                                   'allow_self_signed' => true,
                                 ),
                               );
                              

Enable Password Plugin

  • TODO: SQL / password plugin options

@/etc/roundcube/config.inc.php

$config['plugins'] = array('xskin', 'password');

Maximum size for attached documents =

  • TODO: Maximum size for attached documents

Fix zipdownload plugin

sudo apt-get install php7.0-zip
sudo apt-get install php7.0-gd

Spamassasin

  • TODO: Spamassasin

Roundcube Plugin Installer

cd <roundcube-project-root>
curl -s https://getcomposer.org/installer | php

composer.json-dist to composer.json in your Roundcube root directory

Installing Plugins

https://plugins.roundcube.net/explore/
Then edit your local composer.json file and add the "vendor/plugin" names to the "require" section of the JSON structure. Don't forget to specify the version constraint:

"require" : {
                                  ...,
                                  "roundcube/rcsample": ">=0.2.0"
                              }

After every change to composer.json run

php composer.phar install

Restart Everithing

/etc/init.d/apache2 restart 
/etc/init.d/exim4 restart 
/etc/init.d/dovecot restart

Exim may output a “paniclog”. If so, read it, fix it – and then manually delete the paniclog file, or else you’ll keep getting fake warnings every time you restart exim.


STARTTLS

https://wiki.debian.org/Exim

Troubleshooting

https://mediatemple.net/community/products/dv/204404634/troubleshooting-common-issues-with-email

Exim

exim4 -bP | grep tls_

test with:

sudo apt-get install swaks
swaks -a -tls -q HELO -s gollum.redactate.com -au test -ap '<>'

Receiving emails

Pick an email address that you added to the “email_accounts” database, and try sending email to it while logged-in to server command-line:

exim -d -bt testname@yourdomain.com

…this will give a COMPLETE list of what exim is doing, and it will tell you every decision it made along the way. It should eventually decide the address is “routeable” and OK it.

If that looks OK, try sending an email from your normal email account (e.g. your Hotmail / Gmail / Yahoo.com address). Wait a minute, then check the server to see if it crashed trying to receive the email, by checking the logfiles.

Check exim’s logfiles

Exim will put its logfiles in /var/log/exim4. Check for errors using: tail /var/log/exim4/mainlog

(if there’s a lot of errors, you’ll have to cat the whole thing)

If it rejected the email, it will send a bounce-back to your email provider (yahoo/gmail/etc), and it will ALSO put some info into: tail /var/log/exim4/rejectlog test:
https://www.wormly.com

Sending emails

sending emails

…I waited until I had webmail (Roundcube) working before trying this… Any other Exim problems?

If exim is working, but its blocking/rejecting/losing emails, it will “freeze” them after the first failure. You need to “unfreeze” (i.e. retry) each email to see if you’ve fixed the problem. To process all **frozen** messages in the Exim queue use this command:

mailq | grep frozen | awk '{print $3}' | xargs exim -v -M

Exim usefull commands

https://hostpapasupport.com/list-useful-commands-manage-exim-mail-server/

Spam filters filtering your mails

https://www.mail-tester.com/


Here is a list of commands to help: http://bradthemad.org/tech/notes/exim_cheatsheet.php

Test Dovecot

https://wiki2.dovecot.org/TestInstallation

Roundcube

http://www.iredmail.org/docs/debug.roundcubemail.html

DNS

https://mxtoolbox.com/diagnostic.aspx


configuration (mp4)

Resources

http://t-machine.org/index.php/2014/06/27/webmail-on-your-debian-server-exim4-dovecot-roundcube/
https://intodns.com/herrerosolis.com
Another option would be: https://avix.co/blog/creating-your-own-mail-server-amazon-ec2-postfix-dovecot-postgresql-amavis-spamassassin-apache-and-squirrelmail-part-1/

Only two things I might add: 1. In the file /etc/dovecot/conf.d/auth-sql-conf.ext uncomment driver and set it to mysql 2. /etc/dovecot/conf.d/10-mail.conf uncomment first_valid_uid and set it to [your_uid] (ie. 8). If you need to do the same for first_valid_gid

https://pasztor.at/blog/the-big-exim-tutorial

Adding new Domains

  1. Add the new domain to to MySQL email_accounts.domains
  2. Start creating accounts with the new domain id

Auxiliary Scripts

Create email account

#!/bin/bash
PASSWORD1="hola"
PASSWORD2="mundo"
DOMAIN="herrerosolis.com"
echo "******************************* MAIL ACCOUNT INFORMATION *****************************************"

read -p "Username (without@herrerosolis.com): " USERNAME

while [[ "$PASSWORD1" != "$PASSWORD2" ]]; do
    read -s -p "Password: " PASSWORD1
    echo
    read -s -p "Confirm password: " PASSWORD2
    if [ "$PASSWORD1" != "$PASSWORD2" ]; then
        echo "Passwords dont match, try again..."
    fi
done
echo
echo
echo "Input MySQL database password for user mail"

mysql -h 127.0.0.1 -u cartero -p -D email_accounts -e "INSERT INTO mailboxes VALUES(NULL,1,'$USERNAME',MD5('$PASSWORD1'),'Mail account for $USERNAME@${DOMAIN}',1,NOW(),NOW());"

Create email alias

#!/bin/bash

echo "******************************* MAIL ACCOUNT INFORMATION *****************************************"

read -p "Alias name (without _@redactate.com): " ALIAS

read -p "Destiny account (full email address): " GOTO

echo
echo
echo "Input MySQL database password for user mail"

mysql -h 127.0.0.1 -u cartero -p -D email_accounts -e "INSERT INTO aliases VALUES (null, 2, '$ALIAS', '$GOTO', 'Redirecting $ALIAS@ to $GOTO', 1, NOW(),NOW());"