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, ці чыстыя параметры мантавання, ці нясе кантэкст працэсу магчымасць і ці ёсць адмова палітыкі на адзнацы часу збою. Няспраўнасць, якая перажывае ўсе пяць, не з'яўляецца праблемай прылады, што б ні згадвала паведамленне.

Падобныя артыкулы