Ambaŭ konektmanieroj portas la saman mesaĝon al la sama relaja konto, kaj la diferenco estas kie TLS komenciĝas, ne kion oni sendas. Unu malfermiĝas en klarteksto kaj plibonigas ene de la kanalo; la alia estas ĉifrita ekde la unua bajto. Tiu unu diferenco decidas, kion akceptos strikta relaja politiko, kion povas observi meza aparato, kaj kiun erarmesaĝon vi pasigos posttagmezon legante. Jen kiel mi elektas inter ili, kiam aplikaĵo en privata reto transdonas mesaĝon al interna sendoservo.

Kie la du manieroj vere disiĝas

La tasko estas identa en ambaŭ kazoj: aŭtentigi kiel aprobita konto, nomi permesitan sendanton, listigi ricevantojn, kaj atendi ke la relajilo prenu respondecon. Kio ŝanĝiĝas estas la ordo de operacioj. Haveno 587 komenciĝas kiel SMTP-sesio en klarteksto, anoncas STARTTLS en sia EHLO-respondo, kaj plibonigas nur se la kliento petas. Haveno 465 atendas TLS-manpremon antaŭ ol iu ajn SMTP-komando estas dirita.

Sendado kaj relajado estas malsamaj taskoj, tial la havenaj numeroj ne estas interŝanĝeblaj kutimoj. RFC 6409 priskribas mesaĝan sendadon kiel propran servon, kaj RFC 8314 kovras la varianton de implica TLS, kiun plej multaj modernaj relajiloj elmetas ĉe 465. Se via relajilo dokumentas unu el la du, tiu dokumentaro superas ajnan preferon, kiun vi akiris ĉe antaŭa dunganto.

Kion STARTTLS ĉe 587 donas al vi

La plibonigo okazas ene de la SMTP-konversacio, do la relajilo povas anonci ĝin kaj la kliento povas rifuzi daŭrigi sen ĝi. En Python, starttls() ĵetas escepton kiam la servilo ne proponas la etendaĵon, kio transformas provon de malaltigo en videblan fiaskon anstataŭ silentan klartekstan ensaluton. Tiu konduto estas la ĉefa argumento por la maniero, kaj indas fidi ĝin intence, ne hazarde.

La ekspozicia fenestro estas malgranda sed reala: la komenca EHLO-interŝanĝo estas legebla, kaj nenio antaŭ la plibonigo portas akreditaĵojn. Sur privata vojo inter aplikaĵa subreto kaj interna relajilo, tiu fenestro kutime akcepteblas. Sur vojo, kiun vi ne kontrolas, ĝi estas la kialo, kial kelkaj teamoj insistas pri la alia maniero.

Intercepta aparato, kiu forigas la STARTTLS-kapablon, produktas fiaskintan sendadon, ne retropaŝon. Tio estas la rezulto, kiun vi volas, kaj ĝi ankaŭ estas la plej rapida maniero malkovri, ke io reverkas SMTP sur la vojo.

Kion implica TLS ĉe 465 ŝanĝas

Entute ne ekzistas klarteksta fazo. La kliento faras TLS-manpremon tuj kiam la ingo malfermiĝas, validigas la atestilon kontraŭ sia fida konservejo, kaj nur poste interŝanĝas EHLO, AUTH kaj la mesaĝon. Nenio povas malaltigi la sesion ĉar ne ekzistas klarteksta stato por intertrakti, kaj tio forigas tutan klason de interceptaj fiaskoj.

La kompromiso estas diagnozebleco. Relajilo, kiu respondas ĉe 465 per klarteksta SMTP, aŭ ŝarĝbalancilo, kiu finiĝas TLS per malĝusta atestila nomo, fiaskas ĉe la manpremo kaj donas al vi SSL-eraron anstataŭ retpoŝtan eraron. Kiam tio okazas, la problemo estas sub la aplikaĵo, kaj neniu kvanto da mesaĝkonstruado ĝin riparos.

Unu senda funkcio, du transportoj

La kodo apenaŭ diferencas inter la manieroj, tial la elekto devus esti farita laŭ relaja politiko, ne laŭ biblioteka oportuneco. Unue eksportu la agordojn, poste rulu unu skripton, kiu elektas la transporton laŭ la agordita haveno.

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() kontrolas la ĉenon kaj la gastigan nomon, do privata CA devas esti instalita en la fida konservejo anstataŭ esti preterpasita per nekontrolita kunteksto. send_message() redonas vortaron de ricevantoj, kiujn la relajilo rifuzis; malplena vortaro signifas, ke ĝi akceptis ĉiujn. La dek-kvin-sekunda tempolimo estas deirpunkto, ne agordita valoro por via reto.

Tenu la pasvorton ekster la bildo kaj ekster la protokoloj. Sekreto muntita dum rultempo kaj legita en la medion estas en ordo; eĥi la medion en protokolan linion ne estas, kaj nek estas sencimiga spuro de la AUTH-interŝanĝo.

Elektu laŭ relaja politiko kaj vojo, ne laŭ kutimo

Tri kondiĉoj decidas ĝin. Unue, kion la relajilo efektive anoncas: servo, kiu aŭskultas nur ĉe 587 kun STARTTLS, ne estas atingebla ĉe 465, kiom ajn vi preferas implican TLS. Due, ĉu la vojo inter la aplikaĵo kaj la relajilo estas sufiĉe fidinda por akcepti legeblan EHLO-interŝanĝon. Trie, ĉu io meze reverkas aŭ prokuras SMTP, ĉar tio decidas ĉu la malaltiga risko estas teoria aŭ viva.

Kiam la relajilo proponas ambaŭ, mi prenas 465 por servo, kiu funkcias sen prizorgo, kaj 587 por tiu, kie homo observas la unuan utiligon, nur ĉar la fiaskmesaĝoj montras al malsamaj tavoloj. Tiu prefero estas mia kaj ĝi ne estas politiko.

Kion ajn vi elektos, registru la havenon, la fidan materialon kaj la sendantan domajnon kune kun la utiligo. Poste sendu unu rajtigitan testan mesaĝon kaj konfirmu, ke la relajilo akceptis ĝin, konservante la sendorezulton kaj ĉiun postan liveraporan kiel apartajn rikordojn. Sukcesa transdono al la relajilo estas la fino de la respondeco de la aplikaĵo, ne pruvo, ke la mesaĝo atingis enirkeston.

Rilataj artikoloj