Submissió SMTP amb Python: STARTTLS al 587 o TLS al 465?
STARTTLS al 587 i TLS implícit al 465 tots dos porten una submissió de Python, però només un encaixa amb el vostre relé. Compareu l’handshake i les formes de fallada.
Els dos modes de connexió porten el mateix missatge al mateix compte de relé, i la diferència és on comença el TLS, no què s’envia. Un s’obre en text pla i s’actualitza dins la banda; l’altre està xifrat des del primer byte. Aquesta única diferència decideix què acceptarà una política de relé estricta, què pot observar un equip intermedi i quin missatge d’error passareu una tarda llegint. Així és com trio entre ells quan una aplicació en una xarxa privada lliura un missatge a un servei de submissió intern.
On divergeixen realment els dos modes
La tasca és idèntica en tots dos casos: autenticar-se com a compte aprovat, indicar un remitent permès, llistar els destinataris i esperar que el relé assumeixi la responsabilitat. El que canvia és l’ordre de les operacions. El port 587 comença com una sessió SMTP en text pla, anuncia STARTTLS a la resposta EHLO i només s’actualitza si el client ho demana. El port 465 espera un handshake TLS abans que es digui cap ordre SMTP.
La submissió i el relé són feines diferents, i per això els números de port no són hàbits intercanviables. RFC 6409 descriu la submissió de missatges com un servei propi, i RFC 8314 cobreix la variant de TLS implícit que la majoria de relés moderns exposen al 465. Si el vostre relé documenta un dels dos, aquesta documentació té més pes que qualsevol preferència que hàgiu adquirit en un empleador anterior.
Què us dona STARTTLS al 587
L’actualització passa dins la conversa SMTP, de manera que el relé pot anunciar-la i el client pot negar-se a continuar sense ella. A Python, starttls() llança una excepció quan el servidor no ofereix l’extensió, cosa que converteix un intent de degradació en una fallada visible en lloc d’un inici de sessió silenciós en text pla. Aquest comportament és l’argument principal del mode, i val la pena dependre’n deliberadament i no per accident.
La finestra d’exposició és petita però real: l’intercanvi EHLO inicial és llegible, i res abans de l’actualització no porta credencials. En un camí privat entre una subxarxa d’aplicacions i un relé intern, aquesta finestra sol ser acceptable. En un camí que no controleu, és la raó per la qual alguns equips insisteixen en l’altre mode.
Un dispositiu d’intercepció que elimina la capacitat STARTTLS produeix una submissió fallida, no un retrocés. Aquest és el resultat que voleu, i també és la manera més ràpida de descobrir que alguna cosa reescriu SMTP al camí.
Què canvia el TLS implícit al 465
No hi ha cap fase de text pla. El client fa un handshake TLS tan bon punt s’obre el sòcol, valida el certificat contra el seu magatzem de confiança i només llavors intercanvia EHLO, AUTH i el missatge. Res no pot degradar la sessió perquè no hi ha cap estat de text pla des del qual negociar, cosa que elimina tota una classe de fallades d’intercepció.
El compromís és la diagnosticabilitat. Un relé que respon al 465 amb SMTP en text pla, o un balancejador de càrrega que acaba el TLS amb el nom de certificat equivocat, falla al handshake i us dona un error SSL en lloc d’un error de correu. Quan això passa, el problema és per sota de l’aplicació, i cap quantitat de construcció del missatge no ho arreglarà.
Una funció de submissió, dos transports
El codi gairebé no difereix entre els modes, i per això l’elecció hauria de fer-se per política de relé i no per comoditat de la biblioteca. Exporteu primer la configuració i després executeu un sol script que seleccioni el transport a partir del port configurat.
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() verifica la cadena i el nom d’amfitrió, de manera que una CA privada s’ha d’instal·lar al magatzem de confiança en lloc de saltar-se-la amb un context no verificat. send_message() retorna un diccionari dels destinataris que el relé ha rebutjat; un diccionari buit vol dir que els ha acceptat tots. El temps d’espera de quinze segons és un punt de partida, no un valor ajustat per a la vostra xarxa.
Manteniu la contrasenya fora de la imatge i fora dels registres. Un secret muntat en temps d’execució i llegit a l’entorn està bé; fer eco de l’entorn en una línia de registre no ho està, ni ho està un traç de depuració de l’intercanvi AUTH.
Trieu per política de relé i camí, no per costum
Tres condicions ho decideixen. Primera, què anuncia realment el relé: un servei que només escolta al 587 amb STARTTLS no es pot assolir al 465 per molt que preferiu el TLS implícit. Segona, si el camí entre l’aplicació i el relé és prou de confiança per acceptar un intercanvi EHLO llegible. Tercera, si alguna cosa al mig reescriu o fa de proxy de SMTP, perquè això decideix si el risc de degradació és teòric o real.
Quan el relé ofereix tots dos, trio el 465 per a un servei que funciona sense supervisió i el 587 per a un on una persona mira el primer desplegament, només perquè els missatges de fallada apunten a capes diferents. Aquesta preferència és meva i no és una política.
Triï el que triï, registreu el port, el material de confiança i el domini del remitent al costat del desplegament. Després envieu un missatge de prova autoritzat i confirmeu que el relé l’ha acceptat, guardant el resultat de la submissió i qualsevol informe de lliurament posterior com a registres separats. Un lliurament correcte al relé és el final de la responsabilitat de l’aplicació, no la prova que el missatge hagi arribat a una bústia d’entrada.