Hogyan derítsük ki, melyik systemd-sandbox utasította el az OpenVPN-hookot
A hook rootként működik, systemd alatt viszont elbukik, mert az unit kevesebb képességet, kisebb /dev-et és csak olvasható útvonalakat tart meg.
A hook akkor működik, amikor kézzel, rootként futtatod, és visszaadja Operation not permitted abban a pillanatban, amikor a szolgáltatás elindítja. A fájl nem változott ezek között a futások között; a környezet igen. A systemd kisebb világot ad a folyamatnak: kevesebb képességet, egy másik /dev, csak olvasható fájlrendszer-útvonalakat és egy feladatplafont. Olvasd a hibát olyan határként, amely egy konkrét műveletet utasított el, nem pedig elromlott szkriptként.
Azzal a művelettel kezdd, amelyet elutasítottak
Egy naplósor, amely azt mondja, hogy az unit elbukott, szinte semmit nem mond el. A hasznos sor megnevezi azt a hívást, amelyet elutasítottak: egy eszközcsomópont, amelyet nem lehetett megnyitni, egy könyvtár, amelybe nem lehetett írni, egy névtér, amelyet nem lehetett létrehozni. Keresd meg azt a hívást, mielőtt egyetlen direktívához is hozzányúlnál, mert mindegyik más korláthoz tartozik. Utána azt az unitot olvasd el, amely valóban futott, a drop-inekkel együtt, ne a fájlt, amelyet a csomag szállított.
Az alábbi lekérdezés kiírja a töredék útvonalát, a drop-in könyvtárakat és azokat a tulajdonságokat, amelyek egy eszköz- vagy fájlrendszer-elutasításnál számítanak. Olvasd el mindet, mielőtt eldöntöd, melyik határ releváns:
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'Ebben a blokkban semmi nem ír. Ha az elutasított hívás hiányzik a naplóból, egy futásra növeld a kliens részletességét, ahelyett hogy direktívákat találgatnál. Egy futó folyamat azt bizonyítja, hogy az unit elindult, nem azt, hogy a hook a szükséges jogosultságokkal futott.
Egy névtérhez mount-jogosultság kell, amelyet egy TUN-eszköz soha nem használ
Két műveletnek, amelyek egy hookon belül hasonlónak tűnnek, különböző engedélyprofilja van. A /dev/net/tun megnyitása és az interfész létrehozása hálózati adminisztrációt és hozzáférést kíván ahhoz a csomóponthoz. Egy nevesített hálózati névtér létrehozása is igényel mount-munkát, mert a névtér-eszköz egy friss /sys nézetet mountol, és egy állapotkönyvtárat a helyére köt. upstream kliens-unit szándékosan a határkészletén kívül hagyja a mount-adminisztrációt, ezért a névtér-lépés elutasításra kerül, miközben maga az alagút működött volna.
Ezért az eszközcsomópont megadása nem menti meg a szkriptet. Az eszközengedély egyetlen kérdésre válaszol, hogy ez a folyamat megnyithatja-e ezt a csomópontot, a névtér kérdése pedig más. Párosítsd a bukó hívást a képességével, mielőtt bármit szélesítenél.
A shellben végzett teszt minden képességet örököl, amellyel egy korlátlan root rendelkezik, az unit pedig azt örökli, amit a határkészlete megtartott. A két futás különböző kísérlet akkor is, ha a bináris azonos.
Eszközök, védett útvonalak és feladatplafonok
Ha az elutasított művelet ismert, három direktívacsoport magyarázza a legtöbb hibát. Az eszközhozzáférést a PrivateDevices szabályozza, amely a /dev egy minimális pszeudoeszköz-készlettel helyettesíti, valamint a DevicePolicy a DeviceAllow társaságában, amelyek eldöntik, mit engedélyez a cgroup. /dev/net/tun nem része annak a minimális készletnek, ezért egy PrivateDevices=yes unit nem nyithatja meg, bármit is mond az eszközszabályzat.
A fájlrendszer-védelem egy második kérdésre válaszol. ProtectHome=yes elérhetetlenné teszi a /home, /root és a /run/user könyvtárat, a ProtectSystem=full pedig csak olvashatóvá teszi a /usr, /boot és a /etc könyvtárat. Ezért az a hook, amely a saját konfigurációs fájlja mellé ír, a shellben működik, az unit alatt viszont elbukik, ugyanolyan engedélyformával, mint egy hiányzó képesség. Mozgasd az írható bemenetet a szabályzat által engedett útvonalra, vagy adj hozzá egy szűk ReadWritePaths= bejegyzést.
Az erőforráskorlátok a harmadik csoport, és a legkevésbé nyilvánvaló. TasksMax= korlátozza, hány feladatot hozhat létre az unit, a fájlleíró-plafon pedig ugyanígy viselkedik a socketek és fogantyúk esetében. Egy telepítés, amely túlnőtt a plafonon, erőforráshibával bukik el, nem engedélyhibával, és ezért számít még mindig a pontos üzenet.
Emeld ki a névtér-lépést a hookból
Az az előkészítés, amely olyan jogosultságot igényel, amellyel a kliens nem rendelkezhet, a saját unitjába tartozik. Egy oneshot, amely a kliens előtt fut, megtartja az eredményét, és a kapcsolat elé van rendezve, megadja a névtér-lépésnek, amire szüksége van, anélkül hogy átadná ezeket a jogokat az alagút folyamatának. A kliens ezután arra az unitra hivatkozik, ahelyett hogy a szkriptet a saját up-hookjából hívná.
Az alábbi sablon kezdő forma, nem valami, amit olvasatlanul alkalmazni kell. Ott tartja az előkészítést, ahol a jogosultság él, és érintetlenül hagyja az alagút unitját:
# /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.targetA sorrendet drop-innal add hozzá, ne a csomagolt unit szerkesztésével, hogy egy frissítés ne dobhassa el csendben a változtatásodat. A könyvtár a példánynévhez van kötve, ezért egy második profilnak saját fájl kell:
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-reloadIsmételd meg ugyanazt a hívást a változtatás után
A javítás csak akkor igazolódik, ha ugyanaz a hívás más eredményt ad. Indítsd újra mindkét unitot, futtasd a műveletet, amely elbukott, és hasonlítsd össze az eredményét a változtatás előtt elmentett naplósorral. Egy unit, amely elindul, miközben a hook soha többé nem fut, nem javítás.
Az alábbi sorrend becsületesen tartja az összehasonlítást, mert az elutasított hívást vizsgálja, nem az unit színét. Mentsd a kimenetét a változtatás előtt rögzített naplósor mellé:
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 tun0Számold az elutasításokat, ne az utolsó sort olvasd, mert egy hook elbukhat, újrapróbálhat és sikerülhet, és csak a szám mutatja meg, hogy elmozdult-e a határ. Hagyj megjegyzést a drop-inban, amely megnevezi a lazított direktívát és az okot, mivel a következő frissítés összeveti a konfigurációdat a csomagolt alapértelmezéssel. Egy unit, amely csak akkor indul el, ha minden védelem ki van kapcsolva, nincs megjavítva.