Executeu una acció posterior a la connexió VPN com a usuari, no com a dimoni
Una variable d’entorn que diu USER=alice no canvia amb quin compte s’executa un hook. Separeu l’esdeveniment, el disparador i l’acció, i després demostreu l’UID.
Els hooks de connexió d’un dimoni VPN s’executen com el dimoni i normalment això vol dir que s’executen com a root. En el moment que un d’aquests hooks escriu al directori personal d’algú o reinicia un assistent d’escriptori, heu donat a un esdeveniment de xarxa la capacitat d’actuar com un usuari local arbitrari. La drecera habitual, llegir un nom d’usuari d’una variable d’entorn i passar-lo, no ho arregla. Reetiqueta l’acció sense canviar quin compte l’executa. El que funciona és un lliurament en tres parts on el pas privilegiat diu què ha passat i una unitat separada fa l’acció amb la identitat correcta.
Un nom d’usuari a l’entorn no és un commutador de credencials
Unix decideix a què pot tocar un procés pel seu ID d’usuari efectiu, no per les cadenes del seu entorn. Establir USER=alice o exportar LOGNAME canvia què imprimeix un programari descuidat a la seva sortida; no canvia res en les comprovacions de propietat de fitxers. Un hook que s’executa com a root amb aquesta variable establerta encara obre els fitxers de l’Alice com a root.
Els noms tampoc són el mateix nom. L’inici de sessió d’accés remot, el compte Unix local i el nom comú del certificat són tres identificadors que sovint coincideixen i de vegades no. Un usuari que s’autentica com a.smith pot no tenir cap compte local, o pot mapar-se a asmith mitjançant un servei de directori. Qualsevol disseny que assumeixi que una cadena serveix per als tres papers es trencarà amb el primer contractista.
Així que la primera pregunta no és «quin usuari està connectat» sinó «quin compte local pot fer aquesta acció». Responeu-ho des de la vostra pròpia base de comptes, no des del que hagi enviat el client:
id -u alice
getent passwd alice | cut -d: -f1,3,6
stat -c '%U:%G %a %n' /home/alice/.local/state/vpn-status.json
La tercera ordre és la que la gent s’salta. Si el fitxer objectiu és propietat de root perquè l’hook anterior el va crear, una acció d’usuari ben configurada fallarà amb un error de permisos que sembla un problema de systemd i no ho és.
El dimoni es queda on és
Canviar el User= propi del servei VPN a l’Alice és la palanca equivocada. Altera com el dimoni enllaça interfícies, llegeix claus, escriu el seu fitxer d’estat i gestiona rutes, i trasllada tot aquest risc a una unitat que funcionava. El dimoni necessita els seus privilegis; l’acció posterior no.
Mireu què fa realment la unitat abans de tocar-la. Dues ordres us diuen la identitat d’execució i les línies d’ordre exactes en vigor, incloent-hi qualsevol cosa afegida per una anul·lació:
systemctl show -p User -p Group -p ExecStart openvpn-server@server
systemctl cat openvpn-server@server
En una instal·lació de paquet per defecte el dimoni és root i l’script --up ho hereta. Confirmeu-ho en lloc d’assumir-ho, perquè una distribució endurida pot haver-lo mogut a un compte dedicat, i un drop-in escrit a mà pot executar l’hook a través d’un embolcall amb regles diferents.
Tres parts amb nom, un disparador estret
L’arranjament que sobreviu al manteniment té un esdeveniment, un disparador i una acció. El dimoni informa que una sessió amb nom s’ha aixecat. Un disparador accepta aquest petit fet i demana una acció fixa. L’acció s’executa sota el compte local que posseeix els fitxers que toca. Res de la cadena no accepta un camí d’ordre arbitrari.
L’acció en si és una unitat de servei ordinària, i la identitat és una directiva dins seu:
[Service]
Type=oneshot
User=alice
Group=alice
ExecStart=/usr/local/bin/refresh-vpn-status --slot %i
Fixeu-vos que User=alice s’aplica a tota la unitat, i systemd documenta una excepció que val la pena conèixer: una ordre amb el prefix + s’executa amb tots els privilegis independentment d’això. Aquest prefix és una eina legítima per a un sol pas privilegiat i una manera convenient de desfer la separació que acabeu de construir, així que tracteu qualsevol + en aquesta unitat com una cosa a justificar en la revisió.
Manteniu l’entrada del disparador mínima i no secreta. L’identificador de sessió, el compte mapat i una marca de temps solen ser tot el que cal. No passeu mai un fragment d’shell, un camí d’executable o un bloc d’entorn proporcionat pel client a través de la frontera, perquè el costat receptor no té manera de distingir un valor legítim d’un injectat.
Verifiqueu la identitat que realment va córrer
La revisió de la configuració no és evidència. Després d’una connexió de prova aprovada, confirmeu l’UID que va tocar el fitxer i l’UID que el dimoni encara utilitza:
stat -c '%U %G %y %n' /home/alice/.local/state/vpn-status.json
ps -o user=,pid=,cmd= -p "$(pgrep -o -f 'openvpn --daemon')"
systemd-run --uid=alice --pipe /usr/local/bin/refresh-vpn-status --dry-run
La primera línia hauria de mostrar l’Alice i una marca de temps dins de la vostra finestra de prova. La segona hauria de mostrar encara root o el compte de servei del paquet, cosa que demostra que el dimoni no s’ha mogut. La tercera és la manera més barata de provar els permisos de l’acció sense esperar una reconnexió real.
Si el propietari del fitxer és l’Alice però el seu grup o mode ha canviat entre execucions, busqueu un UMask= o una diferència de permisos del directori en lloc d’un error de lògica. Si la marca de temps no s’ha mogut gens, el disparador no s’ha disparat mai, i el següent pas útil és journalctl -u per a la unitat d’acció en lloc d’una altra reconnexió del client.
Què encara no demostra un esdeveniment de connexió
Un túnel establert és un sol fet. No vol dir que la sessió d’escriptori estigui desbloquejada, que el bus de sessió sigui abastable o que el recurs privat que vol l’acció ja estigui disponible. Una unitat oneshot sota l’UID de l’Alice té la seva identitat i res de la seva sessió, així que qualsevol cosa que requereixi un bus gràfic ha de passar pel mecanisme de sessió d’usuari suportat i definir què passa quan ella tanca la sessió.
Les reconnexions també es repeteixen. Un portàtil que dorm en un tren pot produir una dotzena d’esdeveniments de connexió en una hora, i un hook que afegeix una línia cada cop converteix un fitxer d’estat en un registre que ningú no pot llegir. Feu l’acció idempotent, o deixeu que substitueixi un sol registre amb clau per sessió.
Finalment, limiteu el radi de danys a la mateixa unitat: una vista de sistema de fitxers només de lectura fora del directori d’estat, sense privilegis nous i amb un temps d’espera explícit. La separació entre dimoni i acció d’usuari val la pena per si sola, però val molt més quan l’acció no pot fer res excepte l’única cosa que heu aprovat.