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

ชื่อผู้ใช้ในสภาพแวดล้อมไม่ใช่สวิตช์ตัวตน

Unix ตัดสินว่าโปรเซสแตะต้องอะไรได้จาก ID ผู้ใช้ที่มีผลจริง ไม่ใช่จากสตริงในสภาพแวดล้อม การตั้ง USER=alice หรือการส่งออก LOGNAME เปลี่ยนสิ่งที่ซอฟต์แวร์ที่ประมาทพิมพ์ในเอาต์พุต แต่ไม่เปลี่ยนอะไรในการตรวจสอบความเป็นเจ้าของไฟล์ hook ที่ทำงานในฐานะ root พร้อมตัวแปรนั้นก็ยังเปิดไฟล์ของ Alice ในฐานะ root อยู่ดี

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

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

id -u alice
getent passwd alice | cut -d: -f1,3,6
stat -c '%U:%G %a %n' /home/alice/.local/state/vpn-status.json

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

เดมอนยังคงอยู่ที่เดิม

การเปลี่ยน User= ของบริการ VPN เองเป็น Alice คือคันโยกที่ผิด มันเปลี่ยนวิธีที่เดมอนผูกอินเทอร์เฟซ อ่านคีย์ เขียนไฟล์สถานะ และจัดการเส้นทาง และย้ายความเสี่ยงทั้งหมดนั้นไปยังหน่วยที่ทำงานได้ดี เดมอนต้องการสิทธิพิเศษของมัน การทำงานที่ตามมาไม่ต้องการ

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

systemctl show -p User -p Group -p ExecStart openvpn-server@server
systemctl cat openvpn-server@server

ในการติดตั้งแพ็กเกจแบบปกติ เดมอนเป็น root และสคริปต์ --up สืบทอดสิ่งนั้น ยืนยันแทนการสันนิษฐาน เพราะดิสทริบิวชันที่เสริมความปลอดภัยแล้วอาจย้ายมันไปยังบัญชีเฉพาะ และ drop-in ที่เขียนด้วยมืออาจเรียก hook ผ่านตัวห่อที่มีกฎต่างกัน

สามส่วนที่มีชื่อ ตัวกระตุ้นเดียวที่แคบ

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

การทำงานเองเป็นหน่วยบริการธรรมดา และตัวตนคือคำสั่งหนึ่งภายใน:

[Service]
Type=oneshot
User=alice
Group=alice
ExecStart=/usr/local/bin/refresh-vpn-status --slot %i

สังเกตว่า User=alice มีผลกับทั้งหน่วย และ systemd บันทึกข้อยกเว้นหนึ่งที่ควรรู้ คำสั่งที่มีคำนำหน้า + จะรันด้วยสิทธิพิเศษเต็มรูปแบบโดยไม่สนใจสิ่งนั้น คำนำหน้านั้นเป็นเครื่องมือที่ชอบด้วยกฎหมายสำหรับขั้นตอนที่มีสิทธิพิเศษเพียงขั้นเดียว และเป็นวิธีที่สะดวกในการยกเลิกการแยกที่คุณเพิ่งสร้างขึ้น ดังนั้นให้ถือ + ใด ๆ ในหน่วยนั้นเป็นสิ่งที่ต้องให้เหตุผลในการตรวจทาน

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

ตรวจสอบตัวตนที่รันจริง

การตรวจทานการกำหนดค่าไม่ใช่หลักฐาน หลังการเชื่อมต่อทดสอบที่ได้รับอนุมัติ ให้ยืนยัน UID ที่แตะไฟล์และ UID ที่เดมอนยังใช้อยู่:

stat -c '%U %G %y %n' /home/alice/.local/state/vpn-status.json
ps -o user=,pid=,cmd= -p "$(pgrep -o -f 'openvpn --daemon')"
systemd-run --uid=alice --pipe /usr/local/bin/refresh-vpn-status --dry-run

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

ถ้าเจ้าของไฟล์คือ Alice แต่กลุ่มหรือโหมดของมันเปลี่ยนไประหว่างการรัน ให้มองหา UMask= หรือความแตกต่างของสิทธิ์ไดเรกทอรีแทนข้อผิดพลาดทางตรรกะ ถ้าการประทับเวลาไม่ขยับเลย ตัวกระตุ้นไม่เคยทำงาน และขั้นตอนถัดไปที่มีประโยชน์คือ journalctl -u สำหรับหน่วยการทำงาน ไม่ใช่การเชื่อมต่อใหม่ของไคลเอนต์อีกครั้ง

สิ่งที่เหตุการณ์เชื่อมต่อยังพิสูจน์ไม่ได้

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

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

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

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