OpenVPN หยุดพร้อมข้อผิดพลาด errno 2 เกี่ยวกับ /dev/net/tun ในขณะที่ lsmod แสดงโมดูล tun อย่างสบาย ๆ ข้อเท็จจริงทั้งสองเป็นจริงในเวลาเดียวกัน โมดูลให้ไดรเวอร์ และเส้นทางที่ไคลเอนต์เปิดคือโหนดอุปกรณ์ที่สิ่งอื่นต้องสร้าง การมองทั้งสองเป็นวัตถุเดียวคือเหตุผลที่การซ่อมแซมที่เห็นได้ชัดคือการโหลดโมดูลใหม่ไม่เปลี่ยนอะไรเลย

สองวัตถุ สองเจ้าของที่ต่างกัน

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

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

lsmod | grep -w tun
stat -c "%F %t:%T" /dev/net/tun
grep " /dev " /proc/mounts

devtmpfs กับ mknod ที่ชัดเจน

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

เปรียบเทียบผลลัพธ์ไม่ใช่เจตนา ถ้า devtmpfs ถูกเมานต์ที่ /dev และโหนดยังหายอยู่ ไดรเวอร์ไม่ใช่อันที่คุณคิด หรือมีบางอย่างลบโหนดหลังบูต ถ้า /dev เป็นไดเรกทอรีธรรมดาใน rootfs ที่แตกออกมา ไม่มีกลไกอัตโนมัติ และคนที่สร้างอิมเมจต้องสร้างโหนด

stat -c "%F major=%t minor=%T" /dev/net/tun
# expect: character special file major=a minor=c8

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

sudo modprobe tun
sudo mkdir -p /dev/net
sudo mknod /dev/net/tun c 10 200
sudo chmod 600 /dev/net/tun
ls -l /dev/net/tun

ตัวแยกที่เร็วที่สุดคือชนิดที่ stat รายงาน และไดเรกทอรีบนเส้นทางนั้นคือความประหลาดใจที่พบบ่อยที่สุดในอิมเมจขนาดเล็ก mkdir ที่เจตนาดีใน Dockerfile ทิ้งไว้อย่างนั้น และการโหลดโมดูลไม่ว่าจะมากแค่ไหนก็ไม่แทนที่ไดเรกทอรีด้วยอุปกรณ์อักขระ Major 10 กับ minor 200 คือคู่ที่ลงทะเบียนสำหรับ tun แสดงเป็นเลขฐานสิบหกคือ a และ c8

สี่ร่องรอย สี่สาเหตุ

สี่สาเหตุอธิบายเกือบทุกกรณี และแต่ละอย่างทิ้งร่องรอยต่างกันในบันทึก เริ่มจากสภาพแวดล้อมการบูต อิมเมจที่ไม่มีดิสก์ บอร์ดฝังตัว และขั้น initramfs มักไม่เมานต์ทั้ง devtmpfs และ /devที่จัดการโดย udev ดังนั้นไม่มีโปรเซสใดเขียนโหนดเลย จากนั้นอ่านตัวเลือกการเมานต์ของสิ่งที่โฮสต์ /devเพราะ nodev อนุญาตให้เส้นทางมีอยู่แต่ปฏิเสธที่จะเปิดมัน และการเมานต์ bind ที่นำมาจากโฮสต์ที่เสริมความแข็งแกร่งก็พาธงนี้โดยไม่ประกาศตัว การเมานต์แบบอ่านอย่างเดียวล้มเหลวก่อนและดังกว่า: mknod คืน EROFS ที่นั่น ในขณะที่ EPERM หมายถึงโปรเซสขาดความสามารถ ไม่ใช่ระบบไฟล์ป้องกันตัวเอง สุดท้าย รันไทม์คอนเทนเนอร์ที่มี /dev tmpfs ของตัวเองต้องฉีดโหนดเข้าเนมสเปซของตัวเอง และ cgroup อุปกรณ์ของมันต้องอนุญาตคู่ major และ minor

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

sudo journalctl -k -n 20 --no-pager
sudo ausearch -m AVC -ts recent | tail -5

พิสูจน์ด้วยอินเทอร์เฟซ ไม่ใช่รายการ

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

head -c 1 /dev/net/tun
head: error reading '/dev/net/tun': File descriptor in bad state

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

sudo ip tuntap add dev tun7 mode tun
ip -brief link show tun7
sudo ip tuntap del dev tun7 mode tun

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

เมื่ออุปกรณ์ไม่ใช่ปัญหา

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

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

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