Hooket virker, når du kører det i hånden som root, og returnerer Operation not permitted i det øjeblik, tjenesten starter det. Filen ændrede sig ikke mellem de kørsler; miljøet gjorde. systemd giver processen en mindre verden: færre capabilities, en anderledes /dev, skrivebeskyttede filsystemstier og et loft over antallet af tasks. Læs fejlen som en grænse, der afviste én bestemt operation, ikke som et script, der gik i stykker.

Start ved den operation, der blev afvist

En loglinje, der siger, at unit'en fejlede, fortæller dig næsten intet. Den nyttige linje navngiver det kald, der blev afvist: en enhedsknude, der ikke kunne åbnes, en mappe, der ikke kunne skrives i, et namespace, der ikke kunne oprettes. Find det kald, før du rører en enkelt direktiv, fordi hvert af dem svarer til en forskellig begrænsning. Læs derefter den unit, der faktisk kørte, inklusive drop-ins, i stedet for den fil, pakken leverede.

Forespørgslen nedenfor udskriver fragmentstien, drop-in-mapperne og de egenskaber, der betyder noget for en afvisning af en enhed eller et filsystem. Læs dem alle, før du beslutter, hvilken grænse der er relevant:

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'

Intet i den blok skriver. Hvis det afviste kald mangler i journalen, så hæv klientens detaljeringsgrad for én kørsel i stedet for at gætte på direktiver. En kørende proces beviser, at unit'en startede, ikke at hooket kørte med de privilegier, det havde brug for.

Et namespace kræver mount-rettigheder, som en TUN-enhed aldrig bruger

To operationer, der ligner hinanden inde i et hook, har forskellige tilladelsesprofiler. At åbne /dev/net/tun og oprette interfacet kræver netværksadministration plus adgang til den knude. At oprette et navngivet netværksnamespace kræver også mount-arbejde, fordi namespace-værktøjet mounter et friskt /sys view og binder en tilstandsmappe på plads. opstrøms klient-unit udelader bevidst mount-administration fra sit bounding set, så namespace-skridtet afvises, mens tunnelen selv ville have virket.

Derfor redder det ikke scriptet at give enhedsknuden. En enhedstilladelse besvarer ét spørgsmål, om denne proces må åbne denne knude, og namespace-spørgsmålet er et andet. Match det fejlende kald med dets capability, før du udvider noget.

En test i din shell arver enhver capability, som en ubegrænset root har, og unit'en arver det, dens bounding set beholdt. De to kørsler er forskellige eksperimenter selv når binæren er identisk.

Enheder, beskyttede stier og lofter over tasks

Når den afviste operation er kendt, forklarer tre grupper af direktiver de fleste fejl. Enhedsadgang styres af PrivateDevices, som erstatter /dev med et minimalt sæt pseudoenheder, og af DevicePolicy med DeviceAllow, som afgør, hvad cgroup'en tillader. /dev/net/tun er ikke en del af det minimale sæt, så en unit med PrivateDevices=yes kan ikke åbne den, uanset hvad enhedspolitikken siger.

Filsystembeskyttelse besvarer et andet spørgsmål. ProtectHome=yes gør /home, /root og /run/user utilgængelige, og ProtectSystem=full gør /usr, /boot og /etc skrivebeskyttede. Derfor virker et hook, der skriver ved siden af sin egen konfigurationsfil, i din shell og fejler under unit'en, med samme tilladelsesform som en manglende capability. Flyt det skrivbare input til en sti, som politikken tillader, eller tilføj en smal ReadWritePaths= indgang.

Ressourcegrænser er den tredje gruppe og den mindst oplagte. TasksMax= begrænser, hvor mange tasks unit'en må oprette, og et loft over filbeskrivere opfører sig på samme måde for sockets og håndtag. En udrulning, der er vokset forbi loftet, fejler med en ressourcefejl i stedet for en tilladelsesfejl, og derfor betyder den præcise besked stadig noget.

Flyt namespace-skridtet ud af hooket

Forberedelse, der kræver en privilegie, som klienten ikke må have, hører i sin egen unit. En oneshot, der kører før klienten, beholder sit resultat og er ordnet før forbindelsen, giver namespace-skridtet, hvad det har brug for, uden at overdrage de rettigheder til tunnelprocessen. Klienten refererer derefter til den unit i stedet for at kalde scriptet fra sit up-hook.

Skabelonen nedenfor er en startform, ikke noget man anvender ulæst. Den holder forberedelsen der, hvor privilegiet bor, og lader tunnel-unit'en være urørt:

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

Tilføj rækkefølgen med et drop-in i stedet for at redigere den pakkede unit, så en opgradering ikke lydløst kan tabe din ændring. Mappen er nøglet til instansnavnet, så en anden profil kræver sin egen fil:

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

Gentag det samme kald efter ændringen

En reparation bekræftes kun, når det samme kald giver et andet resultat. Genstart begge units, kør den operation, der fejlede, og sammenlign dens udfald med den loglinje, du gemte før ændringen. En unit, der starter, mens hooket aldrig kører igen, er ikke en reparation.

Sekvensen nedenfor holder sammenligningen ærlig, fordi den kontrollerer det afviste kald i stedet for unit'ens farve. Gem dens output ved siden af den loglinje, du fangede før ændringen:

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

Tæl afvisningerne i stedet for at læse den sidste linje, fordi et hook kan fejle, prøve igen og lykkes, og kun antallet viser, om grænsen flyttede sig. Efterlad en kommentar i drop-in'et, der navngiver den direktiv, du løsnede, og årsagen, da den næste opgradering sammenligner din konfiguration med den pakkede standard. En unit, der kun starter, når al beskyttelse er slået fra, er ikke rettet.

Relaterede artikler