OpenVPN stopt met een errno 2-klacht over /dev/net/tun terwijl lsmod de tun-module rustig oplijst. Beide feiten zijn tegelijk waar. De module levert een driver, en het pad dat de client opent is een apparaatknooppunt dat iets anders moet aanmaken. Ze als één object behandelen is de reden dat de voor de hand liggende reparatie van het opnieuw laden van de module helemaal niets verandert.

Twee objecten, twee verschillende eigenaren

Een kernelmodule en een apparaatknooppunt zijn onafhankelijke objecten met onafhankelijke levenscycli. Het laden van de module registreert een driver, en op een systeem met devtmpfs vraagt de kernel om het bijbehorende knooppunt aan te maken. Op een systeem zonder devtmpfs maakt niets iets aan, en de driver zit daar zonder enig pad dat ernaar wijst. De module kan op hetzelfde moment geladen, gezond en volledig onbereikbaar via het bestandssysteem zijn.

Daarom spreken de fouttekst en de modullijst elkaar niet tegen. De client opent een pad, het pad bestaat niet, en de kernel geeft ENOENT terug voordat de tun-driver ooit wordt geraadpleegd. De module opnieuw laden kan een ontbrekend pad niet repareren, en het pad aanmaken kan een ontbrekende driver niet repareren.

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

devtmpfs tegenover een expliciete mknod

Normaal maken twee mechanismen dat knooppunt aan, en weten welke verantwoordelijk hoort te zijn vertelt u wat u moet repareren. devtmpfs laat de kernel apparaatknooppunten aanmaken in een aangekoppelde instantie, en udev past eigendom en rechten daarop toe. Een expliciete mknod is het handmatige pad voor images en naamruimten waar geen van beide bij het opstarten draait.

Vergelijk uitkomsten in plaats van bedoelingen. Als devtmpfs op /dev is aangekoppeld en het knooppunt nog steeds ontbreekt, is de driver niet degene die u aanneemt, of iets heeft het knooppunt na het opstarten verwijderd. Als /dev een gewone map is in een uitgepakte rootfs, bestaat er geen automatisch mechanisme en moet wie de image bouwt het knooppunt aanmaken.

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

Waar geen van beide mechanismen draait, moet het knooppunt met de hand worden gemaakt, en de volgorde is belangrijk. Maak eerst de bovenliggende map aan, omdat mknod die niet kan verzinnen, dan het tekenapparaat, dan een modus die het alleen voor root schrijfbaar laat. Laad de module opnieuw voordat u het knooppunt aanmaakt, en verwijder een bestaand pad pas nadat ls heeft laten zien dat het een map is en geen apparaat.

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

Het type dat stat rapporteert is de snelste discriminant, en een map op dat pad is de meest voorkomende verrassing in minimale images. Een goedbedoelde mkdir in een Dockerfile laat precies dat achter, en geen enkele hoeveelheid moduleladen vervangt een map door een tekenapparaat. Major 10 met minor 200 is het geregistreerde paar voor tun, hexadecimaal uitgedrukt als a en c8.

Vier sporen, vier oorzaken

Vier oorzaken verklaren bijna elk geval, en elk laat een ander spoor in de logboeken achter. Begin bij de opstartomgeving. Schijfloze images, ingebedde boards en initramfs-fasen koppelen vaak noch devtmpfs noch een door udev beheerd /devaan, zodat geen enkel proces ooit het knooppunt schrijft. Lees daarna de aankoppelopties van wat /devhuisvest, want nodev staat toe dat het pad bestaat maar weigert het te openen, en een bind-koppeling van een geharde host draagt de vlag zonder zich aan te kondigen. Een alleen-lezen koppeling mislukt eerder en luider: mknod geeft daar EROFS terug, terwijl EPERM betekent dat het proces de capaciteit mist en niet dat het bestandssysteem zichzelf beschermt. Ten slotte heeft een containerruntime met zijn eigen /dev tmpfs het knooppunt in zijn eigen naamruimte geïnjecteerd nodig, en zijn device-cgroup moet het major- en minor-paar toestaan.

Controleer de beleidslaag voordat u opnieuw rechten wijzigt. SELinux en AppArmor kunnen een opening weigeren die elk bestandskenmerk toestaat, en die weigering verschijnt in het auditlog in plaats van in de uitvoer van de client. Een rechtenwijziging die nooit in een weigering verschijnt, is tijd besteed aan de volledig verkeerde laag.

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

Bewijs het met een interface, niet met een lijst

Een geslaagde lijst bewijst dat een pad bestaat en niets meer. Open een gezond tun-knooppunt om te lezen en de aanroep komt terug met een fout, omdat het apparaat geen leessemantiek heeft en geen gewoon bestand is. Die fout is hier het successein, en hem herkennen weerhoudt u ervan hem als een storing te achtervolgen.

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

De beslissende test maakt een interface aan, omdat hij de driver, het knooppunt en uw rechten in één stap beproeft. Als het toevoegen slaagt en de link verschijnt, werken alle drie. Wanneer hij faalt, scheidt errno de oorzaken: ENOENT betekent dat er geen bruikbaar knooppunt is, EPERM wijst op capaciteit, aankoppelvlaggen of beleid, en EBUSY betekent dat een ander proces de interface al bezit.

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

Voer die test uit in de context die werkelijk faalt, omdat een service-eenheid, een container of een geplande taak een andere capaciteitenset kan dragen dan uw login-shell. Een test die in de verkeerde context slaagt, bewijst niets over degene die faalt.

Wanneer het apparaat niet het probleem is

Als de interfacetest slaagt en de client nog steeds faalt, is het resterende probleem het profiel, de servicecontext of de route, en het apparaat is in orde. Dat is het moment om te stoppen met het bewerken van apparaatknooppunten, omdat verdere wijzigingen alleen de afstand tot een werkend systeem vergroten.

Voer elke keer dezelfde vijf controles uit in plaats van een nieuwe te verzinnen: is de module geladen zonder een concurrerende module, is het pad een tekenapparaat met de juiste major en minor, zijn de aankoppelopties schoon, draagt de procescontext de capaciteit, en is er een beleidsweigering op het tijdstip van de storing. Een storing die alle vijf overleeft is geen apparaatprobleem, wat de melding ook noemt.

Gerelateerde artikelen