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

สองที่อยู่ สองข้อกล่าวอ้างที่ต่างกัน

ปลายทางภายนอกคือสิ่งที่เซิร์ฟเวอร์สังเกตเมื่อไคลเอนต์เชื่อมต่อ เข้าถึงได้ในชื่อ trusted_ip สำหรับ IPv4 และ trusted_ip6 สำหรับ IPv6 ที่อยู่เสมือนคือสิ่งที่พูลจัดสรรให้กับเซสชันนี้ เข้าถึงได้ในชื่อ ifconfig_pool_remote_ip

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

การอ่านขั้นต่ำมีหน้าตาเช่นนี้

$outer4 = getenv('trusted_ip');
$outer6 = getenv('trusted_ip6');
$inner  = getenv('ifconfig_pool_remote_ip');
$outer  = $outer4 !== false ? $outer4 : $outer6;

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

ตรวจสอบว่า PHP ถูกเริ่มอย่างไร ไม่ใช่เชลล์ของคุณ

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

เริ่มด้วยการยืนยันว่าฮุกทำงานจริงในขั้นตอนที่คุณคาดหวัง สคริปต์ที่ผูกกับขั้นตอนผิด หรือตัวที่ไม่เคยรันเพราะไดเรกทีฟชี้ไปยังเส้นทางอื่น ให้อาการเหมือนกันทุกประการกับตัวแปรที่หายไป เพิ่มบรรทัดเครื่องหมายโดยเจตนาไว้บนสุดของสคริปต์ และยืนยันว่ามันปรากฏก่อนที่คุณจะตรวจสอบสิ่งอื่น บรรทัดเดียวที่มีการประทับเวลาในบันทึกแยก «ไม่เคยรัน» ออกจาก «รันโดยไม่มีค่า»

จากนั้นยืนยันว่าการเรียกใช้นั้นใช้ตัวแปลภาษาและไฟล์กำหนดค่าใด การแก้ไขการกำหนดค่า PHP สำหรับเว็บเซิร์ฟเวอร์ไม่ได้เปลี่ยนอะไรกับกระบวนการ CLI ที่ OpenVPN เริ่ม และทั้งสองอาจโหลดไฟล์ php.ini ที่ต่างกันโดยสิ้นเชิง

ทำไม $_ENV ที่ว่างเปล่าจึงไม่ได้พิสูจน์อะไร

PHP เอกสารอ้างอิง getenv อธิบายการอ่านสภาพแวดล้อมของกระบวนการโดยตรง ซึ่งคืนค่า false เมื่อไม่มีชื่อนั้น ซูเปอร์โกลบอล $_ENV ถูกเติมแยกต่างหาก และไดเรกทีฟ variables_order ตัดสินว่าซูเปอร์โกลบอลใดจะถูกเติม

ถ้าไดเรกทีฟนั้นละเว้น E $_ENV จะยังคงว่างเปล่าไม่ว่าสภาพแวดล้อมจะมีอะไรก็ตาม โค้ดที่ทำดัชนีมันจะรายงานตัวแปรที่หายไปซึ่งจริงๆ แล้วมีอยู่ และการสอบสวนจะมุ่งไปที่ OpenVPN ขณะที่คำตอบอยู่ใน php.ini ตรวจสอบไดเรกทีฟสำหรับการกำหนดค่า CLI โดยเฉพาะ ไม่ใช่สำหรับเว็บเซิร์ฟเวอร์

การอ่านด้วย getenv หลีกเลี่ยงความสับสนทั้งหมดนี้ และสคริปต์ห่อที่ล้างสภาพแวดล้อมก่อนเรียกตัวแปลภาษาจะนำมันกลับมาในอีกรูปแบบหนึ่ง

เก็บความสัมพันธ์ ไม่ใช่การอัปเดตแถว

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

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

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

วินิจฉัยโดยไม่เทสภาพแวดล้อมทิ้ง

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

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

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