Begge tilkoblingsmodusene bærer samme melding til samme relékonto, og forskjellen er hvor TLS begynner, ikke hva som sendes. Den ene åpner i klartekst og oppgraderes i båndet; den andre er kryptert fra første byte. Den ene forskjellen avgjør hva en streng relépolicy vil godta, hva en mellomliggende boks kan observere, og hvilken feilmelding du kommer til å lese en hel ettermiddag. Slik velger jeg mellom dem når en applikasjon på et privat nettverk overleverer en melding til en intern innsendingstjeneste.

Hvor de to modusene faktisk skiller lag

Oppgaven er identisk i begge tilfeller: autentisere som en godkjent konto, navngi en tillatt avsender, liste mottakere og vente på at reléet påtar seg ansvaret. Det som endres er rekkefølgen av operasjoner. Port 587 starter som en SMTP-økt i klartekst, annonserer STARTTLS i EHLO-svaret og oppgraderes bare hvis klienten ber om det. Port 465 forventer et TLS-håndtrykk før noen SMTP-kommando blir sagt.

Innsending og relé er forskjellige jobber, og derfor er portnumrene ikke utskiftbare vaner. RFC 6409 beskriver meldingsinnsending som en egen tjeneste, og RFC 8314 dekker implisitt-TLS-varianten som de fleste moderne reléer tilbyr på 465. Hvis reléet ditt dokumenterer én av de to, går den dokumentasjonen foran enhver preferanse du har med deg fra en tidligere arbeidsgiver.

Hva STARTTLS på 587 gir deg

Oppgraderingen skjer inne i SMTP-samtalen, så reléet kan annonsere den, og klienten kan nekte å fortsette uten den. I Python kaster starttls() et unntak når serveren ikke tilbyr utvidelsen, noe som gjør et nedgraderingsforsøk til en synlig feil i stedet for en stille klartekst-innlogging. Den oppførselen er hovedargumentet for modusen, og det er verdt å stole på den med vilje og ikke ved et uhell.

Eksponeringsvinduet er lite men reelt: den innledende EHLO-utvekslingen er lesbar, og ingenting før oppgraderingen bærer legitimasjon. På en privat vei mellom et applikasjonssubnett og et internt relé er det vinduet vanligvis akseptabelt. På en vei du ikke kontrollerer, er det grunnen til at noen team insisterer på den andre modusen.

En avlyttingsenhet som fjerner STARTTLS-evnen gir en mislykket innsending, ikke en tilbakefall. Det er resultatet du ønsker, og det er også den raskeste måten å oppdage at noe skriver om SMTP på veien.

Hva implisitt TLS på 465 endrer

Det finnes ingen klartekstfase i det hele tatt. Klienten utfører et TLS-håndtrykk så snart sokkelen åpnes, validerer sertifikatet mot sitt tillitslager og utveksler først deretter EHLO, AUTH og meldingen. Ingenting kan nedgradere økten fordi det ikke finnes noen klarteksttilstand å forhandle fra, og det fjerner en hel klasse av avlyttingsfeil.

Byttet er diagnostiserbarhet. Et relé som svarer på 465 med klartekst-SMTP, eller en lastbalanserer som avslutter TLS med feil sertifikatnavn, feiler ved håndtrykket og gir deg en SSL-feil i stedet for en postfeil. Når det skjer, ligger problemet under applikasjonen, og ingen mengde meldingsbygging retter det.

Én innsendingsfunksjon, to transporter

Koden skiller seg knapt mellom modusene, og derfor bør valget tas ut fra relépolicyen og ikke fra bibliotekets bekvemmelighet. Eksporter innstillingene først, og kjør deretter et enkelt skript som velger transporten ut fra den konfigurerte porten.

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() verifiserer kjeden og vertsnavnet, så en privat CA må installeres i tillitslageret i stedet for å omgås med en uverifisert kontekst. send_message() returnerer en ordbok over mottakere som reléet avviste; en tom ordbok betyr at det godtok alle. Tidsavbruddet på femten sekunder er et utgangspunkt, ikke en finjustert verdi for nettverket ditt.

Hold passordet utenfor imaget og utenfor loggene. En hemmelighet som monteres ved kjøring og leses inn i miljøet, er greit; å ekko miljøet ut i en logglinje er det ikke, og heller ikke et feilsøkingsspor av AUTH-utvekslingen.

Velg etter relépolicy og vei, ikke etter vane

Tre betingelser avgjør det. For det første, hva reléet faktisk annonserer: en tjeneste som bare lytter på 587 med STARTTLS kan ikke nås på 465, uansett hvor mye du foretrekker implisitt TLS. For det andre, om veien mellom applikasjonen og reléet er betrodd nok til å godta en lesbar EHLO-utveksling. For det tredje, om noe i midten skriver om eller proxier SMTP, fordi det avgjør om nedgraderingsrisikoen er teoretisk eller levende.

Når reléet tilbyr begge, tar jeg 465 for en tjeneste som kjører uten tilsyn og 587 for en der en person følger den første utrullingen, bare fordi feilmeldingene peker på forskjellige lag. Den preferansen er min, og den er ikke en policy.

Uansett hva du velger, før opp porten, tillitsmaterialet og avsenderdomenet sammen med utrullingen. Send deretter én autorisert testmelding og bekreft at reléet godtok den, og ta vare på innsendingsresultatet og enhver senere leveringsrapport som separate poster. En vellykket overlevering til reléet er slutten på applikasjonens ansvar, ikke bevis for at meldingen nådde en innboks.

Relaterte artikler