สองชื่อ DNS หนึ่งพอร์ต OpenVPN: อะไรคัดเลือกจริง ๆ
ชื่อโฮสต์สองชื่อที่ชี้ไปยังที่อยู่เดียวกันก็ยังไปถึงผู้ฟังเดียวเท่านั้น มีเพียงฟิลด์ที่ไคลเอนต์ส่งเท่านั้นที่เลือก backend ได้ และ OpenVPN มาตรฐานไม่ส่งฟิลด์ใดเลย
ระเบียน A สองรายการ ที่อยู่เดียว เดมอนเดียว ไม่ว่าผู้ใช้จะพิมพ์ชื่อใด แพ็กเก็ตที่มาถึงก็เหมือนกัน และโปรเซสที่ผูกกับพอร์ต 1194 ไม่มีอะไรจะแยกสองสิ่งนั้นได้ นี่ไม่ใช่การกำหนดค่าที่ผิดรอการซ่อม แต่เป็นขีดจำกัดของสิ่งที่การค้นหาชื่อสามารถแสดงออกได้ ทุกสิ่งที่จะเลือกได้ระหว่าง OpenVPN สองอินสแตนซ์ต้องเป็นค่าที่ไคลเอนต์ใส่ลงในคอนเนกชันและส่วนหน้าโอนกลับมาอ่านได้ และไคลเอนต์ OpenVPN มาตรฐานไม่ได้ใส่ค่าลักษณะนั้นแบบเปิดเผย สิ่งที่ตามมาคือมันทิ้งคุณไว้ที่ใด และการจัดวางแบบใดแยกทราฟฟิกได้จริง
เริ่มจากสิ่งที่ตัวแก้ไขชื่อส่งคืนมา
ไคลเอนต์ถามหา vpn-a.example และได้ที่อยู่หนึ่งมา ที่อยู่นั้นบวกพอร์ตคือทั้งหมดที่มันเชื่อมต่อ ชื่อถูกใช้จ่ายภายในเครื่องระหว่างการค้นหา และไม่เคยกลายเป็นฟิลด์บนสาย จึงทำให้ผู้ฟังปลายอีกด้านมองไม่เห็นว่ามีการใช้ป้ายชื่อใด ชี้ vpn-a.example และ vpn-b.example ไปยังโฮสต์เดียวกัน และการค้นหาทั้งสองให้ปลายทางเดียวกัน นั่นคือเหตุผลที่เดมอนดูดกลืนทั้งสองคอนเนกชันไว้ในอินสแตนซ์เดียวโดยไม่บันทึกความแตกต่าง โปรไฟล์ที่อิงที่อยู่มีพฤติกรรมเหมือนโปรไฟล์ที่อิงชื่อทุกประการ ด้วยเหตุผลเดียวกัน
นี่ยังปิดตระกูลของทางลัดที่น่าดึงดูดด้วย คือชื่อแทนเพิ่มเติม การเลือกใบรับรองต่อชื่อบนซ็อกเก็ตเดียว หรือกฎไฟร์วอลล์ที่ผูกกับชื่อที่ถูกถาม ไม่มีสิ่งใดในนั้นสามารถกระทำต่อข้อมูลที่ถูกทิ้งไปแล้วก่อนที่แพ็กเก็ตแรกจะออกจากไคลเอนต์ ถ้าสองเซิร์ฟเวอร์ต้องแยกจากกันต่อไป การแยกต้องเดินทางไปกับคอนเนกชันเอง นั่นคือที่อยู่ พอร์ต หรือฟิลด์เพย์โหลดที่โปรโตคอลกำหนด
ตัวเลือกเดียวที่นับได้เดินทางอยู่ภายในแพ็กเก็ต
HTTP จัดการเรื่องนี้ด้วยส่วนหัว Host และ TLS ด้วยส่วนขยายชื่อเซิร์ฟเวอร์ใน ClientHello และนั่นคือวิธีที่ที่อยู่เดียวให้บริการหลายไซต์หลังผู้ฟังคนเดียว ทั้งสองทำงานได้เพราะโปรโตคอลบนสายพาคำขอสำหรับบริการเฉพาะเจาะจง ข้อเท็จจริงที่เกี่ยวข้องกับ OpenVPN คือไม่มีฟิลด์ใดในสองฟิลด์นี้อยู่ที่ชั้นที่กล่องกลางตรวจสอบ
ในการจัดวางมาตรฐาน การจับมือ TLS ที่ปกป้องช่องควบคุมเกิดขึ้นหลังจากเซสชันภายนอกถูกสร้างแล้วและถูกบรรทุกอยู่ภายในเซสชันนั้น ผู้ฟังที่ยังไม่ได้สิ้นสุดเซสชันนั้นจึงเห็นสตรีมทึบแสง มันยังบอกได้ว่านี่คือ OpenVPN เพราะอ็อปโค้ดและการแบ่งเฟรมในไบต์ต้น ๆ นั้นจำได้ แต่ชื่อเซิร์ฟเวอร์หากมีอยู่กลับอยู่ในส่วนที่เข้ารหัส การออกแบบที่คาดว่าจะอ่านโฮสต์เสมือนจากแพ็กเก็ตภายนอกกำลังอ่านฟิลด์ที่โปรโตคอลไม่เคยเขียนไว้ที่นั่น สิ่งนี้มาจากวิธีสร้างอุโมงค์ ไม่ใช่จากช่องว่างที่การจับคู่รูปแบบที่ฉลาดกว่าจะปิดได้
จุดที่ตัวแยกโปรโตคอลหยุด
มัลติเพล็กเซอร์แบบที่โครงการ sslhจัดทำเอกสารไว้นั้นฟังบนพอร์ตเดียว ตรวจไบต์ต้น ๆ และส่งคอนเนกชันให้กับ backend ที่พูดโปรโตคอลที่มันจำได้ นั่นคือคุณค่าจริงเมื่อพอร์ตเดียวต้องให้บริการ SSH, HTTPS และ OpenVPN พร้อมกัน
sslh เก็บการตรวจแยกสำหรับแต่ละโปรโตคอล และการตรวจ TLS ของมันสามารถดึงชื่อเซิร์ฟเวอร์หรือค่า ALPN ออกจาก ClientHello แท้ได้ การจดจำ OpenVPN มาตรฐานเป็นการจำแนกที่ต่างจากการอ่านส่วนขยาย TLS และทั้งสองไม่ประกอบกัน เพราะไคลเอนต์ที่ไม่เคยส่ง ClientHello ไปยังซ็อกเก็ตที่ฟังอยู่ย่อมทิ้งการตรวจไว้โดยไม่มีอะไรให้อ่าน ดังนั้นกฎที่ตรงกับ OpenVPN จะตอบว่า “นี่คือไคลเอนต์ OpenVPN” และไม่ไปต่อ backend OpenVPN สองตัวหลังกฎนั้นแยกจากกันไม่ได้ ส่วนหน้าไม่มีพื้นฐานสำหรับการเลือก และทุกเซสชันลงเอยที่เดียวกัน การมัลติเพล็กซ์แบ่งพอร์ตเดียวระหว่างบริการที่ต่างกัน มันไม่ได้ทำโฮสติ้งเสมือนภายในบริการเดียว
การจัดวางที่แยกเซิร์ฟเวอร์สองตัวได้จริง
การแยกอย่างซื่อตรงที่ถูกที่สุดคือที่อยู่สาธารณะที่สอง หนึ่งรายการต่ออินสแตนซ์ และโปรไฟล์ที่มีอยู่ก็มีสิ่งที่จำเป็นอยู่แล้วเพราะชี้ไปยังที่อยู่ต่างกัน ต่อไปคือพอร์ตที่ต่างกันบนที่อยู่เดียวกัน ซึ่งไม่เสียค่าใช้จ่ายใดนอกจากตัวเลขในแต่ละโปรไฟล์ TCP และ UDP เก็บตารางพอร์ตแยกกัน ดังนั้น 1194/tcp และ 1194/udp เป็นซ็อกเก็ตที่ไม่เกี่ยวข้องกัน และอย่างละหนึ่งสามารถอยู่ร่วมกันบนโฮสต์เดียวได้ นั่นคือการแยกชั้นขนส่งไม่ใช่การเราท์ตามชื่อ และมักถูกเข้าใจผิดว่าเป็นอย่างนั้น ทั้งสองทางเลือกผ่านการทบทวนไฟร์วอลล์ได้ เพราะกฎตรงกับสิ่งที่แพ็กเก็ตมีอยู่จริง
ถ้าขอบเขตที่คุณต้องการพาดผ่านระหว่างกลุ่มผู้ใช้แทนที่จะเป็นระหว่างสองการใช้งาน อินสแตนซ์เดียวที่มีการกำหนดค่าไคลเอนต์ต่างกัน มีการจัดการใบรับรองของตนเองและมีนโยบายการเข้าถึงที่ชัดเจนสามารถครอบคลุมได้ สิ่งที่ได้มาคือนโยบายภายในเซิร์ฟเวอร์ ไม่ใช่การแยกโดดเดี่ยวระหว่างเซิร์ฟเวอร์ และทั้งสองเป็นข้อกำหนดที่ต่างกัน การแยกโดดเดี่ยวที่ต้องทนได้ภายใต้การถูกบุกรุกยังต้องอาศัยที่อยู่หรือขอบไฟร์วอลล์
ตัวสิ้นสุด TLS ที่วางไว้หน้า OpenVPN สามารถเปิดเผยชื่อเซิร์ฟเวอร์จริงให้กับส่วนหน้าที่มีความสามารถได้ แต่เฉพาะเมื่อฝั่งไคลเอนต์ได้รับการกำหนดค่าสำหรับชั้นเพิ่มเติมนั้นด้วย โปรไฟล์ที่มีอยู่ไม่ได้พฤติกรรมนั้นเพียงเพราะถูกชี้ไปยังที่อยู่เดียวกัน
พิสูจน์ตัวเลือกก่อนสร้าง
ตั้งชื่อฟิลด์ที่จะระบุ backend แล้วยืนยันว่าไคลเอนต์ส่งมันจริง หากคำตอบคือที่อยู่ แผนก็ยืนได้ และหากเป็นชื่อเซิร์ฟเวอร์ภายในการจับมือที่ไม่เคยไปถึงผู้ฟัง งาน DNS ใดก็ไม่ช่วยได้ สามคำสั่งตัดสินฝั่งที่ฟังได้อย่างรวดเร็ว
ss -lntup | grep -E ':443|:1194'
openssl s_client -connect vpn-a.example:443 -servername vpn-a.example </dev/null | head -n 20
journalctl -u openvpn-server@server-a -n 20 --no-pager
บรรทัด openssl ให้ใบรับรองเฉพาะที่ซึ่งมีบางสิ่งบนที่อยู่นั้นสิ้นสุด TLS เมื่อเทียบกับผู้ฟัง OpenVPN มาตรฐานมันจะค้างหรือล้มเหลว และนั่นเองคือคำตอบว่าชั้นนอกเปิดเผยอะไร บรรทัด journal เมื่ออ่านบนทุก backend จะแสดงว่าอินสแตนซ์ใดรับเซสชัน โลโก้ของส่วนหน้าบันทึกสิ่งที่ถูกจำแนกและไม่เคยบันทึกสิ่งที่ตั้งใจ ดังนั้น backend จึงยังเป็นผู้มีอำนาจเดียวว่า ผู้ใช้ไปลงเอยที่ใด
ปิดท้ายด้วยโปรไฟล์สองรายการที่ตั้งใจให้ไปถึงเซิร์ฟเวอร์ต่างกัน เชื่อมต่อทีละรายการและบันทึกโลโก้ของ backend สำหรับแต่ละรายการ หากเซสชันทั้งสองปรากฏใน journal เดียวกัน แสดงว่าตัวเลือกหายไปไม่ใช่กำหนดค่าผิด และการแก้ไขคือที่อยู่หรือพอร์ตที่ไคลเอนต์กำหนดเป้าหมายได้ การแก้ไขกฎเราเทิงไม่ช่วย เพราะกฎนั้นไม่เคยได้รับอะไรให้เราเทิงเลย เมื่อการแยกเป็นจริงแล้ว ให้เขียนไว้ข้าง ๆ โปรไฟล์ เพื่อที่คนถัดไปจะได้ไม่เพิ่มชื่อแทนที่สองและคาดหวังว่ามันจะทำงานนั้น