Перадача паведамленняў Python праз SMTP: STARTTLS на 587 ці TLS на 465?
STARTTLS на 587 і неявны TLS на 465 абодва пераносяць адпраўку Python, але толькі адзін адпавядае вашаму рэле. Параўнайце рукапоціск і рэжымы збояў.
Абодва рэжымы злучэння нясуць тое ж паведамленне на той жа рахунак рэле, і розніца ў тым, дзе пачынаецца TLS, а не ў тым, што адпраўляецца. Адзін адкрываецца ў адкрытым тэксце і паляпшаецца ўнутры канала; другі шыфруецца з першага байта. Гэтая адзіная розніца вызначае, што прыме строгая палітыка рэле, што зможа заўважыць прамежкавы прыбор і якое паведамленне пра памылку вы будзеце чытаць усю пасляабедзенную пару. Вось як я выбіраю паміж імі, калі праграма ў прыватнай сетцы перадае паведамленне ўнутранай службе адпраўкі.
Дзе насамрэч разыходзяцца два рэжымы
Задача аднолькавая ў абодвух выпадках: аўтэнтыфікавацца як ухвалены рахунак, назваць дазволенага адпраўніка, пералічыць атрымальнікаў і дачакацца, пакуль рэле возьме на сябе адказнасць. Змяняецца парадак дзеянняў. Порт 587 пачынае як SMTP-сесія ў адкрытым тэксце, абвяшчае STARTTLS у адказе EHLO і паляпшаецца, толькі калі кліент папросіць. Порт 465 чакае рукапоціск TLS перш чым прамовіць любую каманду SMTP.
Адпраўка і рэле — розныя задачы, таму нумары партоў не ўзаемазаменныя звычкі. RFC 6409 апісвае адпраўку паведамленняў як асобную службу, а RFC 8314 ахоплівае варыянт неявнага TLS, які большасць сучасных рэле прадстаўляе на 465. Калі ваша рэле дакументуе адзін з двух, гэтая дакументацыя важнейшая за любую перавагу, якую вы набылі ў папярэдняга працадаўцы.
Што дае STARTTLS на 587
Паляпшэнне адбываецца ўнутры размовы SMTP, таму рэле можа абвясціць яго, а кліент можа адмовіцца працягваць без яго. У Python starttls() уздымае выключэнне, калі сервер не прапануе пашырэнне, што ператварае спробу паніжэння ў бачны збой замест ціхага ўваходу ў адкрытым тэксце. Гэтыя паводзіны — галоўны аргумент за рэжым, і на іх варта спадзявацца свядома, а не выпадкова.
Акно ўздзеяння невялікае, але рэальнае: пачатковы абмен EHLO чытальны, і да паляпшэння нічога не нясе ўліковых даных. На прыватным шляху паміж падсеткай праграмы і ўнутраным рэле гэтае акно звычайна прымальнае. На шляху, які вы не кантралюеце, менавіта гэта прымушае некаторыя каманды настойваць на іншым рэжыме.
Прылада перахопу, якая выдаляе магчымасць STARTTLS, дае няўдалую адпраўку, а не адкат. Гэта менавіта той вынік, які вам патрэбны, і гэта самы хуткі спосаб выявіць, што нешта перапісвае SMTP на шляху.
Што змяняе неявны TLS на 465
Фазы адкрытага тэксту няма зусім. Кліент выконвае рукапоціск TLS, як толькі адкрываецца сокет, правярае сертыфікат супраць свайго сховішча даверу і толькі потым абменьваецца EHLO, AUTH і паведамленнем. Нішто не можа панізіць сесію, бо няма стану адкрытага тэксту, з якога можна было б дамаўляцца, і гэта прыбірае цэлы клас збояў перахопу.
Гандаль — гэта дыягнастуемасць. Рэле, якое адказвае на 465 адкрытым SMTP, ці балансіроўшчык нагрузкі, які завяршае TLS з няправільным імем сертыфіката, збоіць на рукапоціску і дае вам памылку SSL замест памылкі пошты. Калі гэта здараецца, праблема ніжэй за праграму, і ніякая колькасць пабудовы паведамленняў яе не выправіць.
Адна функцыя адпраўкі, два транспарты
Код амаль не адрозніваецца паміж рэжымамі, таму выбар варта рабіць з палітыкі рэле, а не зручнасці бібліятэкі. Спачатку экспартуйце налады, затым запусціце адзіны скрыпт, які выбірае транспарт паводле наладжанага порта.
export SMTP_HOST=smtp.relay.internal export SMTP_PORT=587 export SMTP_USER=svc-notify export [email protected] export [email protected] export SMTP_PASSWORD="$(cat /run/secrets/smtp_password)"
import os, smtplib, ssl
from email.message import EmailMessage
ctx = ssl.create_default_context()
msg = EmailMessage()
msg["From"] = os.environ["MAIL_FROM"]
msg["To"] = os.environ["MAIL_TEST_TO"]
msg["Subject"] = "relay submission test"
msg.set_content("One authorized test message from the app.")
host = os.environ["SMTP_HOST"]
port = int(os.environ.get("SMTP_PORT", "587"))
if port == 465:
client = smtplib.SMTP_SSL(host, port, context=ctx, timeout=15)
else:
client = smtplib.SMTP(host, port, timeout=15)
client.ehlo()
client.starttls(context=ctx)
client.ehlo()
with client:
client.login(os.environ["SMTP_USER"], os.environ["SMTP_PASSWORD"])
refused = client.send_message(msg)
print("refused:", refused)
create_default_context() правярае ланцуг і імя хоста, таму прыватны CA трэба ўсталяваць у сховішча даверу, а не абыходзіць неправераным кантэкстам. send_message() вяртае слоўнік атрымальнікаў, ад якіх рэле адмовілася; пусты слоўнік азначае, што яно прыняло ўсіх. Пятнаццацісекундны тайм-аўт — гэта адпраўная кропка, а не наладжанае значэнне для вашай сеткі.
Трымайце пароль па-за вобразам і па-за журналамі. Сакрэт, умантаваны падчас выканання і прачытаны ў асяроддзе, — гэта добра; адлюстраванне асяроддзя ў радку журнала — не, як і адладачны след абмену AUTH.
Выбірайце па палітыцы рэле і шляху, а не па звычцы
Тры ўмовы вырашаюць справу. Першая — што рэле фактычна абвяшчае: служба, якая слухае толькі 587 з STARTTLS, недасяжная на 465, як бы вы ні аддавалі перавагу неявнаму TLS. Другая — ці шлях паміж праграмай і рэле дастаткова давераны, каб прыняць чытальны абмен EHLO. Трэцяя — ці нешта ў сярэдзіне перапісвае або праксіруе SMTP, бо гэта вырашае, ці рызыка паніжэння тэарэтычная ці рэальная.
Калі рэле прапануе абодва, я бяру 465 для службы, якая працуе без нагляду, і 587 для той, дзе чалавек сочыць за першым разгортваннем, проста таму што паведамленні пра збоі паказваюць на розныя ўзроўні. Гэтая перавага мая, і гэта не палітыка.
Што б вы ні абралі, запішыце порт, матэрыял даверу і дамен адпраўніка разам з разгортваннем. Затым адпраўце адно дазволенае тэставае паведамленне і пацвердзіце, што рэле яго прыняло, захоўваючы вынік адпраўкі і любы пазнейшы звестак пра дастаўку як асобныя запісы. Паспяховая перадача рэле — гэта канец адказнасці праграмы, а не доказ таго, што паведамленне дасягнула скрыні.