Python-ის SMTP გაგზავნა: STARTTLS 587-ზე თუ TLS 465-ზე?
STARTTLS 587-ზე და იმპლიციტური TLS 465-ზე ორივე გადააქვს Python-ის გაგზავნას, მაგრამ თქვენს რელეს მხოლოდ ერთი შეესაბამება. შეადარეთ ხელის ჩამორთმევა და წარუმატებლობის რეჟიმები.
ორივე კავშირის რეჟიმი ერთსა და იმავე შეტყობინებას ერთსა და იმავე რელეს ანგარიშზე გადასცემს, და განსხვავება იმაშია, სად იწყება TLS, და არა იმაში, რა იგზავნება. ერთი ღია ტექსტით იხსნება და არხის შიგნით აუმჯობესდება; მეორე პირველი ბაიტიდანვე დაშიფრულია. სწორედ ეს ერთი განსხვავება განსაზღვრავს, რას მიიღებს მკაცრი რელეს პოლიტიკა, რას შეძლებს შუამავალი მოწყობილობის დაკვირვება და რომელი შეცდომის შეტყობინების კითხვაში გაატარებთ ნახევარ დღეს. აი, როგორ ვირჩევ მათ შორის, როცა კერძო ქსელში მოქმედი აპლიკაცია შეტყობინებას შიდა გაგზავნის სერვისს გადასცემს.
სად განსხვავდება ორი რეჟიმი სინამდვილეში
დავალება ორივე შემთხვევაში ერთია: ავთენტიფიკაცია დამტკიცებულ ანგარიშად, ნებადართული გამომგზავნის დასახელება, მიმღებების ჩამოთვლა და ლოდინი, სანამ რელე პასუხისმგებლობას აიღებს. იცვლება ოპერაციების თანმიმდევრობა. პორტი 587 იწყება როგორც ღია ტექსტის SMTP სესია, EHLO პასუხში აცხადებს STARTTLS-ს და აუმჯობესდება მხოლოდ მაშინ, თუ კლიენტი ითხოვს. პორტი 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-ს, მხოლოდ იმიტომ, რომ წარუმატებლობის შეტყობინებები სხვადასხვა ფენაზე მიუთითებს. ეს უპირატესობა ჩემია და ეს პოლიტიკა არ არის.
რასაც აირჩევთ, ჩაწერეთ პორტი, ნდობის მასალა და გამომგზავნის დომენი განლაგებასთან ერთად. შემდეგ გაგზავნეთ ერთი უფლებამოსილი სატესტო შეტყობინება და დაადასტურეთ, რომ რელემ ის მიიღო, გაგზავნის შედეგი და ნებისმიერი შემდგომი მიწოდების ანგარიში ცალკე ჩანაწერებად შეინახეთ. რელეზე წარმატებული გადაცემა აპლიკაციის პასუხისმგებლობის დასასრულია და არა მტკიცება, რომ შეტყობინებამ საფოსტო ყუთამდე მიაღწია.