Статья получилась довольно длинной, потому что почта состоит из многих компонентов, которые надо вместе настроить.
В первую очередь настроим DNS, чтобы оно успело обновиться, пока мы делаем все остальное.
Почтовый сервер для своей работы использует домен. Этот домен должен находиться на Cloudflare, чтобы была возможность настроить автоматическое продление Let's Encrypt сертификата по api токену Cloudflare.
Чтобы получить токен Cloudflare, нужно перейти по ссылке https://dash.cloudflare.com/profile/api-tokens. Там можно получить глобальный ключ Global API Key
или сделать отдельный токен с правами на редактирование конкретного домена.
После того, как api токен получен, нужно также получить id домена. Чтобы сделать это, нужно выполнить такой запрос:
# сюда подставьте свой токен
export TOKEN="h6N8z2L4HU4xvoJzKdPD8wTURW0SeSeWRLeI-NGk"
# сюда подставьте свой домен
export DOMAIN="example.com"
apt-get install jq -y
curl -X GET "https://api.cloudflare.com/client/v4/zones" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
| jq -r ".result[] | select(.name == \"$DOMAIN\") | .id"
Для последующих запросов сохраним полученный id домена в переменную среды такой командой:
# сюда подставьте свой id домена
export ZONE_ID="a2127914087be198d9eddc7a159d6d2c"
Почтовый сервер при отправке письма может подставить любой домен, включая чужой. Чтобы не допустить такую уязвимость, почтовые сервера проверяют у домена наличие специальной spf txt-записи, которая должна содержать айпи адреса хостов, с которых могут отправляться письма с этим доменом. В противном случае письма, отправленные с нашего почтового сервера, попадут в спам.
Сначала нужно создать a-запись mail
, которая будет указывать на айпи адрес хоста, на котором будет установлен почтовый сервер. Чтобы сделать это, нужно выполнить такие запросы:
# сюда подставьте свой айпи адрес хоста
export HOST_IP="11.22.33.44"
# удаляем предыдущую a-запись mail, если таковая была
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=A&name=mail.$DOMAIN" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
| jq -r '.result[].id' | xargs -i \
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/{}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
# создаем a-запись mail
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"A","name":"mail.'$DOMAIN'","content":"'$HOST_IP'","ttl":120}'
Теперь нужно создать spf txt-запись, которая будет указывать на созданную a-запись mail
.
Чтобы сделать это, нужно выполнить такой запрос:
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"TXT","name":"'$DOMAIN'","content":"v=spf1 a:mail.'$DOMAIN' -all","ttl":120}'
Чтобы почтовые сервера не отправляли нашу почту в спам, нужно добавить dmarc txt-запись. Эта запись хранит в себе политику, которой придерживается наш почтовый сервер. По сути эта запись сообщаем всем "мы почтовый сервер, мы можем отправлять письма, много писем, не баньте нас, все так и задумано".
Чтобы создать эту запись, нужно выполнить такой запрос:
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{"type":"TXT","name":"_dmarc.'$DOMAIN'","content":"v=DMARC1; p=none; aspf=r; sp=none","ttl":120}'
Подключаемся по ssh к хосту, куда будем устанавливать почтовый сервер.
Чтобы можно было отправлять письма через почтовый сервер удаленно, нужно установить stmp сервер dovecot
. Этот сервер выступает в качестве прокси между клиентами и почтовым сервером postfix
, а также он берет на себя роль аутентификации.
Устанавливаем его такими командами:
apt-get install dovecot-imapd dovecot-pop3d -y
sed -i 's/^auth_mechanisms.\+/auth_mechanisms = plain login/g' /etc/dovecot/conf.d/10-auth.conf
После установки нужно настроить конфиг, открываем его:
nano /etc/dovecot/conf.d/10-master.conf
Находим в конфиге секцию # Postfix smtp-auth
и приводим ее к такому виду:
# Postfix smtp-auth
unix_listener /var/spool/postfix/private/auth {
mode = 0660
user = postfix
group = postfix
}
Открываем еще один конфиг:
nano /etc/dovecot/conf.d/auth-system.conf.ext
Находим секцию passdb {
и приводим ее к виду:
passdb {
#driver = pam
driver = passwd-file
# [session=yes] [setcred=yes] [failure_show_msg=yes] [max_requests=<n>]
# [cache_key=<key>] [<service name>]
#args = dovecot
args = scheme=SHA1 /etc/dovecot/passwd
}
Юзер представляет из себя строку в файле /etc/dovecot/passwd
, которую можно создать такой командой:
doveadm pw -s sha1 | cut -d '}' -f2 | xargs -i echo "login@$DOMAIN:{}" > /etc/dovecot/passwd
# вводим пароль юзера
chown dovecot: /etc/dovecot/passwd
chmod 600 /etc/dovecot/passwd
Логином юзера будет [email protected]
, только вместо example.com
будет свой домен.
В linux может быть установлен почтовый сервер по умолчанию с установкой системы, его нужно удалить.
Чтобы проверить его наличие, можно проверить прослушивание порта 25 такой командой:
netstat -tnlp | grep ":25 "
В Debian скорее всего будет установлен exim
, удалить его можно такой командой:
apt-get remove -y exim*
postfix
- это почтовый сервер, он может отправлять письма. Устанавливаем его такими командами:
apt-get -y install bsd-mailx postfix
В процессе установки вам будут задавать вопросы:
- Вопрос
General type of mail configuration:
- надо выбратьInternet Site
. - Вопрос
System mail name:
- надо ввестиmail.example.com
, только вместоexample.com
укажите свой домен.
После установки редактируем конфиг такими командами:
sed -i "s/^myhostname.\+/myhostname = mail.$DOMAIN/g" /etc/postfix/main.cf
sed -i "s/^smtpd_banner = \$myhostname/smtpd_banner = mail.$DOMAIN/g" /etc/postfix/main.cf
sed -i "s/^mydestination.\+/mydestination = \$myhostname, $DOMAIN, localhost/g" /etc/postfix/main.cf
sed -i 's/^inet_protocols.\+/inet_protocols = ipv4/g' /etc/postfix/main.cf
После чего открываем этот конфиг редактором:
nano /etc/postfix/main.cf
И добавляем такие параметры после комментария # TLS parameters
:
# TLS parameters
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_type = dovecot
Если на хосте имеется несколько айпи адресов, тогда нужно добавить еще одну настройку, в которую надо указать тот адрес, который вы использовали при создании a-записи mail
:
smtp_bind_address = 11.22.33.44
Чтобы соотвествовать требованиям безопасности, и наши письма не попадали в спам, надо настроить шифрование TLS.
Для этого подходят бесплатные сертификаты Lets Encrypt. Чтобы сертификат не приходилось вручную пересоздавать каждые 3 месяца, установим certbot
, который будет это делать за нас с помощью Cloudflare api.
Команды для установки:
# install certbot and certbot-dns-cloudflare
apt install certbot python3-pip -y
pip3 install certbot-dns-cloudflare
# configure cloudflare
mkdir ~/.certbot
printf "# Cloudflare API token used by Certbot\ndns_cloudflare_api_token = $TOKEN" > ~/.certbot/cloudflare.ini
chmod 600 ~/.certbot/cloudflare.ini
certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials ~/.certbot/cloudflare.ini \
--dns-cloudflare-propagation-seconds 60 \
-d $DOMAIN
После последней команды certbot
будет задавать нам вопросы:
- Вопрос
Enter email address
- надо ввести почту администратора, именно вашу личную, чтобы получать уведомления от Lets Encrypt. - Вопрос
Please read the Terms of Service at...
- надо ввестиA
. - Вопрос
Would you be willing...
- надо ввестиN
.
Если все хорошо, сертификат будет сохранен в файлах:
/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem
Можно проверить работает ли обновление сертификата командой certbot renew --dry-run
. Если есть сертификат и нет красных ошибок - значит всё ок.
Чтобы сертификат пересоздавался автоматически, нужно добавить такую команду в crontab -e
:
0 3 * * * certbot renew
Редактируем конфиг postfix
такими командами:
sed -i "s/^smtpd_tls_cert_file.\+/smtpd_tls_cert_file=\/etc\/letsencrypt\/live\/$DOMAIN\/fullchain.pem/g" /etc/postfix/main.cf
sed -i "s/^smtpd_tls_key_file.\+/smtpd_tls_key_file=\/etc\/letsencrypt\/live\/$DOMAIN\/privkey.pem/g" /etc/postfix/main.cf
После чего открываем этот конфиг редактором:
nano /etc/postfix/main.cf
И добавляем такие параметры после комментария # TLS parameters
:
# TLS parameters
smtp_tls_security_level = may
smtp_tls_note_starttls_offer = yes
smtpd_tls_security_level = may
DKIM - это еще одна ступен безопасности, без которой письма будут попадать в спам.
Устанавливаем и настраиваем сервис opendkim
такими командами:
apt-get install opendkim opendkim-tools -y
mkdir -m 750 /etc/opendkim && chown opendkim:opendkim /etc/opendkim
opendkim-genkey -D /etc/opendkim --domain=$(postconf -h mydomain) --selector=dkim
chown opendkim:opendkim /etc/opendkim/dkim.private
chmod 600 /etc/opendkim/dkim.private
sed -i "s/^Socket.\+/Socket inet:8892@localhost/g" /etc/opendkim.conf
sed -i "s/^#SubDomains.\+/SubDomains Yes/g" /etc/opendkim.conf
sed -i "s/^#Canonicalization.\+/Canonicalization relaxed\/relaxed/g" /etc/opendkim.conf
echo "dkim._domainkey.$DOMAIN $DOMAIN:dkim:/etc/opendkim/dkim.private" > /etc/opendkim/KeyTable
echo "*@$DOMAIN dkim._domainkey.$DOMAIN" > /etc/opendkim/SigningTable
echo -e "127.0.0.1\nlocalhost\n*.$DOMAIN" > /etc/opendkim/TrustedHosts
# удаляем предыдущую txt-запись dkim._domainkey, если таковая была
curl -X GET "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records?type=TXT&name=dkim._domainkey.$DOMAIN" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
| jq -r '.result[].id' | xargs -i \
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/{}" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json"
# создаем txt-запись dkim._domainkey
curl -X POST "https://api.cloudflare.com/client/v4/zones/$ZONE_ID/dns_records/import" \
-H "Authorization: Bearer $TOKEN" \
--form 'file=@/etc/opendkim/dkim.txt' \
--form 'proxied=false'
После установки открываем конфиг редактором:
nano /etc/opendkim.conf
И добавляем такие параметры:
RemoveARAll Yes
RemoveOldSignatures Yes
SendReports Yes
KeyTable /etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
InternalHosts refile:/etc/opendkim/TrustedHosts
Открываем конфиг postfix
редактором:
nano /etc/postfix/main.cf
Добавляем эти параметры в конец файла после всех остальных параметров:
# DKIM
milter_default_action = accept
smtpd_milters = inet:localhost:8892
non_smtpd_milters = inet:localhost:8892
Перезагружаем сервисы командами:
systemctl restart dovecot
systemctl enable dovecot
systemctl restart opendkim
systemctl enable opendkim
systemctl restart postfix
systemctl enable postfix
Ждем ~10 минут, пока обновит DNS и Lets Encrypt.
Пробуем отправить письмо через linux команду на gmail:
echo "This is message body" | mailx -s "This is Subject" -r "no-reply@$DOMAIN" [email protected]
Вместо [email protected]
укажите вашу личную почту gmail.
Письмо не должно попасть в Spam и в нем должно быть так:
Далее нужно проверить оригинал письма, его можно открыть так:
В оригинале напротив 3 ступеней безопасности должно быть написано PASS
:
Если все так, значит все хорошо.
Вот пример java кода, который отправляет письмо через smtp:
import javax.mail.Authenticator;
import javax.mail.PasswordAuthentication;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import java.util.Properties;
public class Debug{
public static void main(String[] args) throws Exception{
Properties prop = new Properties();
AuthenticatorMail auth = new AuthenticatorMail("[email protected]", "password");
prop.put("mail.smtp.host", "mail.example.com"); //Хост
prop.put("mail.smtp.ssl.enable", "false"); // SSL используем ?
prop.put("mail.smtp.starttls.enable", "true"); // TLS используем ?
prop.put("mail.smtp.auth", "true"); // Авторизация нужна ли ? Во всех случаях нужна, если это не твой личный-приватный SMTP сервер.
prop.put("mail.smtp.port", 25); //Порт
prop.put("mail.debug", "false");
prop.put("mail.smtp.connectiontimeout", 30_000);
prop.put("mail.smtp.timeout", 30_000);
prop.put("mail.smtp.writetimeout", 30_000);
Session session = Session.getInstance(prop, auth); //Создаем сессию, с указаными настройками, а также логинимся
javax.mail.Message message = new MimeMessage(session); //Сессия уже создана. Определяем к какой сессии принадлежит сообщение.
message.setFrom(new InternetAddress("[email protected]")); //от кого идет сообщение.
message.setRecipients(javax.mail.Message.RecipientType.TO, InternetAddress.parse("[email protected]")); //Тут указываем кому идет сообщениею
message.setSubject("privet3"); //Заголовок
message.setText("privet4"); //текст
Transport.send(message);
}
public static class AuthenticatorMail extends Authenticator{
String login;
String password;
public AuthenticatorMail(String login, String password){
this.login = login;
this.password = password;
}
@Override
protected PasswordAuthentication getPasswordAuthentication(){
return new PasswordAuthentication(login, password);
}
}
}
Этот код для своей работы требует такую зависимость:
<dependency>
<groupId>com.sun.mail</groupId>
<artifactId>javax.mail</artifactId>
<version>1.5.6</version>
</dependency>