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

ตรวจพาธก่อนตรวจสิทธิ์ เพราะข้อผิดพลาดเรื่องสิทธิ์บนไฟล์ผิดก็ยังเป็นไฟล์ผิดอยู่ดี

พาธแบบสัมพัทธ์ถูกแก้เทียบกับโปรเซส ไม่ใช่ไฟล์

เมื่อสคริปต์เรียก open("policy.json"), เคอร์เนลจะแก้ชื่อนั้นเทียบกับไดเรกทอรีทำงานปัจจุบันของโปรเซส ไม่มีอะไรจากตำแหน่งของสคริปต์เองเข้าไปร่วมในการค้นหานั้น OpenVPN เริ่มฮุกจากไดเรกทอรีที่มันเลือกเอง และตัวเลือก cd เปลี่ยนไดเรกทอรีนั้นได้ สคริปต์ที่ทดสอบด้วยมือจากโฟลเดอร์ของตัวเองจึงทำงานต่างออกไปเมื่อเดมอนเรียกมัน การทดสอบด้วยมือผ่าน การรันบริการล้มเหลว และบรรทัดในล็อกเอ่ยถึงเพียงชื่อที่หายไป

Python เปิดเผยตำแหน่งของสคริปต์เป็น __file__ และนั่นคือสิ่งที่ทำให้การยึดทำได้ Path(__file__).resolve().parent / "policy.json" สร้างพาธจากไฟล์แทนที่จะจากโปรเซส บรรทัดเดียวนั้นตัดการพึ่งพาไดเรกทอรีทำงานออกโดยไม่เปลี่ยนอะไรในสิทธิ์

ยึดพาธอย่างตั้งใจ แล้วตรวจว่าคำสั่ง resolve ทำอะไร

resolve() ตามลิงก์สัญลักษณ์ การติดตั้งที่ลิงก์ฮุกเข้าที่จึงยึดที่ปลายทางของลิงก์ ไม่ใช่ที่ไดเรกทอรีที่ถือลิงก์ไว้ โดยปกติแล้วนั่นคือสิ่งที่ต้องการสำหรับการติดตั้งที่มีเวอร์ชัน และเป็นสิ่งที่ผิดสำหรับเลย์เอาต์ที่คาดว่าข้อมูลจะอยู่ข้างลิงก์สัญลักษณ์ ตัดสินใจว่าการติดตั้งหมายถึงแบบไหน แล้วเขียนโค้ดให้ตรงกับมันแทนที่จะไปค้นพบความต่างในโปรดักชัน Path(__file__).parent โดยไม่มี resolve() เก็บไดเรกทอรีของลิงก์เองไว้

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

ข้ามสี่พรมแดนตามลำดับ

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

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

แก้การค้นหาโดยไม่ขยายบริการ

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

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

บันทึกหลักฐานที่ระบุชื่อพรมแดน

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

ข้ออ้างว่า root อ่านไฟล์จากเทอร์มินัลได้ตอบคำถามนั้นเพียงข้อเดียวและไม่ตอบข้ออื่นเลย จงทดสอบจากบริบทของบริการเองแทน

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