Koukku toimii, kun ajat sen käsin rootina, ja palauttaa Operation not permitted hetkellä, jolla palvelu käynnistää sen. Tiedosto ei muuttunut näiden ajojen välillä; ympäristö muuttui. systemd antaa prosessille pienemmän maailman: vähemmän kyvykkyyksiä, toisenlaisen /dev, vain luku -tiedostojärjestelmäpolut ja tehtäväkaton. Lue epäonnistuminen rajana, joka hylkäsi yhden tietyn toiminnon, ei rikkoutuneena skriptinä.

Aloita toiminnosta, joka hylättiin

Lokirivi, joka kertoo unitin epäonnistuneen, ei kerro sinulle juuri mitään. Hyödyllinen rivi nimeää kutsun, joka hylättiin: laitepiste, jota ei voitu avata, hakemisto, johon ei voitu kirjoittaa, nimiavaruus, jota ei voitu luoda. Löydä se kutsu ennen kuin kosket yhteenkään direktiiviin, koska jokainen vastaa eri rajoitusta. Lue sitten unit, joka todella ajettiin, drop-init mukaan lukien, sen sijaan että lukisit tiedoston, jonka paketti toimitti.

Alla oleva kysely tulostaa fragmentin polun, drop-in-hakemistot ja ominaisuudet, joilla on merkitystä laitteen tai tiedostojärjestelmän hylkäyksessä. Lue ne kaikki ennen kuin päätät, mikä raja on olennainen:

systemctl show "[email protected]" -p FragmentPath -p DropInPaths -p PrivateDevices -p DevicePolicy -p DeviceAllow -p ProtectHome -p ProtectSystem -p TasksMax
journalctl -u "[email protected]" -b --no-pager | grep -iE 'operation not permitted|permission denied|cannot open|failed'

Mikään tuossa lohkossa ei kirjoita. Jos hylätty kutsu puuttuu lokista, nosta asiakkaan yksityiskohtaisuutta yhden ajon ajaksi sen sijaan että arvaisit direktiivejä. Käynnissä oleva prosessi todistaa, että unit käynnistyi, ei että koukku ajettiin tarvittavilla oikeuksilla.

Nimiavaruus tarvitsee mount-oikeudet, joita TUN-laite ei koskaan käytä

Kahdella toiminnolla, jotka näyttävät samalta yhden koukun sisällä, on erilaiset lupaprofiilit. Avaaminen /dev/net/tun ja liitännäisen luominen tarvitsee verkonhallintaa sekä pääsyn tuohon pisteeseen. Nimetyn verkkonimiavaruuden luominen tarvitsee myös mount-työtä, koska nimiavaruustyökalu mounttaa tuoreen /sys näkymän ja sitoo tilahakemiston paikalleen. ylävirran asiakas-unit jättää mount-hallinnan tarkoituksella rajasarjansa ulkopuolelle, joten nimiavaruuden vaihe hylätään, vaikka itse tunneli olisi toiminut.

Siksi laitepisteen myöntäminen ei pelasta skriptiä. Laitelupa vastaa yhteen kysymykseen, saako tämä prosessi avata tämän pisteen, ja nimiavaruuden kysymys on toinen. Sovita epäonnistuva kutsu sen kyvykkyyteen ennen kuin laajennat mitään.

Testi shellissäsi perii jokaisen kyvykkyyden, joka rajoittamattomalla rootilla on, ja unit perii sen, minkä sen rajasarja säilytti. Ne kaksi ajoa ovat eri kokeita vaikka binääri olisi identtinen.

Laitteet, suojatut polut ja tehtäväkatot

Kun hylätty toiminto tunnetaan, kolme direktiiviryhmää selittää useimmat epäonnistumiset. Laitekäyttöä säätelee PrivateDevices, joka korvaa /dev minimaalisella pseudolaitteiden joukolla, ja DevicePolicy kanssa DeviceAllow, jotka päättävät, mitä cgroup sallii. /dev/net/tun ei kuulu tuohon minimaaliseen joukkoon, joten unit, jolla on PrivateDevices=yes ei voi avata sitä, mitä laitekäytäntö sanoo tahansa.

Tiedostojärjestelmän suojaus vastaa toiseen kysymykseen. ProtectHome=yes tekee /home, /root ja /run/user saavuttamattomiksi, ja ProtectSystem=full tekee /usr, /boot ja /etc vain luku -tilaan. Siksi koukku, joka kirjoittaa oman asetustiedostonsa viereen, toimii shellissäsi ja epäonnistuu unitin alla, samalla luvan muodolla kuin puuttuva kyvykkyys. Siirrä kirjoitettava syöte polulle, jonka käytäntö sallii, tai lisää kapea ReadWritePaths= merkintä.

Resurssirajat ovat kolmas ryhmä ja vähiten ilmeinen. TasksMax= rajoittaa, kuinka monta tehtävää unit saa luoda, ja tiedostokuvauksien katto käyttäytyy samoin socketeille ja kahvoille. Katon yli kasvanut käyttöönotto epäonnistuu resurssivirheeseen eikä lupavirheeseen, ja siksi täsmällinen viesti on yhä merkityksellinen.

Siirrä nimiavaruuden vaihe koukun ulkopuolelle

Valmistelu, joka tarvitsee oikeuden, jota asiakkaalla ei saa olla, kuuluu omaan unitiinsa. Oneshot, joka ajetaan ennen asiakasta, säilyttää tuloksensa ja on järjestetty ennen yhteyttä, antaa nimiavaruuden vaiheelle sen, mitä se tarvitsee, luovuttamatta näitä oikeuksia tunneliprosessille. Sitten asiakas viittaa tuohon unitiin sen sijaan että kutsuisi skriptin omasta up-koukustaan.

Alla oleva mallipohja on aloitusmuoto, ei mitään mitä sovelletaan lukematta. Se pitää valmistelun siellä missä oikeus elää ja jättää tunnelin unitin koskemattomaksi:

# /etc/systemd/system/[email protected]
[Unit]
Description=Namespace preparation for %i
Before=openvpn-client@%i.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/sbin/netns-up.sh %i
CapabilityBoundingSet=CAP_NET_ADMIN CAP_SYS_ADMIN
[Install]
WantedBy=multi-user.target

Lisää järjestys drop-inilla sen sijaan että muokkaisit paketoitua unitia, jotta päivitys ei voi hiljaa pudottaa muutostasi. Hakemisto on avaimellinen instanssin nimeen, joten toinen profiili tarvitsee oman tiedostonsa:

sudo mkdir -p /etc/systemd/system/[email protected]
sudo tee /etc/systemd/system/[email protected]/10-namespace.conf >/dev/null <<'CONF'
[Unit]
[email protected]
[email protected]
CONF
sudo systemctl daemon-reload

Toista sama kutsu muutoksen jälkeen

Korjaus vahvistetaan vasta kun sama kutsu tuottaa eri tuloksen. Käynnistä molemmat unitit uudelleen, suorita toiminto, joka epäonnistui, ja vertaa sen tulosta lokiriviin, jonka tallensit ennen muutosta. Unit, joka käynnistyy, kun koukku ei enää koskaan aja, ei ole korjaus.

Alla oleva sarja pitää vertailun rehellisenä, koska se tarkistaa hylätyn kutsun eikä unitin väriä. Tallenna sen tuloste ennen muutosta tallentamasi lokirivin viereen:

sudo systemctl restart "[email protected]" "[email protected]"
systemctl status "[email protected]" --no-pager
journalctl -u "[email protected]" -b --no-pager | grep -c 'Operation not permitted'
ip -brief addr show tun0

Laske hylkäykset sen sijaan että lukisit viimeisen rivin, koska koukku voi epäonnistua, yrittää uudelleen ja onnistua, ja vain määrä näyttää, siirtyikö raja. Jätä drop-iniin kommentti, joka nimeää löysentämäsi direktiivin ja syyn, koska seuraava päivitys vertaa asetuksiasi paketin oletusarvoon. Unit, joka käynnistyy vasta kun jokainen suojaus on kytketty pois, ei ole korjattu.

Aiheeseen liittyvät artikkelit