OpenVPN pysähtyy errno 2 -valitukseen kohteesta /dev/net/tun samalla kun lsmod luettelee tun-moduulin ongelmitta. Molemmat tosiasiat pitävät samanaikaisesti. Moduuli tarjoaa ajurin, ja polku, jonka asiakas avaa, on laitesolmu, jonka jonkin muun on luotava. Niiden kohteleminen yhtenä objektina on syy siihen, miksi ilmeinen korjaus eli moduulin uudelleenlataus ei muuta yhtään mitään.

Kaksi objektia, kaksi eri omistajaa

Ytimen moduuli ja laitesolmu ovat itsenäisiä objekteja, joilla on itsenäiset elinkaaret. Moduulin lataus rekisteröi ajurin, ja devtmpfs-järjestelmässä ydin pyytää vastaavan solmun luomista. Ilman devtmpfs:iä mikään ei luo mitään, ja ajuri istuu siellä ilman yhtään polkua, joka osoittaisi siihen. Moduuli voi olla ladattu, terve ja täysin tavoittamaton tiedostojärjestelmän kautta samalla hetkellä.

Siksi virheteksti ja moduuliluettelo eivät ole ristiriidassa. Asiakas avaa polun, polkua ei ole, ja ydin palauttaa ENOENTin ennen kuin tun-ajurilta edes kysytään. Moduulin uudelleenlataus ei voi korjata puuttuvaa polkua, eikä polun luominen voi korjata puuttuvaa ajuria.

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

devtmpfs vastaan eksplisiittinen mknod

Yleensä kaksi mekanismia luo tuon solmun, ja tieto siitä, kumman pitäisi olla vastuussa, kertoo mitä korjata. devtmpfs saa ytimen luomaan laitesolmuja liitettyyn instanssiin, ja udev asettaa niiden päälle omistajuuden ja oikeudet. Eksplisiittinen mknod on manuaalinen polku levykuville ja nimiavaruuksille, joissa kumpikaan niistä ei käynnistyksen yhteydessä toimi.

Vertaa tuloksia älä aikomuksia. Jos devtmpfs on liitetty kohtaan /dev ja solmu puuttuu yhä, ajuri ei ole se mitä oletat, tai jokin poisti solmun käynnistyksen jälkeen. Jos /dev on tavallinen hakemisto puretussa rootfs:ssä, automaattista mekanismia ei ole ja levykuvan rakentajan on luotava solmu.

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

Missä kumpikaan mekanismi ei toimi, solmu on tehtävä käsin, ja järjestyksellä on väliä. Luo ensin yläkansio, koska mknod ei voi keksiä sitä, sitten merkkiaselaite, sitten tila, joka jättää sen kirjoitettavaksi vain rootille. Lataa moduuli uudelleen ennen solmun luomista ja poista olemassa oleva polku vasta sen jälkeen, kun ls on osoittanut, että se on hakemisto eikä laite.

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

Nopein erottelija on tyyppi, jonka stat ilmoittaa, ja hakemisto tuolla polulla on yleisin yllätys minimaalisissa levykuvissa. Hyväntahtoinen mkdir Dockerfilessä jättää juuri sen, eikä mikään määrä moduulien lataamista korvaa hakemistoa merkkiaselaitteella. Major 10 ja minor 200 on tunin rekisteröity pari, ilmaistuna heksadesimaalina a ja c8.

Neljä jälkeä, neljä syytä

Neljä syytä selittää lähes jokaisen tapauksen, ja kukin jättää erilaisen jäljen lokeihin. Aloita käynnistysympäristöstä. Levytön levykuva, sulautetut kortit ja initramfs-vaiheet eivät usein liitä kumpaakaan: ei devtmpfs:iä eikä udevin hallitsemaa /dev, joten mikään prosessi ei koskaan kirjoita solmua. Lue sitten liitosasetukset siitä, mikä /devisännöi, koska nodev sallii polun olemassaolon mutta kieltäytyy avaamasta sitä, ja kovennetulta isännältä otettu bind-liitos kantaa lipun ilmoittautumatta. Vain luku -liitos epäonnistuu aikaisemmin ja äänekkäämmin: mknod palauttaa siellä EROFSin, kun taas EPERM tarkoittaa, että prosessilta puuttuu kyky, ei että tiedostojärjestelmä suojelisi itseään. Lopuksi konttiajoympäristö, jolla on oma /dev tmpfs, tarvitsee solmun injektoituna omaan nimiavaruuteensa, ja sen laite-cgroupin on sallittava major- ja minor-pari.

Tarkista käytäntökerros ennen kuin muutat oikeuksia uudelleen. SELinux ja AppArmor voivat hylätä avauksen, jonka kaikki tiedostomääritteet sallivat, ja se hylkäys näkyy auditointilokissa eikä asiakkaan tulosteessa. Oikeuksien muutos, joka ei koskaan näy hylkäyksessä, on aikaa, joka on käytetty täysin väärään kerrokseen.

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

Todista se liitännällä, älä luettelolla

Onnistunut luettelo todistaa, että polku on olemassa eikä mitään muuta. Avaa terve tun-solmu lukemista varten, ja kutsu palaa virheellä, koska laitteella ei ole lukusemantiikkaa eikä se ole tavallinen tiedosto. Se virhe on tässä onnistumisen merkki, ja sen tunnistaminen estää sinua jahtaamasta sitä vikana.

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

Ratkaiseva testi luo liitännän, koska se harjoittaa ajuria, solmua ja käyttöoikeuksiasi yhdessä vaiheessa. Jos lisäys onnistuu ja linkki ilmestyy, kaikki kolme toimivat. Kun se epäonnistuu, errno erottaa syyt: ENOENT tarkoittaa, ettei käyttökelpoista solmua ole, EPERM viittaa kykyyn, liitoslippuihin tai käytäntöön, ja EBUSY tarkoittaa, että toinen prosessi omistaa liitännän jo.

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

Aja tuo testi siinä kontekstissa, joka todella epäonnistuu, koska palveluyksikkö, kontti tai ajastettu työ voi kantaa eri kykyjoukon kuin kirjautumiskuoresi. Testi, joka menee läpi väärässä kontekstissa, ei todista mitään epäonnistuvasta.

Kun laite ei ole ongelma

Jos liitäntätesti onnistuu ja asiakas epäonnistuu yhä, jäljellä oleva ongelma on profiili, palvelukonteksti tai reitti, ja laite on kunnossa. Silloin on aika lopettaa laitesolmujen muokkaaminen, koska lisämuutokset vain lisäävät etäisyyttä toimivaan järjestelmään.

Aja samat viisi tarkistusta joka kerta sen sijaan että keksisit uuden: onko moduuli ladattu ilman kilpailevaa moduulia, onko polku merkkiaselaite oikealla major- ja minor-arvolla, ovatko liitosasetukset puhtaat, kantaa prosessikonteksti kyvyn, ja onko vian aikaleiman kohdalla käytäntöhylkäys. Vika, joka selviää kaikista viidestä, ei ole laiteongelma, mitä viesti sitten mainitseekaan.

Aiheeseen liittyvät artikkelit