การใช้งานปัจจุบันของฉันมีดังนี้:
- ด้านหน้า AWS Load Balancer (ประเภทเครือข่าย TCP/UDP) บนสแต็กคู่ ซึ่งเปลี่ยนเส้นทางไปยังฟลีตของอินสแตนซ์ EC2
- อินสแตนซ์ EC2 เหล่านี้กำลังเรียกใช้ HaProxy เพื่อรับคำขอไปยังรายการกระบวนการ
- รายการกระบวนการคืออินสแตนซ์ aiosmtpd (จาก Python)
เนื่องจากจุดประสงค์คือการเชื่อมต่อผ่าน SMTP ฉันจำเป็นต้องทราบ IP ของไคลเอ็นต์ แต่ฉันต้องส่งการตอบกลับก่อนด้วย
ที่ผมสังเกตคือถ้า
- ฉันผูกส่วนหน้าโดยไม่ต้อง
ผู้รับมอบฉันทะ
- แต่เปลี่ยนเส้นทางเซิร์ฟเวอร์ที่สำรองไว้โดยการส่ง
ส่งพร็อกซี
- และปิดใช้งาน Proxy v2 บน AWS Load Balancer
มันทำงานได้อย่างสมบูรณ์ ... สำหรับ IPv4 เท่านั้น (?) !
IPv6 ใช้งานไม่ได้ และพร็อกซีส่งคืน IP ของ AWS Load Balancer ให้ฉันแทน
ดังนั้นฉันจึงลองเปิดใช้งาน Proxy v2 ที่ระดับ AWS การตั้งค่า ผู้รับมอบฉันทะ
ที่ส่วนหน้า ผูก
และ ส่งพร็อกซี
บน เซิร์ฟเวอร์
คุณสมบัติแบ็กเอนด์
คราวนี้ใช้ได้กับทั้ง IPv4/v6 แต่จะไม่ส่งการตอบกลับเริ่มต้น: จะไม่มีการเชื่อมต่อกับโค้ดไพธอนจนกว่าจะส่งบรรทัดแรกจากไคลเอ็นต์!
สิ่งนี้เป็นไปไม่ได้ในโปรโตคอล SMTP: ในฐานะเซิร์ฟเวอร์ ฉันต้องเป็นคนแรกที่ส่งการตอบกลับ
เกิดอะไรขึ้น?
นี่คือการกำหนดค่า HaProxy ของฉัน:
ทั่วโลก
บันทึก /dev/log local0.log
บันทึก /dev/log local1 ประกาศ
chroot /var/lib/haproxy
ผู้ใช้แฮพร็อกซี
กลุ่ม haproxy
ภูต
แม็กซ์คอน60000
# การเข้ารหัสเริ่มต้นเพื่อใช้กับซ็อกเก็ตการฟังที่เปิดใช้งาน SSL
# สำหรับข้อมูลเพิ่มเติม โปรดดูที่ ciphers(1SSL) รายการนี้มาจาก:
# https://hynek.me/articles/hardening-your-web-servers-ssl-ciphers/
# สามารถรับรายการทางเลือกพร้อมคำสั่งเพิ่มเติมได้จาก
# https://mozilla.github.io/server-side-tls/ssl-config-generator/?server=haproxy
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA -CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ต้องการรหัสลูกค้า no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA -CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-server-options no-sslv3 no-tlsv10 no-tlsv11 no-tls-tickets
ค่าเริ่มต้น
หมดเวลาเชื่อมต่อ 5 วินาที
ไคลเอ็นต์หมดเวลา 30 วินาที
เซิร์ฟเวอร์หมดเวลา 30 วินาที
โหมด tcp
smtp ส่วนหน้า
ผูก: 25 ยอมรับพร็อกซี
ผูก :::25 ยอมรับพร็อกซี
default_backend smtp_backend
แบ็กเอนด์ smtp_backend
โหมด tcp
หมดเวลาเซิร์ฟเวอร์ 1 ม
หมดเวลาเชื่อมต่อ 5 วินาที
เซิร์ฟเวอร์ srv1 127.0.0.1:2525 send-proxy maxconn 500
ฉันพยายามเพิ่ม/ลบอย่างใดอย่างหนึ่งหรือทั้งสองอย่างหรือไม่มีเลย ผู้รับมอบฉันทะ
และ ส่งพร็อกซี
และแม้กระทั่ง send-proxy-v2
. ไร้โชค!
สิ่งที่ใกล้เคียงที่สุดที่ฉันเคยใช้งานมาคือไม่มีการเปิดใช้งาน Proxy v2 ที่ส่วนท้ายของ AWS ไม่ใช่ ผู้รับมอบฉันทะ
ในส่วนหน้าและ ส่งพร็อกซี
บนไคลเอนต์ แต่ใช้ไม่ได้กับ IPv6
ฉันได้สร้างสคริปต์พื้นฐานเพื่ออธิบายปัญหา:
# -*- การกำหนดค่า:utf-8 -*-
นำเข้าซ็อกเก็ต argparse
คลาส TestingServer (วัตถุ):
def __init__(ตัวเอง โฮสต์ พอร์ต):
self.sock = socket.socket(ซ็อกเก็ต.AF_INET6)
self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, จริง)
self.sock.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, เท็จ)
self.sock.bind((โฮสต์, พอร์ต))
พิมพ์ ('การตั้งค่าผู้ฟังใน {}:{}'.format (โฮสต์, พอร์ต))
def ฟัง (ตัวเอง):
พิมพ์('กำลังรอการเชื่อมต่อ ...')
self.sock.listen()
ในขณะที่ทรู:
พยายาม:
ลูกค้า ที่อยู่ = self.sock.accept()
พิมพ์('มีการเชื่อมต่อ!' ที่อยู่)
client.send(b'250 กำลังส่งข้อมูลเริ่มต้น\r\n')
ข้อมูล = client.recv(1024)
พิมพ์ ('ข้อมูลที่ได้รับ:' ข้อมูล)
client.send(b'250 Gotcha')
ข้อมูล = client.recv(1024)
พิมพ์('ได้รับข้อมูล 2:', ข้อมูล)
client.send (b'250 วินาที gotcha')
ลูกค้า.ปิด()
ยกเว้น ข้อยกเว้นเป็น e:
พิมพ์ ('มีข้อยกเว้น!', จ)
ผ่าน
ในที่สุด:
พยายาม:
ลูกค้า.ปิด()
ยกเว้น ข้อยกเว้น:
ผ่าน
ถ้า __name__ == '__main__':
parser = argparse.ArgumentParser(description='เริ่ม SMTPD daemon')
parser.add_argument('--host', nargs='?', default='localhost', type=str)
parser.add_argument('--port', nargs='?', default=2552, type=int)
หาเรื่อง = parser.parse_args()
TestingServer(args.host, args.port).listen()
เมื่อตั้งค่าเปิดใช้งาน AWS Proxy v2 ด้วย ผู้รับมอบฉันทะ
สำหรับ ผูก
และ ส่งพร็อกซี
สำหรับ เซิร์ฟเวอร์
นี่คือสิ่งที่เกิดขึ้น:
ฉันเริ่มเซิร์ฟเวอร์:
$> หลาม testserver.py --port 2525 --host ::
ตั้งค่าการฟังบน :::2525
กำลังรอการเชื่อมต่อ ...
บนเครื่องของฉัน ฉันทำ:
$> เทลเน็ต {aws-loadbalancer-name}.elb.eu-west-3.amazonaws.com 25
กำลังพยายาม {ipv6}...
เชื่อมต่อกับ {aws-loadbalancer-name}.elb.eu-west-3.amazonaws.com
อักขระ Escape คือ '^]'
ยังไม่มีอะไรบนเซิร์ฟเวอร์
ในตอนท้ายของลูกค้า (telnet) ฉันเขียนอะไรก็ได้:
$> ก [ป้อน]
จากนั้น ทันทีที่ฉันกด Enter เซิร์ฟเวอร์จะแสดงสิ่งต่อไปนี้:
ที่อยู่คือ: ('::ffff:127.0.0.1', 42494, 0, 0)
มีการเชื่อมต่อ! <socket.socket fd=4, family=AddressFamily.AF_INET6, type=SocketKind.SOCK_STREAM, proto=0, laddr=('::ffff:127.0.0.1', 2525, 0, 0), raddr=(':: ffff:127.0.0.1', 42494, 0, 0)> ('::ffff:127.0.0.1', 42494, 0, 0)
ข้อมูลที่ได้รับ: b'PROXY TCP6 {clients_ip} {loadbalancer_ip} 44012 25\r\ns\r\n'
เห็นได้ชัดว่าด้วยเหตุผลบางอย่าง HaProxy ไม่ส่งการเชื่อมต่อไปยังกระบวนการ Python ทันทีที่ถูกสร้างขึ้น แต่จะรอให้ข้อมูลแรกมาถึง
ฉันจะหลีกเลี่ยงสิ่งนั้นได้อย่างไร
ขอบคุณล่วงหน้า!