Begge tilkoplingsmodusane ber same meldinga til same relékontoen, og skilnaden er kvar TLS byrjar, ikkje kva som vert sendt. Den eine opnar i klartekst og vert oppgradert i bandet; den andre er kryptert frå fyrste byte. Den eine skilnaden avgjer kva ein streng relépolitikk vil godta, kva ein mellomliggjande boks kan sjå, og kva feilmelding du kjem til å lese ein heil ettermiddag. Slik vel eg mellom dei når ein applikasjon på eit privat nettverk overlet ei melding til ein intern innsendingsteneste.

Kvar dei to modusane faktisk skil lag

Oppgåva er identisk i begge tilfelle: autentisere som ein godkjend konto, namngje ein tillaten avsendar, liste mottakarar og vente på at reléet tek på seg ansvaret. Det som endrar seg er rekkjefølgja av operasjonar. Port 587 byrjar som ein SMTP-økt i klartekst, annonserer STARTTLS i EHLO-svaret og vert oppgradert berre om klienten ber om det. Port 465 ventar eit TLS-handtrykk før nokon SMTP-kommando vert sagt.

Innsending og relé er ulike jobbar, og difor er portnummera ikkje utskiftbare vanar. RFC 6409 skildrar meldingsinnsending som ei eiga teneste, og RFC 8314 dekkjer implisitt-TLS-varianten som dei fleste moderne relé tilbyr på 465. Viss reléet ditt dokumenterer éin av dei to, går den dokumentasjonen framom kvar preferanse du har med deg frå ein tidlegare arbeidsgjevar.

Kva STARTTLS på 587 gir deg

Oppgraderinga skjer inne i SMTP-samtalen, så reléet kan annonsere ho, og klienten kan nekte å halde fram utan ho. I Python kastar starttls() eit unntak når tenaren ikkje tilbyr utvidinga, noko som gjer eit nedgraderingsforsøk til ein synleg feil i staden for ein stille klartekst-innlogging. Den åtferda er hovudargumentet for modusen, og det er verdt å stole på han med vilje og ikkje ved eit uhell.

Eksponeringsvindauget er lite men reelt: den innleiande EHLO-utvekslinga er lesbar, og ingenting før oppgraderinga ber legitimasjon. På ein privat veg mellom eit applikasjonssubnett og eit internt relé er det vindauget vanlegvis akseptabelt. På ein veg du ikkje kontrollerer, er det grunnen til at nokre team insisterer på den andre modusen.

Ei avlyttareining som fjernar STARTTLS-evna gir ei mislukka innsending, ikkje ein tilbakefall. Det er resultatet du ønskjer, og det er også den raskaste måten å oppdage at noko skriv om SMTP på vegen.

Kva implisitt TLS på 465 endrar

Det finst ingen klartekstfase i det heile. Klienten utfører eit TLS-handtrykk så snart sokkelen opnar, validerer sertifikatet mot sitt tillitslager og utvekslar først deretter EHLO, AUTH og meldinga. Ingenting kan nedgradere økta fordi det ikkje finst nokon klarteksttilstand å forhandle frå, og det fjernar ein heil klasse av avlyttingsfeil.

Bytet er diagnostiserbarheit. Eit relé som svarar på 465 med klartekst-SMTP, eller ein lastbalansar som avsluttar TLS med feil sertifikatnamn, feilar ved handtrykket og gir deg ein SSL-feil i staden for ein postfeil. Når det skjer, ligg problemet under applikasjonen, og inga mengd meldingsbygging rettar det.

Éin innsendingsfunksjon, to transportar

Koden skil seg knapt mellom modusane, og difor bør valet takast ut frå relépolitikken og ikkje frå biblioteket si lette. Eksporter innstillingane først, og køyr deretter eit einskilt skript som vel transporten ut frå 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 kjeda og vertsnamnet, så ein privat CA må installerast i tillitslageret i staden for å omgåast med ein uverifisert kontekst. send_message() returnerer ei ordbok over mottakarar som reléet avviste; ei tom ordbok tyder at det godtok alle. Tidsavbrotet på femten sekund er eit utgangspunkt, ikkje ein finjustert verdi for nettverket ditt.

Hald passordet utanfor imaget og utanfor loggane. Ei løyndom som vert montert ved køyring og lesen inn i miljøet, er greitt; å ekko miljøet ut i ei logglinje er det ikkje, og heller ikkje eit feilsøkingsspor av AUTH-utvekslinga.

Vel etter relépolitikk og veg, ikkje etter vane

Tre vilkår avgjer det. For det første, kva reléet faktisk annonserer: ei teneste som berre lyttar på 587 med STARTTLS kan ikkje nåast på 465, uansett kor mykje du føretrekk implisitt TLS. For det andre, om vegen mellom applikasjonen og reléet er betrodd nok til å godta ei lesbar EHLO-utveksling. For det tredje, om noko i midten skriv om eller proxier SMTP, fordi det avgjer om nedgraderingsrisikoen er teoretisk eller levande.

Når reléet tilbyr begge, tek eg 465 for ei teneste som køyrer utan tilsyn og 587 for ei der ein person følgjer den første utrullinga, berre fordi feilmeldingane peikar på ulike lag. Den preferansen er min, og han er ikkje ein politikk.

Uansett kva du vel, før opp porten, tillitsmaterialet og avsendar-domenet saman med utrullinga. Send deretter éi autorisert testmelding og stadfest at reléet godtok ho, og ta vare på innsendingsresultatet og kvar seinare leveringsrapport som separate postar. Ei vellukka overlevering til reléet er slutten på ansvaret til applikasjonen, ikkje prov på at meldinga nådde ein innboks.

Relaterte artiklar