Hook-u funksionon kur e ekzekutoni me dorë si root dhe kthen Operation not permitted në momentin që shërbimi e nis atë. Skedari nuk ndryshoi midis atyre ekzekutimeve; mjedisi ndryshoi. systemd i jep procesit një botë më të vogël: më pak aftësi, një /dev tjetër, shtigje të sistemit të skedarëve vetëm për lexim dhe një kufi detyrash. Lexojeni dështimin si një kufi që refuzoi një operacion specifik, jo si një skript që u prish.

Filloni nga operacioni që u refuzua

Një rresht log që thotë se unit-i dështoi nuk ju thotë pothuajse asgjë. Rreshti i dobishëm emërton thirrjen që u refuzua: një nyje pajisjeje që nuk mund të hapej, një drejtori ku nuk mund të shkruhej, një namespace që nuk mund të krijohej. Gjeni atë thirrje përpara se të prekni një direktivë të vetme, sepse secila korrespondon me një kufizim tjetër. Më pas lexoni unit-in që ekzekutoi vërtet, duke përfshirë drop-in-et, në vend të skedarit që dërgoi paketa.

Pyetja më poshtë printon shtegun e fragmentit, drejtoritë e drop-in-it dhe vetitë që kanë rëndësi për një refuzim pajisjeje ose sistemi skedarësh. Lexojini të gjitha përpara se të vendosni cili kufi është i rëndësishëm:

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'

Asgjë në atë bllok nuk shkruan. Nëse thirrja e refuzuar mungon në ditar, rrisni nivelin e detajeve të klientit për një ekzekutim në vend që të hamendësoni direktivat. Një proces në punë dëshmon se unit-i u nis, jo se hook-u ekzekutoi me privilegjet që i nevojiteshin.

Një namespace kërkon të drejta mount që një pajisje TUN nuk i përdor kurrë

Dy operacione që duken njësoj brenda një hook-u kanë profile leje të ndryshme. Hapja e /dev/net/tun dhe krijimi i ndërfaqes kërkon administrim rrjeti plus akses në atë nyje. Krijimi i një namespace rrjeti të emërtuar kërkon gjithashtu punë me mount, sepse mjeti i namespace-it monton një pamje të re të /sys dhe lidh një drejtori gjendjeje në vendin e vet. unit-i i klientit të rrjedhës së sipërme me qëllim e lë administrimin e mount-it jashtë grupit të tij kufizues, kështu që hapi i namespace-it refuzohet ndërsa vetë tuneli do të kishte funksionuar.

Kjo është arsyeja pse dhënia e nyjes së pajisjes nuk e shpëton skriptin. Një leje pajisjeje u përgjigjet një pyetjeje, nëse ky proces mund ta hapë këtë nyje, dhe pyetja e namespace-it është një tjetër. Përputhni thirrjen që dështon me aftësinë e saj përpara se të zgjeroni çdo gjë.

Një test në shell-in tuaj trashëgon çdo aftësi që ka një root i pakufizuar, dhe unit-i trashëgon atë që mbajti grupi i tij kufizues. Dy ekzekutimet janë eksperimente të ndryshme edhe kur binari është identik.

Pajisjet, shtigjet e mbrojtura dhe kufijtë e detyrave

Pasi operacioni i refuzuar të njihet, tre grupe direktivash shpjegojnë shumicën e dështimeve. Aksesi ndaj pajisjeve rregullohet nga PrivateDevices, i cili zëvendëson /dev me një grup minimal pajisjesh pseudo, dhe nga DevicePolicy me DeviceAllow, të cilat vendosin çfarë lejon cgroup-i. /dev/net/tun nuk është pjesë e atij grupi minimal, prandaj një unit me PrivateDevices=yes nuk mund ta hapë, pavarësisht se çfarë thotë politika e pajisjeve.

Mbrojtja e sistemit të skedarëve i përgjigjet një pyetjeje të dytë. ProtectHome=yes i bën /home, /root dhe /run/user të paarritshme, dhe ProtectSystem=full i bën /usr, /boot dhe /etc vetëm për lexim. Prandaj një hook që shkruan pranë skedarit të vetë konfigurimit funksionon në shell-in tuaj dhe dështon nën unit, me të njëjtën formë leje si një aftësi që mungon. Zhvendoseni hyrjen e shkrueshme në një shteg që politika e lejon, ose shtoni një ReadWritePaths= zë të ngushtë.

Kufijtë e burimeve janë grupi i tretë, dhe më pak i dukshëm. TasksMax= kufizon sa detyra mund të krijojë unit-i, dhe një kufi përshkruesish skedarësh sillet njësoj për socket-t dhe dorezat. Një instalim që u rrit përtej kufirit dështon me një gabim burimi, jo me një gabim leje, prandaj mesazhi i saktë ka ende rëndësi.

Zhvendosni hapin e namespace-it jashtë hook-ut

Përgatitja që kërkon një privilegj që klienti nuk duhet ta ketë i takon unit-it të vet. Një oneshot që ekzekutohet para klientit, e mban rezultatin dhe është i renditur para lidhjes i jep hapit të namespace-it atë që i nevojitet pa i dhënë ato të drejta procesit të tunelit. Klienti më pas i referohet atij unit-i në vend që ta thërrasë skriptin nga hook-u i tij up.

Shablloni më poshtë është një formë fillestare, jo diçka që aplikohet pa e lexuar. E mban përgatitjen aty ku jeton privilegji dhe e lë unit-in e tunelit të paprekur:

# /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

Shtoni renditjen me një drop-in në vend që të modifikoni unit-in e paketuar, kështu që një përditësim nuk mund ta hedhë në heshtje ndryshimin tuaj. Drejtoria është e lidhur me emrin e instancës, prandaj një profil i dytë kërkon skedarin e vet:

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

Përsëritni të njëjtin thirrje pas ndryshimit

Një riparim konfirmohet vetëm kur e njëjta thirrje prodhon një rezultat të ndryshëm. Rinisni të dy unit-et, ekzekutoni operacionin që dështoi dhe krahasoni përfundimin e tij me rreshtin e ditarit që ruajtët përpara ndryshimit. Një unit që niset ndërsa hook-u nuk ekzekutohet më kurrë nuk është riparim.

Sekuenca më poshtë e mban krahasimin e ndershëm, sepse kontrollon thirrjen e refuzuar dhe jo ngjyrën e unit-it. Ruajeni daljen e saj pranë rreshtit të ditarit që kapët përpara ndryshimit:

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

Numëroni refuzimet në vend që të lexoni rreshtin e fundit, sepse një hook mund të dështojë, të riprovojë dhe të ketë sukses, dhe vetëm numri tregon nëse kufiri u zhvendos. Lini një koment në drop-in duke emërtuar direktivën që lëshuat dhe arsyen, pasi përditësimi tjetër e krahason konfigurimin tuaj me parazgjedhjen e paketuar. Një unit që niset vetëm pasi çdo mbrojtje është çaktivizuar nuk është rregulluar.

Artikuj të ngjashëm