อนุญาตการเชื่อมต่อ TLS passthrough ในบริบท stream ของ NGINX
NGINX สามารถปฏิเสธรีเลย์ TCP TLS โดยไม่ต้องยุติ TLS วางกฎการอนุญาตใน stream ไม่ใช่ใน HTTP location และตรวจสอบว่า peer address ใดมาถึง
การส่งต่อไบต์ที่เข้ารหัสโดยไม่เคยถือใบรับรองคือสิ่งที่รีเลย์ TLS passthrough ทำ และการจัดวางนี้ทั้งสะดวกและสับสนเล็กน้อยในเวลาเดียวกัน เนื่องจากไม่มีอะไรถูกถอดรหัส จึงไม่มีสิ่งใดในรูปแบบ HTTP ให้กรอง ดังนั้นบล็อก location ปกติและกฎการเข้าถึงของมันจึงไม่มีประโยชน์ที่นี่ การตัดสินใจที่ NGINX มีนั้นเกี่ยวกับการเชื่อมต่อเอง: peer เครือข่ายใดที่เคาะเข้ามา และควรส่งต่อ peer นั้นหรือไม่
การตัดสินใจอยู่ใน stream ไม่ใช่ HTTP
ในบริบท stream NGINX ทำงานกับเซสชัน TCP มันเห็น peer address ของการเชื่อมต่อและสามารถใช้กฎ allow และ deny ตามลำดับกับมันก่อนที่ไบต์ใดจะถึง backend นี่เป็นการตัดสินใจรับเข้าที่สมบูรณ์และไม่ต้องใช้คีย์ส่วนตัว ใบรับรอง หรือการแยกวิเคราะห์ HTTP
| Mechanism | What it decides | Where it lives |
|---|---|---|
| stream allow/deny | Whether this TCP peer is relayed | stream server block |
| ssl_preread map | Which backend a named connection goes to | stream, with the preread module |
| HTTP location rule | Whether an HTTP request is served | http server block |
สามบรรทัดนั้นไม่เคยแทนที่กัน การคัดลอกคำสั่งเข้าถึง HTTP ไปยังไฟล์ใดก็ตามที่กล่าวถึงพอร์ต 443 ไม่เปลี่ยนแปลงอะไร เพราะเซิร์ฟเวอร์ stream ไม่เคยอ่านบริบท http
ยอมรับการเชื่อมต่อก่อนส่งต่อ
สมมติว่ามีการติดตั้งที่มีโมดูล stream และ stream-access พร้อมใช้งาน ไคลเอนต์ VPN เก็บต้นทางแอดเดรสไว้ใน 10.90.0.0/24 และมี backend ที่ 192.168.50.20:443 ซึ่งให้บริการตัวตน HTTPS ที่ต้องการอยู่แล้ว
stream {
server {
listen 443;
allow 10.90.0.0/24;
deny all;
proxy_pass 192.168.50.20:443;
}
}
อ่านสิ่งนี้เป็นส่วนหนึ่งของ listener ใหม่ ไม่ใช่การแทนที่การกำหนดค่าที่ทำงานอยู่โดยตรง ตรวจสอบ listener และ include ที่มีอยู่ก่อน เพราะ listener ที่สองบนพอร์ตที่ถูกใช้อยู่แล้วจะล้มเหลวตอน reload ไม่ใช่ตอนร้องขอ
สังเกตสิ่งที่ขาดหาย: ไม่มี listen ... ssl ไม่มีคำสั่งใบรับรอง NGINX ส่งต่อสตรีมที่เข้ารหัส และ backend ยุติ TLS และนำเสนอใบรับรองที่ไคลเอนต์ตรวจสอบ ไคลเอนต์ที่ไม่ผ่าน allowlist จะถูกปฏิเสธเซสชัน TCP ไม่ใช่หน้า HTTP 403 และเครื่องมือใดที่คาดหวังรหัสสถานะจะรายงานสิ่งที่ไม่เป็นประโยชน์
allowlist ครอบคลุมอะไรและไม่ครอบคลุมอะไร
หากรีเลย์เลือก backend ตามชื่อเซิร์ฟเวอร์อยู่แล้ว ssl_preread และ map สามารถทำงานนั้นต่อไปได้หากโมดูลพร้อมใช้งาน ชื่อยังคงถูกจัดหาโดยไคลเอนต์ และไม่มีสิ่งใดในการนำเสนอชื่อที่พิสูจน์ว่าไคลเอนต์เข้าร่วม VPN
กฎตัวอย่างครอบคลุม listener stream ทั้งหมด สิ่งนี้ทำให้ไม่เหมาะกับจุดเข้าออกเดียวที่ให้บริการเว็บไซต์สาธารณะและเว็บไซต์เฉพาะ VPN ผสมกัน ซึ่งจะต้องมีข้อยกเว้นต่อชื่อ listener แยกมักจะชัดเจนกว่านโยบายที่ขึ้นกับชื่อซึ่งไม่มีใครอ่านได้
ควรกล่าวถึงช่องว่างอีกสองประการ การจับคู่ prefix ไม่ได้บอกว่าแพ็กเก็ตเข้ามาทางอินเทอร์เฟซใด และกฎ HTTP ที่อื่นไม่สามารถจำกัด listener นี้ได้ ทั้งสองเป็นเหตุผลที่ควรเก็บนโยบาย ingress ระดับโฮสต์ไว้ในภาพแทนที่จะถือว่า allow บรรทัดเดียวเป็นขอบเขตทั้งหมด เขียนขอบเขตเป็นสองการควบคุมที่ต้องสอดคล้องกัน เพื่อให้การเปลี่ยนแปลงในสิ่งใดสิ่งหนึ่งได้รับการทบทวน
NGINX เห็นแอดเดรสของใครจริง ๆ
ตัวกระจายโหลด พร็อกซีอื่น หรือ source NAT สามารถวางตัวกลางไว้หน้า NGINX ได้ ดังนั้น peer ที่เห็นจึงไม่ใช่แอดเดรสภายในของไคลเอนต์ VPN ตรวจสอบเส้นทางจริงก่อนเลือก prefix เพราะ allowlist ที่เขียนสำหรับแอดเดรสผิดจะอนุญาตทุกคนที่อยู่หลังตัวแปลหรือไม่ให้ใครเลย
โปรโตคอล PROXY และการจัดการ IP จริง เป็นคำตอบที่ชอบด้วยกฎหมาย และควรกำหนดค่าเฉพาะสำหรับผู้ส่งที่เข้ากันได้และได้รับความไว้วางใจอย่างชัดเจน กลไกทั้งสองเชื่อสิ่งที่ผู้ส่งพูด และนั่นคือเหตุผลที่รายชื่อผู้ส่งต้องสั้น การไว้วางใจส่วนหัวระบุตัวตนจากไคลเอนต์อินเทอร์เน็ตใด ๆ ไม่ได้ทำให้ข้อมูลต้นทางน่าเชื่อถืออีกครั้ง แต่เปลี่ยนการปฏิเสธที่ถูกต้องเป็นการยอมรับที่ปลอมแปลง
ทดสอบทั้งสองผลลัพธ์ก่อน reload
ตรวจสอบความถูกต้องของการกำหนดค่า ทำ reload แล้วทดสอบสองครั้ง: เซสชันใหม่ที่ได้รับอนุญาตซึ่งต้องทำงานได้ และเซสชันที่ถูกปฏิเสธซึ่งไม่ควรทำงาน เก็บการตรวจสอบใบรับรอง backend ตามปกติไว้ในการทดสอบที่ได้รับอนุญาต เพราะรีเลย์ที่เชื่อมต่อได้แต่ตรวจสอบ backend ไม่ได้แก้ปัญหาหนึ่งและสร้างอีกปัญหาหนึ่ง
nginx -t
ss -tnp | grep ':443'
ตรวจสอบเส้นทางไปยัง backend และการตอบกลับแยกกันด้วย การผ่าน allowlist พิสูจน์เพียงว่า NGINX ยอมรับเซสชัน ไม่สามารถบังคับ backend ที่เข้าถึงไม่ได้ให้ตอบกลับ และไม่สามารถซ่อมเส้นทางกลับที่ขาดหายก่อนคุณเริ่มได้