Каждый раз при запуске проекта в продакшн, встает вопрос, как отправлять письма с боевого сервера. Есть множество удобных сервисов, таких как mailgun или mailjet, можно отправлять письма со своего домена через smtp
Яндекса. Но иногда нужно организовать свой почтовый сервер и рассылать письма через него.
На этапе разработки проекта в качестве почтового сервера мы используем DebugMail сервис, который позволяет без настройки своего smtp
сервера отправлять тестовые письма.
Письма, письма лично на почту ношу…
Установим свой почтовый сервер, будем использовать простой и удобный PostFix.
$ sudo apt-get install postfix
В интерактивном режиме указываем тип и домен нашего сервера.
Для дальнейшей настройки скопируем базовый конфигурационный файл для debian
. И добавим доступ только из локального хоста.
$ sudo cp /usr/share/postfix/main.cf.debian /etc/postfix/main.cf
$ echo "
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
mydestination = localhost
" | sudo tee -a /etc/postfix/main.cf
$ sudo service postfix reload
По умолчанию у postfix
открыт 25 порт из внешнего мира, закроем его, отредактировав /etc/postfix/master.cf
.
--- /etc/postfix/master.cf
+++ /etc/postfix/master.cf
@@ -10,7 +10,7 @@
# (yes) (yes) (yes) (never) (100)
# ==========================================================================
# smtp inet n - - - - smtpd
-smtp inet n - n - - smtpd
+127.0.0.1:smtp inet n - n - - smtpd
#smtp inet n - - - 1 postscreen
#smtpd pass - - - - - smtpd
#dnsblog unix - - - - 0 dnsblog
Теперь можем попробовать отправить свое первое письмо с собственного сервера. Воспользуемся для этого стандартной библиотекой python
SMTPLib.
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
text_part = MIMEText('There are test mail text content', 'plain')
html_part = MIMEText('''
<html>
<head></head>
<body>
<h1>Hello from Earth<h1>
<p>There are test mail html content.</p>
<p style="color:red">have a good day ;)</p>
</body>
</html>
''', 'html')
from_, to_ = 'from@some.com', 'to@other.org'
msg = MIMEMultipart('alternative')
msg['Subject'] = 'Hello from Earth'
msg['From'] = from_
msg['To'] = to_
msg.attach(text_part)
msg.attach(html_part)
host = 'localhost'
conn = smtplib.SMTP(host)
conn.sendmail(from_, [to_], msg.as_string())
conn.quit()
Письмо успешно отправляется и обязательно попадает в спам, т.к. выглядит очень подозрительно и отправлено с неподтвержденного адреса.
Подключим tls
шифрование писем, добавив следующие строки в /etc/postfix/main.cf
smtpd_tls_security_level = may
smtp_tls_security_level = may
smtp_tls_loglevel = 1
smtpd_tls_loglevel = 1
smtpd_tls_CAfile = /etc/letsencrypt/live/example.com/chain.pem
smtpd_tls_cert_file = /etc/letsencrypt/live/example.com/cert.pem
smtpd_tls_key_file = /etc/letsencrypt/live/example.com/privkey.pem
smtp_tls_session_cache_database = btree:/var/lib/postfix/smtp_scache
smtp_tls_mandatory_exclude_ciphers = aNULL, MD5 , DES, ADH, RC4, PSD, SRP, 3DES, eNULL
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols=!SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_ciphers=high
tls_high_cipherlist=EDH+CAMELLIA:EDH+aRSA:EECDH+aRSA+AESGCM:EECDH+aRSA+SHA384:EECDH+aRSA+SHA256:EECDH:+CAMELLIA256:+AES256:+CAMELLIA128:+AES128:+SSLv3:!aNULL:!eNULL:!LOW:!3DES:!MD5:!EXP:!PSK:!DSS:!RC4:!SEED:!ECDSA:CAMELLIA256-SHA:AES256-SHA:CAMELLIA128-SHA:AES128-SHA
tls_random_source = dev:/dev/urandom
Здесь нам понадобятся сертификаты, можно подписать их самостоятельно, но проще и надежнее использовать бесплатные сертификаты от Let’s Encrypt. Подробнее о том как их получать в статье Давайте шифровать.
После настройки перезапускаем postfix
и проверяем доступность tls
.
$ openssl s_client -starttls smtp -showcerts -connect localhost:25
CONNECTED(00000003)
depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3
verify error:num=20:unable to get local issuer certificate
---
Certificate chain
0 s:/CN=example.com
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
-----BEGIN CERTIFICATE-----
<cert content>
-----END CERTIFICATE-----
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
-----BEGIN CERTIFICATE-----
<cert content>
-----END CERTIFICATE-----
---
Server certificate
subject=/CN=example.com
issuer=/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 3884 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 4096 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
Protocol : TLSv1.2
Cipher : ECDHE-RSA-AES256-GCM-SHA384
Session-ID: 9C05050D143CE1474438AEC0A57BD8303953053608ECD5775B565A26455EDADB
Session-ID-ctx:
Master-Key: D6A09F3AC016E489542EAA11E9EFF7B56118D24F849FD480B26E219322E8D97D43DE7C537E9B928A67DDCAD3F9397EC6
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 7200 (seconds)
TLS session ticket:
0000 - cd a5 c1 43 13 c9 2c 4b-7e 14 f6 53 92 86 89 29 ...C..,K~..S...)
0010 - 88 dc ae cc d4 61 a1 4c-ec 05 b1 61 94 0c b1 6c .....a.L...a...l
0020 - 13 57 84 63 0f e4 6a d7-da 08 45 7e 80 3e fd d7 .W.c..j...E~.>..
0030 - 16 82 70 d5 e4 8a bd ba-6f 9d b9 6f d9 49 56 b7 ..p.....o..o.IV.
0040 - d4 41 5a c7 27 53 82 b5-8d 5d 22 08 38 3c a5 59 .AZ.'S...]".8<.Y
0050 - 6b 06 e7 5f ba 36 2f 91-44 85 7a 5d 1c e4 da d9 k.._.6/.D.z]....
0060 - 90 78 64 10 63 6a df c9-79 8d d9 10 66 dc 24 74 .xd.cj..y...f.$t
0070 - fb 5a f6 f5 02 14 c5 d5-b0 b3 65 57 24 01 f2 bd .Z........eW$...
0080 - 06 31 f9 a9 e3 32 42 ad-f0 3b b5 3d 39 77 2c 95 .1...2B..;.=9w,.
0090 - 8f fd e9 4f c9 4c 1d 77-8e 23 e4 ca 48 e8 9f ed ...O.L.w.#..H...
Start Time: 1489229346
Timeout : 300 (sec)
Verify return code: 20 (unable to get local issuer certificate)
---
250 DSN
QUIT
DONE
Подписываюсь под каждым словом…
На сегодняшний день, считается обязательной подпись электронных писем с помощью DKIM. Это делается для того, чтобы почтовый сервер получателя мог удостоверится в том, что почта отправлена именно с сервера указанного в поле From
.
Установка и настройка OpenDKIM
Устанавливаем необходимые пакеты, OpenDKIM.
$ sudo apt-get install opendkim opendkim-tools
Генерируем ключи и сохраняем их доступными для чтения группе opendkim
, а также добавляем в эту группу postfix
.
$ sudo mkdir /etc/opendkim
$ sudo opendkim-genkey -D /etc/opendkim -d $(hostname -d) -s $(hostname)
$ sudo chgrp opendkim /etc/opendkim/*
$ sudo chmod g+r /etc/opendkim/*
$ sudo gpasswd -a postfix opendkim
Теперь указываем opendkim
где находятся ключи. Для этого дописываем в конфигурационный файл /etc/opendkim.conf
следующие строки.
Canonicalization relaxed/relaxed
SyslogSuccess yes
RequireSafeKeys false
KeyTable file:/etc/opendkim/keytable
SigningTable file:/etc/opendkim/signingtable
X-Header yes
Подробнее ознакомится с возможными параметрами можно в документации.
Теперь заполним таблицы ключей в файлах /etc/opendkim/keytable
и /etc/opendkim/signingtable
. Они указывают соответсвие между доменом и ключем, которым необходимо подписывать письмо.
# /etc/opendkim/keytable
ключ домен:селектор:/путь/до/ключа
# /etc/opendkim/signingtable
домен ключ
Например:
# /etc/opendkim/keytable
mail._domainkey.example.com example.com:mail:/etc/opendkim/mail.private
# /etc/opendkim/signingtable
example.com mail._domainkey.example.com
Настройка Postfix для работы с OpenDKIM
Указываем о необходимости подписывать все письма с помощью dkim
.
$ sudo postconf -e milter_default_action=accept
$ sudo postconf -e milter_protocol=2
$ sudo postconf -e smtpd_milters=unix:/var/run/opendkim/opendkim.sock
$ sudo postconf -e non_smtpd_milters=unix:/var/run/opendkim/opendkim.sock
Перезапускаем службы и отправляем подписанное письмо.
$ sudo service postfix restart
$ sudo service opendkim restart
Настроийки доменной зоны
Чтобы сервер мог удостовериться в корректности подписи, нужно добавить TXT
запись содежащую ключ. Сделать это нужно в контрольной панели регистратора. Проверим, что dns
зоны обновились.
$ dig +short TXT mail._domainkey.example.com
"v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvfTJ37Gqs06fhG0YYj/6HbojCrDp
F8X6u20YUaOax+jrvO0KtItfWYUi6hkCJeKbGTAOmqhWLu1T/DMt0XaICAJ7Q8525Z4ghwfvc5LgYyNSDEODeF
LNPlXgn3IP5o6Og2We/SnO4QCv8drKGf0N2xm5IIzIT8CjsbM6gPQIHTQIDAQAB"
Также хорошо указать разрешенные ip
адреса для исходящих писем в запись spf
.
$ dig +short TXT example.com
"v=spf1 a:example.com ip4:<ip v4 addr> ip6:<ip v6 addr> ~all"
Все не как у людей
Сегодня мы живем в далеком и почти светлом будущем, когда по бескрайним просторам сети широко распространяется ipv6
адресация. Но, оказывается, абсолютно все письма отправленные с ipv6
всегда воспринимаются гуглом как спам. Даже пройдя верификацию по dkim
и spf
записям, всеравно уходят в нежелательную почту.
Так что укажем в конфигурации postfix
отправку только с использованием ipv4
.
$ sudo postconf -e inet_protocols=ipv4
Теперь письма успешно доставляются и проходят все валидации.
Пример письма
Delivered-To: samael500@gmail.com
Received: by 10.182.174.67 with SMTP id bq3csp1452226obc;
Tue, 28 Feb 2017 08:09:52 -0800 (PST)
X-Received: by 10.46.22.18 with SMTP id w18mr1151641ljd.86.1488298192253;
Tue, 28 Feb 2017 08:09:52 -0800 (PST)
Return-Path: <info@*********>
Received: from ********* (*********. [**.**.**.**])
by mx.google.com with ESMTPS id x14si1225569lfd.155.2017.02.28.08.09.51
for <samael500@gmail.com>
(version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128);
Tue, 28 Feb 2017 08:09:52 -0800 (PST)
Received-SPF: pass (google.com: domain of info@********* designates **.**.**.** as permitted sender) client-ip=**.**.**.**;
Authentication-Results: mx.google.com;
dkim=pass header.i=*********;
spf=pass (google.com: domain of info@********* designates **.**.**.** as permitted sender) smtp.mailfrom=info@*********
Received: from ********* (localhost [127.0.0.1])
by ********* (Postfix) with ESMTP id D9ED0452AB
for <samael500@gmail.com>; Tue, 28 Feb 2017 16:09:53 +0000 (UTC)
DKIM-Filter: OpenDKIM Filter v2.9.2 ********* D9ED0452AB
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=*********;
s=mail; t=1488298193;
bh=juRoCRHzIAJJ4fKO8VlXEyxNddxTS8ftBnWmLxjdAik=;
h=Subject:From:To:Date:From;
b=G0Z6uXOV0LQHscdUOwMg5rjuJA/KWZ7x6Iqx3Z2x01nZ2kD+E1OgyP4zEfqS9XDiS
fG04P0qpIJyGEmO8hgRDIlH1d5FIDGjGPMAFDynwZ9j7pG1h88yLHThdtesUN7Fjib
2yL1xxiyw2dZbtfvgXwhj0Nb9RXpphrY+c9v2fW4=
Content-Type: multipart/alternative;
boundary="===============3211535685628593130=="
MIME-Version: 1.0
Subject: Hello from Earth
From: info@*********
To: samael500@gmail.com
Message-Id: <20170228160953.D9ED0452AB*********>
Date: Tue, 28 Feb 2017 16:09:53 +0000 (UTC)
--===============3211535685628593130==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
There are test mail text content
--===============3211535685628593130==
Content-Type: text/html; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
<html>
<head></head>
<body>
<h1>Hello from Earth<h1>
<p>There are test mail html content.</p>
<p style="color:red">have a good day ;)</p>
</body>
</html>
--===============3211535685628593130==--
Django settings
Теперь можно подключить отправку писем через наш smtp
в джанго.
EMAIL_HOST = 'localhost'
EMAIL_PORT = 25
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
DEFAULT_FROM_EMAIL = 'info@example.com'