OpenVPN با شکایت errno 2 دربارهٔ /dev/net/tun متوقف می‌شود، در حالی که lsmod ماژول tun را با خیال راحت فهرست می‌کند. هر دو واقعیت همزمان درست‌اند. ماژول یک درایور فراهم می‌کند، و مسیری که کلاینت باز می‌کند یک گره دستگاه است که چیز دیگری باید آن را بسازد. رفتار کردن با این دو به‌عنوان یک شیء همان دلیلی است که تعمیر بدیهیِ بارگذاری دوبارهٔ ماژول هیچ چیز را تغییر نمی‌دهد.

دو شیء، دو مالک متفاوت

ماژول هسته و گره دستگاه دو شیء مستقل با چرخه‌های حیات مستقل هستند. بارگذاری ماژول یک درایور ثبت می‌کند، و در سیستمی با devtmpfs هسته درخواست می‌کند که گره متناظر ساخته شود. در سیستمی بدون devtmpfs هیچ چیز چیزی نمی‌سازد، و درایور آنجا می‌ماند بدون هیچ مسیری که به آن اشاره کند. ماژول می‌تواند همزمان بارگذاری‌شده، سالم و از طریق سیستم فایل کاملاً دست‌نیافتنی باشد.

به همین دلیل متن خطا و فهرست ماژول‌ها با هم تناقض ندارند. کلاینت مسیری را باز می‌کند، مسیر وجود ندارد، و هسته پیش از آنکه درایور tun اصلاً مورد پرسش قرار گیرد ENOENT برمی‌گرداند. بارگذاری دوبارهٔ ماژول نمی‌تواند مسیر گمشده را تعمیر کند، و ساختن مسیر نمی‌تواند درایور گمشده را تعمیر کند.

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 درست است، آیا گزینه‌های سوار کردن پاک هستند، آیا زمینهٔ فرایند توانایی را دارد، و آیا در زمان شکست رد سیاست وجود دارد. عیبی که از هر پنج سالم بیرون بیاید مشکل دستگاه نیست، هر چه پیام بگوید.

مقالات مرتبط