OpenVPN se zaustavlja uz prijavu errno 2 o /dev/net/tun dok lsmod mirno navodi tun modul. Obe činjenice su istovremeno tačne. Modul pruža drajver, a putanja koju klijent otvara je čvor uređaja koji mora stvoriti nešto drugo. Tretiranje toga kao jednog objekta razlog je zašto očigledna popravka ponovnim učitavanjem modula ne menja baš ništa.

Dva objekta, dva različita vlasnika

Kernel modul i čvor uređaja su nezavisni objekti sa nezavisnim životnim ciklusima. Učitavanje modula registruje drajver, a na sistemu sa devtmpfs kernel traži da se odgovarajući čvor stvori. Na sistemu bez devtmpfs ništa ništa ne stvara, a drajver stoji tamo bez putanje koja pokazuje na njega. Modul može biti učitan, zdrav i potpuno nedostupan kroz datotečni sistem u istom trenutku.

Zato se tekst greške i spisak modula ne protivreče. Klijent otvara putanju, putanja ne postoji, a kernel vraća ENOENT pre nego što se tun drajver uopšte konsultuje. Ponovno učitavanje modula ne može popraviti putanju koja nedostaje, a stvaranje putanje ne može popraviti drajver koji nedostaje.

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

devtmpfs protiv eksplicitnog mknod

Taj čvor obično stvaraju dva mehanizma, a znanje koji bi trebalo da bude odgovoran kaže vam šta popraviti. devtmpfs pušta kernel da stvara čvorove uređaja u montiranoj instanci, a udev na njih primenjuje vlasništvo i dozvole. Eksplicitni mknod je ručna putanja za slike i imenske prostore u kojima nijedan od njih ne radi pri podizanju.

Uporedite ishode, a ne namere. Ako je devtmpfs montiran na /dev i čvor još uvek nedostaje, drajver nije onaj koji pretpostavljate, ili je nešto uklonilo čvor nakon podizanja. Ako je /dev običan direktorijum unutar raspakovane rootfs, nema automatskog mehanizma i onaj ko gradi sliku mora stvoriti čvor.

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

Gde nijedan mehanizam ne radi, čvor se mora napraviti ručno, a redosled je važan. Prvo stvorite roditeljski direktorijum, jer mknod ga ne može izmisliti, zatim znakovni uređaj, zatim režim rada koji ga ostavlja zapisivim samo za root. Ponovo učitajte modul pre nego što stvorite čvor i uklonite postojeću putanju tek nakon što je ls pokazao da je to direktorijum, a ne uređaj.

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

Tip koji prijavljuje stat najbrži je razlikovni znak, a direktorijum na toj putanji najčešće je iznenađenje u minimalnim slikama. Dobronamerni mkdir u Dockerfileu ostavlja upravo to, i nikakvo učitavanje modula ne zamenjuje direktorijum znakovnim uređajem. Major 10 sa minor 200 registrovan je par za tun, izražen heksadecimalno kao a i c8.

Četiri traga, četiri uzroka

Četiri uzroka objašnjavaju gotovo svaki slučaj, a svaki ostavlja drugačiji trag u zapisima. Počnite sa okruženjem za podizanje. Slike bez diska, ugrađene ploče i faze initramfs često ne montiraju ni devtmpfs ni /devkojim upravlja udev, pa nijedan proces nikada ne zapisuje čvor. Zatim pročitajte opcije montiranja onoga što hostuje /dev, jer nodev dopušta da putanja postoji dok odbija da je otvori, a bind montiranje uzeto sa očvrslog hosta nosi tu zastavicu a da se ne najavi. Montiranje samo za čitanje pada ranije i glasnije: mknod tamo vraća EROFS, dok EPERM znači da procesu nedostaje sposobnost, a ne da se datotečni sistem štiti. Na kraju, kontejnerski runtime sa sopstvenim /dev tmpfs zahteva da čvor bude ubrizgan u njegov vlastiti imenski prostor, a njegova cgroup uređaja mora dopustiti par major i minor.

Proverite sloj politike pre nego što ponovo menjate dozvole. SELinux i AppArmor mogu odbiti otvaranje koje svaki atribut datoteke dopušta, a to odbijanje pojavljuje se u revizijskom zapisu, a ne u izlazu klijenta. Promena dozvole koja se nikada ne pojavi u odbijanju vreme je potrošeno na potpuno pogrešan sloj.

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

Dokažite to interfejsom, ne spiskom

Uspešan spisak dokazuje da putanja postoji i ništa više. Otvorite zdrav tun čvor za čitanje i poziv se vraća sa greškom, jer uređaj nema semantiku čitanja i nije obična datoteka. Ta greška je ovde signal uspeha, a prepoznavanje vas sprečava da je lovite kao kvar.

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

Odlučujući test stvara interfejs, jer vežba drajver, čvor i vaše privilegije u jednom koraku. Ako dodavanje uspe i link se pojavi, sva tri rade. Kada ne uspe, errno razdvaja uzroke: ENOENT znači da nema upotrebljivog čvora, EPERM upućuje na sposobnost, zastavice montiranja ili politiku, a EBUSY znači da interfejs već poseduje drugi proces.

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

Pokrenite taj test u kontekstu koji zaista pada, jer servisna jedinica, kontejner ili zakazani posao mogu nositi drugačiji skup sposobnosti od vaše prijavne ljuske. Test koji prođe u pogrešnom kontekstu ne dokazuje ništa o onom koji pada.

Kada uređaj nije problem

Ako test interfejsa uspe, a klijent još uvek pada, preostali problem je profil, kontekst servisa ili ruta, a uređaj je u redu. To je trenutak da prestanete uređivati čvorove uređaja, jer dalje promene samo dodaju udaljenost od radnog sistema.

Pokrenite istih pet provera svaki put umesto da izmišljate novu: je li modul učitan bez suparničkog modula, je li putanja znakovni uređaj sa pravim major i minor, jesu li opcije montiranja čiste, nosi li kontekst procesa sposobnost i postoji li odbijanje politike na vremenskoj oznaci kvara. Kvar koji preživi svih pet nije problem uređaja, šta god poruka spominjala.

Povezani članci