Once I was looking for an answer to the question of how to make a fully functional registration system for an email server. I found the answer and decided to assemble several guides found on the Internet into one working model for debian based systems.
The first thing you need to do is to add A and MX dns entries for mail server, preferably on the website where you subscribe to the server:
Add the TXT SPF record to the server dns settings:
v=spf1 a mx ip4:xx.xx.xx.xx include:google.com ~all
Sample MX entry for DNS server:
mx.example.com @ 10 86400
You must have the encfs program installed to start your safe adventure with the e-mail server:
apt install encfs
mkdir /encrypted-mail /decrypted-mail
chgrp mail /decrypted-mail/
chmod -R g+rw /decrypted-mail/
gpasswd -a mail fuse
chgrp fuse /dev/fuse; chmod g+rw /dev/fuse
encfs /encrypted-mail /decrypted-mail --public
You may need to install fuse and modpobe it.
After mounting the encrypted folder, install postfix, dovecot and mysql:
apt install install postfix postfix-mysql dovecot-core dovecot-imapd dovecot-mysql mysql-server dovecot-lmtpd
Create a database for the mail server:
mysql_secure_installation
mysql
create database mailserver;
use mailserver;
GRANT SELECT,INSERT,UPDATE ON mailserver.* TO 'mailuser'@'127.0.0.1' IDENTIFIED BY 'mailuserpass';
FLUSH PRIVILEGES;
Create database tables:
CREATE TABLE `domains` (
`id` int(11) NOT NULL auto_increment,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `users` (
`id` int(11) NOT NULL auto_increment,
`domain_id` int(11) NOT NULL,
`username` varchar(255) NOT NULL,
`email` varchar(100) NOT NULL,
`password` varchar(60) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `email` (`email`),
FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `aliases` (
`id` INT NOT NULL AUTO_INCREMENT,
`domain_id` INT NOT NULL,
`source` varchar(100) NOT NULL,
`destination` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
FOREIGN KEY (domain_id) REFERENCES domains(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `mailserver`.`domains`
(`id` ,`name`)
VALUES
('1', 'mx.example.com');
('2', 'example.com');
INSERT INTO `mailserver`.`aliases`
(`id`, `domain_id`, `source`, `destination`)
VALUES
('1', '1', 'webmaster@example.com', 'root');
('2', '1', 'www-data@example.com', 'root');
exit
Get ssl certificate for your domain:
git clone https://github.com/acmesh-official/acme.sh.git acme
cd acme && ./acme.sh --install
mkdir /etc/acme/live
acme.sh --issue --home /etc/acme/live -d mx.example
Move the default postfix configuration file to backup and create new one:
mv /etc/postfix/main.cf /etc/postfix/main.cf.orig
nano /etc/postfix/main.cf
smtpd_tls_cert_file=/etc/acme/live/mx.example/fullchain.cer
smtpd_tls_key_file=/etc/acme/live/mx.example/mx.example.key
smtpd_use_tls=yes
smtpd_tls_auth_only = yes
smtp_tls_security_level = may
smtp_tls_loglevel = 2
smtpd_tls_received_header = yes
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_recipient_restrictions =
permit_sasl_authenticated,
permit_mynetworks,
reject_unauth_destination
mydestination = localhost
virtual_transport = lmtp:unix:private/dovecot-lmtp
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps = mysql:/etc/postfix/mysql-virtual-alias-maps.cf
local_recipient_maps = $virtual_mailbox_maps
Create the files needed for the symbiosis of postfix with the mysql database:
nano /etc/postfix/mysql-virtual-mailbox-domains.cf
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM domains WHERE name='%s'
nano /etc/postfix/mysql-virtual-mailbox-maps.cf
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT 1 FROM users WHERE email='%s'
nano /etc/postfix/mysql-virtual-alias-maps.cf
user = mailuser
password = mailuserpass
hosts = 127.0.0.1
dbname = mailserver
query = SELECT destination FROM aliases WHERE source='%s'
systemctl restart postfix
Now that postfix is set up, it’s time to set up dovecot:
mv /etc/dovecot/dovecot.conf /etc/dovecot/dovecot.conf.orig
mv /etc/dovecot/conf.d/10-mail.conf /etc/dovecot/conf.d/10-mail.conf.orig
mv /etc/dovecot/conf.d/10-auth.conf /etc/dovecot/conf.d/10-auth.conf.orig
mv /etc/dovecot/dovecot-sql.conf.ext /etc/dovecot/dovecot-sql.conf.ext.orig
mv /etc/dovecot/conf.d/10-master.conf /etc/dovecot/conf.d/10-master.conf.orig
mv /etc/dovecot/conf.d/10-ssl.conf /etc/dovecot/conf.d/10-ssl.conf.orig
nano /etc/dovecot/dovecot.conf
protocols = imap
nano /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:/decrypted-mail/%d/%n
mail_privileged_group = mail
first_valid_uid = 0
nano /etc/dovecot/conf.d/10-auth.conf
disable_plaintext_auth = yes
auth_mechanisms = plain login
#INSERT a hashtag in front of the following import. This separates your mail server's login from UNIX logins.
#!include auth-system.conf.ext
#REMOVE the hashtag in front of the following import. This points it at mysql for authentication.
!include auth-sql.conf.ext
nano /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
driver = sql
args = /etc/dovecot/dovecot-sql.conf.ext
}
userdb {
driver = static
args = uid=mail gid=mail home=/decrypted-mail/%d/%n
nano /etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = host=localhost dbname=mailserver user=mailuser password=mailuserpass
default_pass_scheme = BLF-CRYPT
password_query = SELECT email as user, password FROM users WHERE email='%u';
Configure the permissions for the dovecot folder:
chown -R mail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot
Configure the 10-master.conf file:
nano /etc/dovecot/conf.d/10-master.conf
service imap-login {
inet_listener imap {
port = 0
}
service pop3-login {
inet_listener pop3 {
port = 0
}
service lmtp {
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0666
group = postfix
user = postfix
}
# Create inet listener only if you can't use the above UNIX socket
#inet_listener lmtp {
# Avoid making LMTP visible for the entire internet
#address =
#port =
#}
user=mail
}
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Its default
# permissions make it readable only by root, but you may need to relax these
# permissions. Users that have access to this socket are able to get a list
# of all usernames and get results of everyone's userdb lookups.
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0600
user = mail
#group =
}
# Postfix smtp-auth
#unix_listener /var/spool/postfix/private/auth {
# mode = 0666
#}
# Auth process is run as this user.
user = dovecot
}
service auth-worker {
# Auth worker process is run as root by default, so that it can access
# /etc/shadow. If this isn't necessary, the user should be changed to
# $default_internal_user.
user = mail
}
service auth {
# auth_socket_path points to this userdb socket by default. It's typically
# used by dovecot-lda, doveadm, possibly imap process, etc. Its default
# permissions make it readable only by root, but you may need to relax these
# permissions. Users that have access to this socket are able to get a list
# of all usernames and get results of everyone's userdb lookups.
unix_listener /var/spool/postfix/private/auth {
mode = 0666
user = postfix
group = postfix
}
unix_listener auth-userdb {
mode = 0600
user = mail
#group =
}
# Postfix smtp-auth
#unix_listener /var/spool/postfix/private/auth {
# mode = 0666
#}
# Auth process is run as this user.
user = dovecot
}
service auth-worker {
# Auth worker process is run as root by default, so that it can access
# /etc/shadow. If this isn't necessary, the user should be changed to
# $default_internal_user.
user = mail
}
And SSL configuration file:
nano /etc/dovecot/conf.d/10-ssl.conf
ssl_cert = </etc/acme/live/mx.example/fullchain.cer
ssl_key = </etc/acme/live/mx.example/mx.example.key
ssl = required
It is good to have tools such as opendmarc, opendkim and spf configured as well. Properly configured programs prevent messages from our server from landing into the spam folder on gmail, for example.
apt install opendkim opendkim-tool
mkdir -pv /etc/opendkim/
chown -Rv opendkim:opendkim /etc/opendkim
chmod go-rwx /etc/opendkim/*
cd /etc/opendkim/
opendkim-genkey -r -h rsa-sha256 -d mx.example -s mail
mv -v mail.private mail
cat mail.txt
nano /etc/opendkim/KeyTable
mx.example mx.example:mail:/etc/opendkim/mail
nano /etc/opendkim/SigningTable
*@mx.example mx.example
nano /etc/opendkim/TrustedHosts
127.0.0.1
nano /etc/opendkim.conf
##
## opendkim.conf -- configuration file for OpenDKIM filter
##
Canonicalization relaxed/relaxed
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
KeyTable refile:/etc/opendkim/KeyTable
LogWhy Yes
MinimumKeyBits 1024
Mode sv
PidFile /var/run/opendkim/opendkim.pid
SigningTable refile:/etc/opendkim/SigningTable
Socket inet:8891@localhost
Syslog Yes
SyslogSuccess Yes
TemporaryDirectory /var/tmp
UMask 022
UserID opendkim:opendkim
Add DNS record for DKIM:
HOST: mail._domainkey.mx.example.com
TXT: v=DKIM1; h=rsa-sha256; k=rsa; s=email; p=MIGfMAEGCSqGSIc3DQEBAQUAA4GNADCBiQKBgQCdQB1jKvQl6U9m3QEd1QJcV 1zERGt7fgdSD9PmxtcqySEJ1wTVha/jVeDfklbAakfALCzJ0XZxXEDCLGCmVRM8r2OTuCcwGyiZNsa2dqO4yu2ZNhPtehmvCn9X7EeHyTSNhUpsmP2zVsLydhbGHlYRNFij0VmWQAhYzDwkFXMfwIDRQAB
TTL: 7200
Get “p=” value from generated mail.txt file.
apt install opendmarc
chown -Rv opendmarc:opendmarc /etc/opendmarc
chmod go-rwx /etc/opendmarc/*
nano /etc/opendmarc.conf
AuthservID mx.example
PidFile /var/run/opendmarc/opendmarc.pid
RejectFailures false
Syslog true
SyslogFacility mail
TrustedAuthservIDs mx.example
IgnoreHosts /etc/opendmarc/ignore.hosts
UMask 0002
UserID opendmarc:opendmarc
Socket inet:8892@127.0.0.1
FailureReportsSentBy dmarc@mx.example.com
FailureReportsBcc dmarc@mx.example.com
FailureReports false
AutoRestart true
HistoryFile /var/log/opendmarc.log
Add DNS record for DMARC:
HOST: _dmarc.mx.example.com
TXT: v=DMARC1; p=none; pct=100; rua=mailto:dmarc@mx.example.com
TTL: 7200
Add these lines to postfix main.cf file:
nano /etc/postfix/main.cf
smtpd_milters = inet:127.0.0.1:8891, inet:127.0.0.1:8892
non_smtpd_milters = $smtpd_milters
milter_default_action = accept
Then download the registration form files, edit for your needs and upload to your web server.
In the server.php file you should change the values for mailuser, mailuserpass and the name of the database if you used a different one(done earlier when configuring the mysql server). Everything else is up to your own ideas of what your registration page should look like.
Don’t forget to give the appropriate permissions to the downloaded files:
find /path/to/your/www -type d -exec chmod 755 {} +
find /path/to/your/www -type f -exec chmod 644 {} +
chown -R www-data.www-data /path/to/your/www
Go to example.com/register.php and try register some test account. You may notice that passwords are hashed and stored in the database in the blowfish format with 12 cost option, so compared to the encrypted disk space it gives a pretty good result when it comes to data security of such a server. Encfs is just one example of disk space encryption. Veracrypt or dd with luks encryption can also be used for this purpose.
You can see what it looks like in practice at https://sunless.cloud/account or maybe you want to create an email account at sunless.cloud? You will receive a 100 MB mailbox in encrypted storage space but accessible via webmail only from the TOR network. The second option is to configure an email client such as Thunderbird. Feel free to use it but please don’t use your email account to send spam.
The tutorials that I used to write this tube can be found at:
https://sealedabstract.com/code/nsa-proof-your-e-mail-in-2-hours/index.html
https://codewithawa.com/posts/complete-user-registration-system-using-php-and-mysql-database