/dev/net/tun saknas trots att tun är laddad: reparera enhetsnoden
En laddad tun-modul bevisar inte att enhetsnoden finns. Separera drivrutinen från noden, bygg upp den igen i rätt ordning och verifiera med ett gränssnitt.
OpenVPN stannar med ett errno 2-klagomål om /dev/net/tun medan lsmod lugnt listar tun-modulen. Båda fakta är sanna samtidigt. Modulen tillhandahåller en drivrutin, och sökvägen som klienten öppnar är en enhetsnod som något annat måste skapa. Att behandla dem som ett objekt är anledningen till att den uppenbara reparationen att läsa in modulen igen inte ändrar någonting alls.
Två objekt, två olika ägare
En kärnmodul och en enhetsnod är oberoende objekt med oberoende livscykler. Inläsning av modulen registrerar en drivrutin, och på ett system med devtmpfs ber kärnan att motsvarande nod skapas. På ett system utan devtmpfs skapar ingenting någonting, och drivrutinen sitter där utan någon sökväg som pekar på den. Modulen kan vara laddad, frisk och helt oåtkomlig via filsystemet i samma ögonblick.
Därför motsäger inte feltexten och modullistan varandra. Klienten öppnar en sökväg, sökvägen finns inte, och kärnan returnerar ENOENT innan tun-drivrutinen ens tillfrågas. Att läsa in modulen igen kan inte reparera en saknad sökväg, och att skapa sökvägen kan inte reparera en saknad drivrutin.
lsmod | grep -w tun
stat -c "%F %t:%T" /dev/net/tun
grep " /dev " /proc/mounts
devtmpfs kontra en explicit mknod
Normalt skapar två mekanismer den noden, och att veta vilken som borde vara ansvarig berättar vad du ska reparera. devtmpfs låter kärnan skapa enhetsnoder i en monterad instans, och udev lägger ägarskap och behörigheter ovanpå. En explicit mknod är den manuella vägen för avbildningar och namnområden där ingen av dem körs vid start.
Jämför resultat i stället för avsikter. Om devtmpfs är monterat på /dev och noden fortfarande saknas är drivrutinen inte den du antar, eller något tog bort noden efter start. Om /dev är en vanlig katalog i ett uppackat rootfs finns ingen automatisk mekanism, och den som bygger avbildningen måste skapa noden.
stat -c "%F major=%t minor=%T" /dev/net/tun
# expect: character special file major=a minor=c8
Där ingen av mekanismerna körs måste noden göras för hand, och ordningen spelar roll. Skapa först den överordnade katalogen, eftersom mknod inte kan hitta på den, sedan teckenhet, sedan ett läge som lämnar den skrivbar bara för root. Läs in modulen igen innan du skapar noden, och ta bara bort en befintlig sökväg efter att ls har visat att det är en katalog och inte en enhet.
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
Den snabbaste skillnaden är den typ som stat rapporterar, och en katalog på den sökvägen är den vanligaste överraskningen i minimala avbildningar. En välmenande mkdir i en Dockerfile lämnar precis det, och ingen mängd modulinläsning ersätter en katalog med en teckenhet. Major 10 med minor 200 är det registrerade paret för tun, uttryckt hexadecimalt som a och c8.
Fyra spår, fyra orsaker
Fyra orsaker förklarar nästan varje fall, och var och en lämnar ett annat spår i loggarna. Börja med startmiljön. Diskfria avbildningar, inbyggda kort och initramfs-faser monterar ofta varken devtmpfs eller en udev-styrd /dev, så ingen process skriver någonsin noden. Läs sedan monteringsalternativen på det som är värd för /dev, eftersom nodev tillåter att sökvägen finns men vägrar öppna den, och en bind-montering hämtad från en härdad värd bär flaggan utan att annonsera det. En skrivskyddad montering misslyckas tidigare och högre: mknod returnerar EROFS där, medan EPERM betyder att processen saknar förmågan och inte att filsystemet skyddar sig självt. Slutligen behöver en containerruntime med egen /dev tmpfs att noden injiceras i sitt eget namnområde, och dess enhets-cgroup måste tillåta paret major och minor.
Kontrollera policylagret innan du ändrar behörigheter igen. SELinux och AppArmor kan neka en öppning som varje filattribut tillåter, och den nekningen syns i granskningsloggen i stället för i klientens utdata. En behörighetsändring som aldrig syns i en nekning är tid som lagts på helt fel lager.
sudo journalctl -k -n 20 --no-pager
sudo ausearch -m AVC -ts recent | tail -5
Bevisa det med ett gränssnitt, inte med en lista
En lyckad lista bevisar att en sökväg finns och inget mer. Öppna en frisk tun-nod för läsning och anropet kommer tillbaka med ett fel, eftersom enheten inte har lässtemantik och inte är en vanlig fil. Det felet är här framgångssignalen, och att känna igen det hindrar dig från att jaga det som ett fel.
head -c 1 /dev/net/tun
head: error reading '/dev/net/tun': File descriptor in bad state
Det avgörande testet skapar ett gränssnitt, eftersom det prövar drivrutinen, noden och dina privilegier i ett enda steg. Om tillägget lyckas och länken visas fungerar alla tre. När det misslyckas skiljer errno orsakerna: ENOENT betyder att det inte finns någon användbar nod, EPERM pekar på förmåga, monteringsflaggor eller policy, och EBUSY betyder att en annan process redan äger gränssnittet.
sudo ip tuntap add dev tun7 mode tun
ip -brief link show tun7
sudo ip tuntap del dev tun7 mode tun
Kör det testet i den kontext som faktiskt misslyckas, eftersom en tjänsteenhet, en container eller ett schemalagt jobb kan bära en annan förmågeuppsättning än ditt inloggningsskal. Ett test som går igenom i fel kontext bevisar ingenting om det som misslyckas.
När enheten inte är problemet
Om gränssnittstestet lyckas och klienten fortfarande misslyckas är det återstående problemet profilen, tjänstekontexten eller rutten, och enheten är i ordning. Det är ögonblicket att sluta redigera enhetsnoder, eftersom fler ändringar bara ökar avståndet till ett fungerande system.
Kör samma fem kontroller varje gång i stället för att hitta på en ny: är modulen laddad utan en konkurrerande modul, är sökvägen en teckenhet med rätt major och minor, är monteringsalternativen rena, bär processkontexten förmågan, och finns det en policynekning vid tidpunkten för felet. Ett fel som överlever alla fem är inte ett enhetsproblem, vad meddelandet än nämner.