OpenVPN se oprește cu o plângere errno 2 despre /dev/net/tun în timp ce lsmod listează liniștit modulul tun. Ambele fapte sunt adevărate în același timp. Modulul oferă un driver, iar calea pe care o deschide clientul este un nod de dispozitiv pe care altceva trebuie să îl creeze. Tratarea lor ca un singur obiect este motivul pentru care reparația evidentă de reîncărcare a modulului nu schimbă absolut nimic.

Două obiecte, doi proprietari diferiți

Un modul de kernel și un nod de dispozitiv sunt obiecte independente cu cicluri de viață independente. Încărcarea modulului înregistrează un driver, iar pe un sistem cu devtmpfs kernelul cere crearea nodului corespunzător. Pe un sistem fără devtmpfs nimic nu creează nimic, iar driverul stă acolo fără nicio cale care să arate spre el. Modulul poate fi încărcat, sănătos și complet inaccesibil prin sistemul de fișiere în același moment.

De aceea textul erorii și lista de module nu se contrazic. Clientul deschide o cale, calea nu există, iar kernelul returnează ENOENT înainte ca driverul tun să fie măcar întrebat. Reîncărcarea modulului nu poate repara o cale lipsă, iar crearea căii nu poate repara un driver lipsă.

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

devtmpfs față de un mknod explicit

De obicei două mecanisme creează acel nod, iar a ști care ar trebui să fie responsabil îți spune ce să repari. devtmpfs lasă kernelul să creeze noduri de dispozitiv într-o instanță montată, iar udev aplică deasupra proprietatea și permisiunile. Un mknod explicit este calea manuală pentru imagini și spații de nume în care niciunul dintre ele nu rulează la pornire.

Compară rezultatele, nu intențiile. Dacă devtmpfs este montat pe /dev și nodul încă lipsește, driverul nu este cel pe care îl presupui, sau ceva a eliminat nodul după pornire. Dacă /dev este un director obișnuit într-un rootfs despachetat, nu există niciun mecanism automat și cel care construiește imaginea trebuie să creeze nodul.

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

Acolo unde nu rulează niciunul dintre mecanisme, nodul trebuie făcut manual, iar ordinea contează. Creează mai întâi directorul părinte, pentru că mknod nu îl poate inventa, apoi dispozitivul de caractere, apoi un mod care îl lasă scriibil doar pentru root. Reîncarcă modulul înainte de a crea nodul și elimină o cale existentă doar după ce ls a arătat că este un director, nu un dispozitiv.

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

Cel mai rapid discriminant este tipul raportat de stat , iar un director pe acea cale este cea mai frecventă surpriză în imaginile minime. Un mkdir bine intenționat într-un Dockerfile lasă exact asta, iar nicio cantitate de încărcare de module nu înlocuiește un director cu un dispozitiv de caractere. Major 10 cu minor 200 este perechea înregistrată pentru tun, exprimată hexazecimal ca a și c8.

Patru urme, patru cauze

Patru cauze explică aproape fiecare caz, iar fiecare lasă o urmă diferită în jurnale. Începe cu mediul de pornire. Imaginile fără disc, plăcile încorporate și etapele initramfs adesea nu montează nici devtmpfs, nici un /devadministrat de udev, așa că niciun proces nu scrie vreodată nodul. Apoi citește opțiunile de montare ale oricui găzduiește /dev, pentru că nodev permite ca calea să existe dar refuză să o deschidă, iar o montare bind luată de pe un host întărit poartă steagul fără să se anunțe. O montare doar-citire eșuează mai devreme și mai tare: mknod returnează acolo EROFS, în timp ce EPERM înseamnă că procesului îi lipsește capacitatea, nu că sistemul de fișiere se protejează. În final, un runtime de containere cu propriul /dev tmpfs are nevoie ca nodul să fie injectat în propriul spațiu de nume, iar cgroupul său de dispozitive trebuie să permită perechea major și minor.

Verifică stratul de politică înainte de a schimba din nou permisiuni. SELinux și AppArmor pot refuza o deschidere pe care toate atributele fișierului o permit, iar acel refuz apare în jurnalul de audit, nu în ieșirea clientului. O schimbare de permisiuni care nu apare niciodată într-un refuz este timp petrecut pe stratul complet greșit.

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

Dovedește-o cu o interfață, nu cu o listare

O listare reușită dovedește că o cale există și nimic mai mult. Deschide un nod tun sănătos pentru citire și apelul revine cu o eroare, pentru că dispozitivul nu are semantică de citire și nu este un fișier obișnuit. Acea eroare este aici semnalul de succes, iar recunoașterea ei te oprește din a o urmări ca pe o defecțiune.

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

Testul decisiv creează o interfață, pentru că exersează driverul, nodul și privilegiile tale într-un singur pas. Dacă adăugarea reușește și legătura apare, toate trei funcționează. Când eșuează, errno separă cauzele: ENOENT înseamnă că nu există niciun nod utilizabil, EPERM indică capacitatea, steagurile de montare sau politica, iar EBUSY înseamnă că un alt proces deține deja interfața.

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

Rulează acel test în contextul care eșuează cu adevărat, pentru că o unitate de serviciu, un container sau o sarcină programată pot purta un set de capacități diferit de cel al shellului tău de conectare. Un test care trece în contextul greșit nu dovedește nimic despre cel care eșuează.

Când dispozitivul nu este problema

Dacă testul de interfață reușește și clientul tot eșuează, problema rămasă este profilul, contextul serviciului sau ruta, iar dispozitivul este în regulă. Acesta este momentul să te oprești din editarea nodurilor de dispozitiv, pentru că modificările suplimentare adaugă doar distanță față de un sistem funcțional.

Rulează aceleași cinci verificări de fiecare dată în loc să inventezi una nouă: modulul este încărcat fără un modul rival, calea este un dispozitiv de caractere cu major și minor corecte, opțiunile de montare sunt curate, contextul procesului poartă capacitatea, și există un refuz de politică la momentul defecțiunii. O defecțiune care supraviețuiește tuturor cinci nu este o problemă de dispozitiv, orice ar menționa mesajul.

Articole similare