Háky připojení u démona VPN běží jako démon, a to obvykle znamená, že běží jako root. Ve chvíli, kdy jeden z těchto háků zapíše do něčího domovského adresáře nebo restartuje pomocníka plochy, dali jste síťové události schopnost jednat jako libovolný místní uživatel. Obvyklá zkratka, tedy přečíst uživatelské jméno z proměnné prostředí a předat ho dál, to neřeší. Pouze akci přeznačí, aniž by změnila, který účet ji vykonává. Funguje třídílné předání, kdy privilegovaný krok řekne, co se stalo, a samostatná jednotka provede akci pod správnou identitou.

Uživatelské jméno v prostředí není přepínač identity

Unix rozhoduje, na co proces může sáhnout, podle jeho efektivního uživatelského ID, ne podle řetězců v jeho prostředí. Nastavení USER=alice nebo export LOGNAME mění, co nedbalý software vypíše do svého výstupu; na kontroly vlastnictví souborů to nemění nic. Hák běžící jako root s nastavenou proměnnou stejně otevírá Aliciny soubory jako root.

Ani jména nejsou totéž jméno. Přihlášení vzdáleného přístupu, místní unixový účet a obecné jméno certifikátu jsou tři identifikátory, které se často shodují a občas ne. Uživatel, který se autentizuje jako a.smith, nemusí mít vůbec žádný místní účet, nebo se může přes adresářovou službu mapovat na asmith. Jakýkoli návrh, který předpokládá, že jeden řetězec slouží všem třem rolím, se zlomí na prvním dodavateli.

První otázka tedy není „který uživatel je připojen“, ale „který místní účet smí tuto akci provést“. Odpovězte na ni ze své vlastní databáze účtů, ne z toho, co poslal klient:

id -u alice
getent passwd alice | cut -d: -f1,3,6
stat -c '%U:%G %a %n' /home/alice/.local/state/vpn-status.json

Třetí příkaz je ten, který lidé přeskakují. Pokud cílový soubor vlastní root, protože ho vytvořil předchozí hák, správně nastavená uživatelská akce selže s chybou oprávnění, která vypadá jako problém systemd a není.

Démon zůstává tam, kde je

Změnit vlastní User= služby VPN na Alici je špatná páčka. Mění, jak démon váže rozhraní, čte klíče, zapisuje svůj stavový soubor a spravuje trasy, a přesouvá všechno to riziko do jednotky, která fungovala. Démon potřebuje svá oprávnění; následná akce ne.

Podívejte se, co jednotka skutečně dělá, než na ni sáhnete. Dva příkazy vám řeknou identitu provádění a přesné příkazové řádky v platnosti, včetně všeho, co přidal soubor s přepsáním:

systemctl show -p User -p Group -p ExecStart openvpn-server@server
systemctl cat openvpn-server@server

Při výchozí instalaci z balíčku je démon root a skript --up to dědí. Potvrďte to, místo abyste to předpokládali, protože zpevněná distribuce ho mohla přesunout na vyhrazený účet a ručně napsaný drop-in může hák spouštět přes obal s jinými pravidly.

Tři pojmenované části, jeden úzký spouštěč

Uspořádání, které přežije údržbu, má událost, spouštěč a akci. Démon hlásí, že pojmenovaná relace naskočila. Spouštěč přijme tento malý fakt a požádá o jednu pevnou akci. Akce běží pod místním účtem, kterému patří soubory, jichž se dotýká. Nic v řetězu nepřijímá libovolnou cestu k příkazu.

Akce samotná je běžná jednotka služby a identita je jedna direktiva uvnitř ní:

[Service]
Type=oneshot
User=alice
Group=alice
ExecStart=/usr/local/bin/refresh-vpn-status --slot %i

Všimněte si, že User=alice platí pro celou jednotku, a systemd dokumentuje jednu výjimku, kterou stojí za to znát: příkaz s předponou + běží s plnými oprávněními bez ohledu na to. Tato předpona je legitimní nástroj pro jediný privilegovaný krok a pohodlný způsob, jak zrušit oddělení, které jste právě postavili, takže každý + v této jednotce berte jako něco, co je třeba v recenzi odůvodnit.

Udržujte vstup spouštěče minimální a ne tajný. Identifikátor relace, mapovaný účet a časové razítko obvykle stačí. Nikdy nepředávejte přes hranici fragment shellu, cestu ke spustitelnému souboru ani blok prostředí od klienta, protože přijímající strana nemá jak rozeznat legitimní hodnotu od vložené.

Ověřte identitu, která skutečně běžela

Kontrola konfigurace není důkaz. Po schváleném testovacím připojení potvrďte UID, které se souboru dotklo, a UID, které démon stále používá:

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

První řádek by měl ukázat Alici a časové razítko uvnitř vašeho testovacího okna. Druhý by měl stále ukazovat root nebo balíčkový servisní účet, což dokazuje, že se démon nepohnul. Třetí je nejlevnější způsob, jak otestovat oprávnění akce, aniž byste čekali na skutečné opětovné připojení.

Pokud je vlastníkem souboru Alice, ale jeho skupina nebo režim se mezi běhy změnily, hledejte UMask= nebo rozdíl v oprávněních adresáře, ne logickou chybu. Pokud se časové razítko vůbec nepohnulo, spouštěč se nikdy nespustil, a užitečným dalším krokem je journalctl -u pro jednotku akce, ne další opětovné připojení klienta.

Co událost připojení stále nedokazuje

Navázaný tunel je jeden fakt. Neznamená, že je odemčena relace plochy, že je dosažitelná sběrnice relace nebo že soukromý zdroj, který akce chce, je už dostupný. Jednotka oneshot pod Aliciným UID má její identitu a nic z její relace, takže cokoli, co vyžaduje grafickou sběrnici, musí jít přes podporovaný mechanismus uživatelské relace a definovat, co se stane, když se odhlásí.

Opětovná připojení se také opakují. Notebook, který usne ve vlaku, může za hodinu vyprodukovat tucet událostí připojení, a hák, který pokaždé přidá řádek, promění stavový soubor v log, který nikdo nepřečte. Udělejte akci idempotentní, nebo jí dovolte nahradit jediný záznam klíčovaný relací.

Nakonec ohraničte poloměr škod v samotné jednotce: pohled na souborový systém jen pro čtení mimo stavový adresář, žádná nová oprávnění a explicitní časový limit. Oddělení démona a uživatelské akce má cenu samo o sobě, ale má mnohem větší cenu, když akce nemůže udělat nic jiného než tu jedinou věc, kterou jste schválili.

Související články