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 значи да процесу недостаје способност, а не да се датотечни систем штити. На крају, контејнерски runtime са сопственим /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, јесу ли опције монтирања чисте, носи ли контекст процеса способност и постоји ли одбијање политике на временској ознаци квара. Квар који преживи свих пет није проблем уређаја, шта год порука спомињала.

Повезани чланци