วางตัวห่อ TLS ไว้หน้า OpenVPN แล้วการติดตั้งจะมีผู้ฟังตัวที่สอง ซึ่งมีเพียงตัวเดียวเท่านั้นที่ควรหันสู่อินเทอร์เน็ต เมื่ออุโมงค์ทำงานจากเครื่องเดียวกันแต่ล้มเหลวจากภายนอก การส่งต่อมักเล็งไปที่ตัวผิดในสองตัวนั้น สิ่งที่ต้องเรียงให้ตรงกันคือห่วงโซ่ กฎสาธารณะไปถึงตัวห่อ และการเชื่อมต่อแบ็กเอนด์ของตัวห่อเองไปถึงซ็อกเก็ต VPN จริง มีการกระโดดสี่ครั้งที่พาห่วงโซ่นี้ และครั้งใดครั้งหนึ่งอาจยุติการสอบสวนก่อนเวลา

สองซ็อกเก็ต สองเจ้าของ หนึ่งในนั้นเป็นสาธารณะ

ตัวห่อปิด TLS สำหรับการเชื่อมต่อภายนอก และส่งกระแสไบต์ที่ถอดรหัสแล้วไปยังผู้ฟัง OpenVPN TCP ธรรมดา สิ่งเหล่านี้คือ ซ็อกเก็ตแยกกันที่มีไฟล์กำหนดค่าแยกกัน ดังนั้นการแก้ไขอันหนึ่งจึงทิ้งอีกอันไว้ไม่ถูกแตะต้องและยังดูเหมือนสุขภาพดี นอกจากนี้ยังหมายความว่าทั้งสองอาจแยกจากกันทุกครั้งที่มีการเปลี่ยนอันหนึ่งโดยไม่อีกอัน

การทำงานจากเครื่องเดียวกันจึงพิสูจน์ได้น้อยมากเกี่ยวกับเส้นทางสาธารณะ การเชื่อมต่อตรงไปยังพอร์ตดั้งเดิมข้ามตัวห่อ การเชื่อมต่อกับตัวห่อที่ที่อยู่ LAN ของมันข้ามการแปล และมีเพียงการจัดวางฝั่งไคลเอนต์เต็มรูปแบบเท่านั้นที่ฝึกสิ่งที่ผู้ใช้ภายนอกทำ ชื่อก็ต้องอ่านอย่างระมัดระวังเช่นกัน เพราะ localhost หมายถึงโฮสต์ที่รันคอมโพเนนต์นั้นโดยเฉพาะ ไม่ใช่โฮสต์ที่คุณบังเอิญพิมพ์อยู่ เก็บความแตกต่างนี้ไว้ในบันทึกของคุณ เพราะการทดสอบที่บันทึกโดยไม่ระบุการกระโดดที่มันครอบคลุมเชิญชวนให้เปรียบเทียบสองสิ่งที่ไม่เหมือนกัน

การส่งต่อไปลงเอยที่ไหนจริง ๆ

ห่วงโซ่นี้จับไว้เป็นสี่การกระโดดได้ง่ายกว่าการจับเป็นหมายเลขพอร์ตสามหมายเลข เพราะหมายเลขเดียวกันอาจหมายถึงสิ่งต่างกันในแต่ละปลาย อ่านตารางทีละแถว แต่ละแถวคือซ็อกเก็ตหนึ่งตัวที่ต้องรับการเชื่อมต่อแล้วเปิดตัวถัดไป เมื่อแถวหนึ่งเล็งไปที่คู่ผิด อาการในคอลัมน์สุดท้ายคือสิ่งที่ผู้ใช้รายงาน

HopListens onMust reachSymptom when it points elsewhere
Client machineA local plaintext portThe wrapper's public TLS addressPlain text offered to a TLS endpoint
Router forwardPublic TCP 443The wrapper host on its translated portWrapper records no connection at all
Wrapper, server modeIts accepted port, 8443 hereThe native VPN socket on loopbackHandshake completes, then silence
Native VPN listenerLoopback TCP portIts peer, over TCP onlyA UDP socket behind a byte-stream relay

กฎที่เล็งไปที่พอร์ต VPN ดั้งเดิมแทนที่จะเป็นตัวห่อจะข้ามการปิด TLS ไปทั้งหมด และนี่คือรูปแบบที่พบบ่อยที่สุดของความผิดพลาดนี้ ไคลเอนต์เปิดการจับมือกับผู้ฟังที่คาดหวังการใส่กรอบแบบ OpenVPN การเชื่อมต่อจึงอาจเปิดขึ้นแล้วหยุดนิ่ง ตรวจสอบพอร์ตที่แปลแล้วกับไฟล์กำหนดค่าของตัวห่อไม่ใช่กับความจำ เพราะคนต่อไปที่แก้ไฟล์นั้นอาจเลือกหมายเลขอื่น สองคำสั่งด้านล่างแสดงทั้งสองด้านของคำถามนี้พร้อมกัน

grep -nE '^[[:space:]]*(accept|connect)' /etc/stunnel/stunnel.conf
ss -lntp | grep -E ':443|:8443|:1194'

ที่อยู่ผูกที่ปฏิเสธคุณ

บรรทัด accept ที่ผูกกับ 127.0.0.1 ตอบเฉพาะโฮสต์เอง ดังนั้นการส่งต่อที่มาถึงที่อยู่ LAN จึงเชื่อมต่อไม่ได้และไคลเอนต์เห็นการปฏิเสธที่ฟังดูเหมือนปัญหาตัวกรองแพ็กเก็ต มองที่ซ็อกเก็ตก่อนแตะไฟร์วอลล์ เพราะซ็อกเก็ตคือคำตอบที่ถูกกว่า จะผูกตัวห่อกับอินเทอร์เฟซที่การส่งต่อเล็ง หรือเก็บไว้บน loopback แล้วทำให้การส่งต่อไปถึง loopback ผ่านสิ่งที่ทำงานได้จริง สองอย่างนี้เป็นสองการออกแบบ และแต่ละแบบต้องมีกฎของตัวเอง

การผสมทั้งสองอย่างเข้าด้วยกันคือวิธีที่ตัวห่อที่ทำงานอยู่กลายเป็นสิ่งที่เข้าถึงไม่ได้จากภายนอกในขณะที่ทุกไฟล์ดูถูกต้อง การตรวจสอบด้านล่างเป็นคำสั่งเดียว และมันตอบคำถามที่การทบทวนกฎไฟร์วอลล์ตอบไม่ได้ หากผลลัพธ์แสดงที่อยู่ loopback ในขณะที่การส่งต่อเล็งที่อยู่ LAN การแก้ไขเป็นของบรรทัด accept ไม่ใช่ตัวกรองแพ็กเก็ต

ss -lntp '( sport = :8443 )'

การทดสอบในเครื่องพิสูจน์อะไรจริง ๆ

ความสำเร็จที่บันทึกไว้โดยไม่ระบุการกระโดดที่มันครอบคลุมไม่ใช่หลักฐานเกี่ยวกับการกระโดดอื่น จดว่าการทดสอบใช้ที่อยู่ใดและโปรเซสใดตอบกลับ จากนั้นทำซ้ำจากภายนอกเครือข่ายไม่ใช่จากโทรศัพท์บน Wi-Fi เดียวกัน เพราะพฤติกรรมการวกกลับต่างกันระหว่างเราเตอร์ และการเชื่อมต่อที่ไม่เคยออกจาก LAN ก็สามารถทำให้ไคลเอนต์พอใจได้โดยที่การส่งต่อไม่เกี่ยวข้องเลย มีเพียงความพยายามครั้งที่สองนั้นเท่านั้นที่ฝึกเส้นทางที่คนนอกใช้

เมื่อความพยายามล้มเหลว ให้ติดตามการเชื่อมต่อครั้งเดียวในทั้งสองล็อก คือบรรทัดที่รับและเชื่อมต่อของตัวห่อ แล้วตามด้วยล็อกเซสชันของดีมอน VPN เอง เส้นแบ่งแรกที่ไม่บันทึกอะไรเลยคือที่ที่การเปลี่ยนแปลงครั้งต่อไปควรอยู่ เมื่อ OpenVPN เริ่มเปรียบเทียบตัวเลือกของคู่สัญญา ให้ตัดสินข้อความเหล่านั้นตามกฎของ implementation นั้น คำเตือนเรื่อง link-mtu ไม่สอดคล้องไม่ได้ถึงตายโดยอัตโนมัติ การตรึง link-mtu ไว้ที่ 1500 ไม่ได้พิสูจน์ว่าการแบ่งส่วนหยุดแล้ว และการเจรจาไซเฟอร์เป็นของกำหนดค่าภายใน OpenVPN ไม่ใช่การส่งต่อของเราเตอร์

nc -vz 127.0.0.1 1194
journalctl -u stunnel -n 50 --no-pager

การตรวจเหล่านั้นครอบคลุมซ็อกเก็ตแบ็กเอนด์และล็อกของตัวห่อ ซึ่งแยกแบ็กเอนด์ที่ถูกปฏิเสธออกจากการจับมือที่ถูกปฏิเสธ รันระหว่างที่มีความพยายามครั้งเดียวกำลังดำเนินอยู่ ไม่ใช่หลังจากนั้น เวลาประทับจากการพยายามครั้งเดียวคือสิ่งที่ทำให้การเปรียบเทียบน่าเชื่อถือ

สองอัตลักษณ์ สองลำดับความล้มเหลว

ใบรับรองภายนอกเป็นของตัวห่อและอัตลักษณ์ภายในเป็นของ OpenVPN และอาจมาจากหน่วยงานต่างกัน การปิดการตรวจสอบใดตัวหนึ่งเพื่อให้เส้นทางดูสุขภาพดีคือการแลกการป้องกันจริงกับผลลัพธ์ด้านภาพ เก็บการตรวจสอบทั้งสองไว้และดีบักตามลำดับ เพราะลำดับของข้อความบ่งชี้ชั้น ปัญหาใบรับรองของตัวห่อปรากฏก่อนการเจรจาตัวเลือก VPN ใด ๆ ส่วนความล้มเหลวของอัตลักษณ์ภายในจะปรากฏเฉพาะเมื่อกระแสถูกสถาปนาแล้ว

คู่มือ stunnel กำหนด accept, connect และโหมดไคลเอนต์อย่างแม่นยำ และส่วนเหล่านั้นตอบคำถามเรื่องซ็อกเก็ตได้เร็วกว่าการเดา เมื่อยังบอกไม่ได้ว่าชั้นใดตอบ ให้อ่านใบรับรองที่พอร์ตสาธารณะนำเสนอด้วยคำสั่งด้านล่าง การมีใบรับรองของตัวห่อในผลลัพธ์หมายความว่าการส่งต่อไปถึงตัวห่อ และความเงียบหรือข้อผิดพลาดของโปรโตคอลหมายความว่าไม่ถึง ความแตกต่างเดียวนี้ประหยัดเวลากว่าการตรวจอื่นใดที่นี่

openssl s_client -connect vpn.example:443 -servername vpn.example </dev/null 2>/dev/null | openssl x509 -noout -subject -issuer

บทความที่เกี่ยวข้อง