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, ні керований udev /dev, тож жоден процес ніколи не записує вузол. Далі прочитайте параметри монтування того, що містить /dev, бо nodev дозволяє шляху існувати, але відмовляє в його відкритті, а bind-монтування, узяте з укріпленого хоста, несе цей прапорець, не оголошуючи себе. Монтування лише для читання падає раніше й голосніше: mknod повертає там EROFS, тоді як EPERM означає, що процесу бракує можливості, а не те, що файлова система захищає себе. Нарешті, контейнерному runtime із власним /dev tmpfs потрібно, щоб вузол було впроваджено в його власний простір імен, а його device 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, чи чистими є параметри монтування, чи несе контекст процесу потрібну можливість і чи є відмова політики на позначці часу збою. Несправність, що переживає всі п'ять, не є проблемою пристрою, хоч би що згадувало повідомлення.

Схожі статті