Hooken fungerer når du kjører den for hånd som root, og returnerer Operation not permitted i det øyeblikket tjenesten starter den. Filen endret seg ikke mellom disse kjøringene; miljøet gjorde det. systemd gir prosessen en mindre verden: færre capabilities, en annen /dev, skrivebeskyttede filsystemstier og et tak på antall oppgaver. Les feilen som en grense som avviste én bestemt operasjon, ikke som et skript som gikk i stykker.

Start ved operasjonen som ble avvist

En logglinje som sier at uniten feilet, forteller deg nesten ingenting. Den nyttige linjen navngir kallet som ble avvist: en enhetsnode som ikke kunne åpnes, en katalog det ikke kunne skrives i, et navnerom som ikke kunne opprettes. Finn det kallet før du rører en eneste direktiv, fordi hver av dem svarer til en forskjellig begrensning. Les deretter uniten som faktisk kjørte, inkludert drop-ins, i stedet for filen pakken leverte.

Spørringen nedenfor skriver ut fragmentstien, drop-in-katalogene og egenskapene som betyr noe for en avvisning av enhet eller filsystem. Les alle før du bestemmer hvilken grense som 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'

Ingenting i den blokken skriver. Hvis det avviste kallet mangler i journalen, øk klientens detaljnivå for én kjøring i stedet for å gjette på direktiver. En kjørende prosess beviser at uniten startet, ikke at hooken kjørte med privilegiene den trengte.

Et navnerom trenger mount-rettigheter som en TUN-enhet aldri bruker

To operasjoner som ser like ut inne i én hook, har forskjellige tillatelsesprofiler. Å åpne /dev/net/tun og opprette grensesnittet krever nettverksadministrasjon pluss tilgang til den noden. Å opprette et navngitt nettverksnavnerom krever også mount-arbeid, fordi navneromsverktøyet monterer en fersk /sys visning og binder en tilstandskatalog på plass. oppstrøms klient-unit lar bevisst mount-administrasjonen ligge utenfor sitt bounding set, så navneromstrinnet avvises mens tunnelen selv ville ha fungert.

Derfor redder det ikke skriptet å gi enhetsnoden. En enhetstillatelse svarer på ett spørsmål, om denne prosessen kan åpne denne noden, og navneromsspørsmålet er et annet. Match det feilende kallet med capabilityen før du utvider noe.

En test i skallet ditt arver alle capabilities en ubegrenset root har, og uniten arver det bounding setet beholdt. De to kjøringene er forskjellige eksperimenter selv når binæren er identisk.

Enheter, beskyttede stier og tak for oppgaver

Når den avviste operasjonen er kjent, forklarer tre grupper med direktiver de fleste feilene. Enhetstilgang styres av PrivateDevices, som erstatter /dev med et minimalt sett pseudoenheter, og av DevicePolicy med DeviceAllow, som avgjør hva cgroupen tillater. /dev/net/tun er ikke en del av det minimale settet, så en unit med PrivateDevices=yes kan ikke åpne den, uansett hva enhetspolicyen sier.

Filsystembeskyttelse svarer på et andre spørsmål. ProtectHome=yes gjør /home, /root og /run/user utilgjengelige, og ProtectSystem=full gjør /usr, /boot og /etc skrivebeskyttet. Derfor fungerer en hook som skriver ved siden av sin egen konfigurasjonsfil i skallet ditt og feiler under uniten, med samme tillatelsesform som en manglende capability. Flytt det skrivbare innholdet til en sti policyen tillater, eller legg til en smal ReadWritePaths= oppføring.

Ressursgrenser er den tredje gruppen, og den minst åpenbare. TasksMax= begrenser hvor mange oppgaver uniten kan opprette, og et tak på fildeskriptorer oppfører seg likt for sockets og håndtak. En utrulling som har vokst forbi taket, feiler med en ressursfeil i stedet for en tillatelsesfeil, og derfor betyr den nøyaktige meldingen fortsatt noe.

Flytt navneromstrinnet ut av hooken

Forberedelser som krever en privilegie klienten ikke skal ha, hører hjemme i sin egen unit. En oneshot som kjører før klienten, beholder resultatet sitt og er ordnet før tilkoblingen, gir navneromstrinnet det det trenger uten å overlate rettighetene til tunnelprosessen. Klienten refererer deretter til den uniten i stedet for å kalle skriptet fra sin up-hook.

Malen nedenfor er en startform, ikke noe man bruker ulest. Den holder forberedelsene der privilegiet bor og lar tunneluniten 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

Legg til rekkefølgen med en drop-in i stedet for å redigere den pakkede uniten, slik at en oppgradering ikke stille kan miste endringen din. Katalogen er nøklet til instansnavnet, så en andre profil trenger 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

Gjenta det samme kallet etter endringen

En reparasjon bekreftes bare når det samme kallet gir et annet resultat. Start begge unitene på nytt, kjør operasjonen som feilet, og sammenlign resultatet med logglinjen du lagret før endringen. En unit som starter mens hooken aldri kjører igjen, er ikke en reparasjon.

Sekvensen nedenfor holder sammenligningen ærlig, fordi den kontrollerer det avviste kallet og ikke fargen på uniten. Lagre utdataene ved siden av logglinjen du fanget før endringen:

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

Tell avvisningene i stedet for å lese den siste linjen, fordi en hook kan feile, prøve på nytt og lykkes, og bare antallet viser om grensen flyttet seg. Legg igjen en kommentar i drop-in-en som navngir direktivet du løsnet og grunnen, siden neste oppgradering sammenligner konfigurasjonen din med den pakkede standarden. En unit som bare starter når all beskyttelse er slått av, er ikke reparert.

Relaterte artikler