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

นี่คือความล้มเหลวแบบใดในสามแบบ?

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

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

มุมมองระบบไฟล์ของยูนิตคือผู้ต้องหาปกติเมื่อมีเพียงบริการที่ล้มเหลว

systemctl show -p ProtectHome -p ProtectSystem -p ReadWritePaths [email protected]
ls -l /var/lib/vpn-otp/*/.google_authenticator 2>/dev/null | head -n 20

ตัวเลือก A: ย้ายความลับออกจากบ้าน

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

นี่คือตัวเลือกที่เรียบร้อย และเป็นตัวเลือกที่ต้องระวังที่สุด เพราะที่เก็บที่ใช้ร่วมกันทำให้ไดเรกทอรีเดียวเป็นขอบเขตความปลอดภัยของทุกบัญชี

auth required pam_google_authenticator.so secret=/var/lib/vpn-otp/${USER}/.google_authenticator user=vpn-otp

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

ตัวเลือก B: เก็บไฟล์ไว้และเปิดพาธ

หากความลับต้องอยู่ในไดเรกทอรีบ้านของผู้ใช้ ยูนิตต้องมีมุมมองที่รวมไดเรกทอรีนั้นเพียงแห่งเดียวโดยไม่เปิดเผยทุกบ้านบนโฮสต์ drop-in สามารถเพิ่ม bind mount ที่จำกัดขอบเขตแคบ ๆ ภายใต้บ้านที่ถูกแซนด์บ็อกซ์

วิธีนี้รักษาความเป็นเจ้าของต่อผู้ใช้เป็นโมเดล โดยแลกกับการเปลี่ยนแปลงยูนิตที่ต้องตรวจทานทุกครั้งที่แซนด์บ็อกซ์เปลี่ยน

สองขีดจำกัดที่มองข้ามได้ง่าย พาธแบบอ่านอย่างเดียวที่เพิ่มภายใต้ไดเรกทอรีที่แซนด์บ็อกซ์ซ่อนอยู่จะไม่เปิดเผย เพราะไดเรกทอรีแม่ไม่มีอยู่จริง และเมานต์ต้องเขียนได้หากโมดูลต้องอัปเดตสถานะ ซึ่งนำตัวเลือกถัดไปเข้ามาเกี่ยวข้อง เขียน drop-in เป็นไฟล์แยกแทนการแก้ไขยูนิตที่แพ็กเกจไว้ใต้ /usr/lib เพื่อให้การอัปเกรดแพ็กเกจไม่ย้อนกลับอย่างเงียบ ๆ

ตัวเลือก C: คงแซนด์บ็อกซ์ไว้และแก้การเขียนสถานะ

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

ความล้มเหลวนั้นดูเหมือนรหัสผิดในความพยายามครั้งถัดไปไม่ใช่ครั้งนี้ จึงถูกเข้าใจผิดว่าเป็นนาฬิกาคลาดเคลื่อน

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

ตรวจสอบในฐานะบริการ ไม่ใช่ในฐานะตัวคุณเอง

ทุกการตรวจสอบที่สำคัญต้องทำงานภายในแซนด์บ็อกซ์ของยูนิตด้วยตัวตนของยูนิต

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

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

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