สคริปต์ฮุกของ OpenVPN ใช้ทันเนลที่เพิ่งสร้างได้หรือไม่
OpenVPN รันฮุกภายในกระบวนการเริ่มทำงานของตัวเอง สคริปต์ที่ต้องการทันเนลจึงต้องรอทันเนลที่กำลังรอสคริปต์นั้นอยู่ แยกงานออกไปและเลือกฮุกตามเหตุการณ์
สคริปต์ฮุกจะไปถึงอีกฝั่งของทันเนลได้หรือไม่ ขึ้นอยู่กับสามเงื่อนไข คือ ฮุกนั้นผูกกับเหตุการณ์ใด OpenVPN ยังอยู่ในลำดับการเริ่มทำงานหรือไม่เมื่อสคริปต์รัน และงานถูกแยกออกจากลำดับนั้นแล้วหรือยัง สคริปต์ส่วนใหญ่ล้มเหลวที่เงื่อนไขที่สาม การเพิ่มการหน่วงเวลาถือว่าปัญหาวงจรชีวิตเป็นปัญหาด้านเวลา และมันได้ผลบ่อยพอที่จะทำให้เข้าใจผิด
เลือกเหตุการณ์ก่อน จากนั้นเลือกกลไกการเลื่อนงาน แล้วจึงเลือกสถานที่ที่งานที่เลื่อนไว้จะรัน การตัดสินใจสามข้อนี้เป็นอิสระต่อกัน และการผสมกันคือสิ่งที่ทำให้ฮุกสองบรรทัดกลายเป็นทั้งบ่าย
ทำไมฮุกจึงต้องรอทันเนลที่กำลังรอฮุกนั้น
OpenVPNเรียกฮุกของตัวเองแบบซิงโครนัส ภายในโปรเซสเดียวกับที่ย้ายแพ็กเก็ต up ทำงานเมื่อมีอุปกรณ์ tun หรือ tap อยู่แล้ว แต่ก่อนที่จะมีการเพิ่มเส้นทางใด ๆ และ route-up ทำงานหลังเส้นทาง ทั้งสองจังหวะไม่ได้สัญญาว่าอีกฝั่งจะตอบ ตราบใดที่สคริปต์ของฮุกใดฮุกหนึ่งยังทำงานอยู่ เดมอนจะไม่ส่งต่อ คำสั่งที่คาดว่าทราฟฟิกจะผ่านทันเนลจึงรอทันเนลที่กำลังรอคำสั่งนั้นอยู่ การรอแบบวงกลมนี้คือสาเหตุที่การรันด้วยมือผ่านได้แต่ฮุกหมดเวลา
การแก้ปัญหาแบบตรงไปตรงมา คือการหน่วงเวลาสองสามวินาที บางครั้งได้ผลเพราะปลายทางฝั่งไกลขึ้นมาเอง ไม่ใช่เพราะการหน่วงเวลาแก้อะไร ตัวช่วยที่คอยถามหาความพร้อมคือคำตอบอีกแบบ ฮุกคืนค่า การส่งต่อเริ่มทำงาน และตัวช่วยได้สิ่งที่ต้องการเมื่อมันอยู่ที่นั่นจริง ๆ
เลือกฮุกตามเหตุการณ์ ไม่ใช่ตามความสะดวก
ผูก up กับงานที่ต้องการแค่อุปกรณ์ route-up กับงานที่ต้องการเส้นทาง และเก็บ client-connect กับ client-disconnectไว้สำหรับงานรายไคลเอนต์ฝั่งเซิร์ฟเวอร์ การทำความสะอาดเมื่อตัดการเชื่อมต่อต้องทนทานต่อการรันสองครั้ง เพราะการเชื่อมต่อใหม่ผ่านอินเทอร์เฟซแบบถาวรไม่ได้ทำตามลำดับเดียวกับการรีสตาร์ตเต็มรูปแบบ บนเซิร์ฟเวอร์ client-connect ทำงานเมื่อไคลเอนต์พิสูจน์ตัวตน ไม่ใช่เมื่อเดมอนเริ่มทำงาน ซึ่งควรยืนยันก่อนย้ายตรรกะไปที่นั่น หากงานเป็นของวงจรชีวิตของบริการไม่ใช่ของการเชื่อมต่อครั้งเดียว หน่วย systemd คือบ้านที่ดีกว่าฮุกใด ๆ
เดมอนเรียกคำสั่งของฮุกโดยตรง โดยไม่มีเชลล์คั่นกลาง ifconfig_remote ในบรรทัด up จึงมาเป็นข้อความตามตัวอักษร ห่อคำสั่งด้วยเชลล์ที่ชัดเจน หรือเก็บตรรกะไว้ในสคริปต์ที่อ่านสภาพแวดล้อม ipchange เป็นไดเรกทีฟหนึ่งตัวที่เพิ่มอาร์กิวเมนต์ของตัวเอง มันส่งที่อยู่และพอร์ตให้คำสั่ง สคริปต์ที่เขียนไว้โดยไม่คาดหวังจึงเห็นสองอย่างที่ไม่คาดคิด
สามวิธีในการแยกงานออกไป
ทางเลือกแรกคือส่งตัวช่วยไปทำงานเบื้องหลังจากภายในฮุก เริ่มมันด้วย setsid หรือ systemd-run --scope ปิดตัวอธิบายที่สืบทอดมา แล้วออกทันทีด้วยศูนย์ วิธีที่สองคือให้ฮุกเขียนไฟล์สถานะเล็ก ๆ และให้ตัวจับเวลาของ systemd หรือหน่วยเส้นทางลงมือกับมัน ซึ่งทำให้ฮุกเรียบง่ายและวางนโยบายการลองใหม่ไว้ที่ที่มีบันทึก วิธีที่สามคือทิ้งฮุกทั้งหมดและย้ายงานไปยังหน่วยที่จัดลำดับหลังบริการ VPN ซึ่งเป็นคำตอบที่ถูกต้องเมื่องานไม่ได้เป็นรายการเชื่อมต่อเลย ทั้งสามวิธีใช้กฎเดียวกัน ฮุกต้องคืนค่าก่อนที่มันจะต้องการทันเนล
ไม่ว่าจะเลือกแบบใด ทำให้ตัวช่วยทำงานซ้ำได้อย่างปลอดภัยและสังเกตได้ การเรียกสองครั้งที่ทับซ้อนกันไม่ควรทำการกระทำที่ย้อนกลับไม่ได้เดียวกันสองครั้ง และตัวช่วยที่ล้มเหลวต้องทิ้งอะไรบางอย่างที่มนุษย์อ่านได้
แยกความล้มเหลวของแซนด์บ็อกซ์ออกจากความล้มเหลวของวงจรชีวิต
เมื่อสคริปต์รันจากพรอมป์ root และล้มเหลวในฐานะบริการ สาเหตุมักเป็นแซนด์บ็อกซ์ ไม่ใช่เครือข่าย ProtectHome ที่จำกัด ProtectSystem ที่อ่านได้อย่างเดียว หรือไดเรกทอรีทำงานที่ไม่มีอยู่อาจทำให้สคริปต์หรือข้อมูลของมันเข้าถึงไม่ได้ และบนโฮสต์ SELinux ไฟล์ยังต้องมีบริบทที่อนุญาตให้เรียกใช้ อ่านยูนิตด้วย systemctl cat openvpn-server@server และบันทึกด้วย journalctl -u openvpn-server@server -n 80 แล้วมองหาข้อผิดพลาดด้านสิทธิ์หรือบริบท แทนการหมดเวลาของการเชื่อมต่อ การปิดใช้งานแซนด์บ็อกซ์ควรเป็นก้าวสุดท้าย ไม่ใช่ก้าวแรก
ความล้มเหลวของวงจรชีวิตดูแตกต่างออกไป ฮุกเริ่มทำงาน ตัวช่วยรัน และซ็อกเก็ตก็ยังไม่อยู่ตรงนั้น เขียนบรรทัดที่มีการประทับเวลาจากฮุกเอง ตัดการเชื่อมต่อแล้วเชื่อมต่อใหม่ และอ่านเหตุการณ์ตามลำดับ เมื่อตัวช่วยต้องการทันเนลจริง ๆ ให้มันถามหาความพร้อมแทนการนอนหลับตามระยะเวลาคงที่
สามฟิลด์ทำให้รายงานครั้งถัดไปมีประโยชน์ ฮุกใดทำงาน ใช้บัญชีใด และหยุดที่ไหน หากไม่มีสิ่งเหล่านี้ การสอบสวนครั้งต่อ ๆ ไปจะเริ่มจากศูนย์