Hooki połączenia w demonie VPN działają jako demon, a to zwykle oznacza, że działają jako root. W chwili, gdy jeden z tych hooków zapisuje w katalogu domowym kogoś lub restartuje pomocnika pulpitu, dałeś zdarzeniu sieciowemu możliwość działania jako dowolny lokalny użytkownik. Zwykły skrót, czyli odczytanie nazwy użytkownika ze zmiennej środowiskowej i przekazanie jej dalej, tego nie naprawia. Zmienia jedynie etykietę akcji, nie zmieniając konta, które ją wykonuje. Działa przekazanie w trzech częściach, w którym uprzywilejowany krok mówi, co się stało, a osobna jednostka wykonuje akcję pod właściwą tożsamością.

Nazwa użytkownika w środowisku nie jest przełącznikiem tożsamości

Unix decyduje, czego proces może dotknąć, na podstawie swojego efektywnego ID użytkownika, a nie tekstów w swoim środowisku. Ustawienie USER=alice lub wyeksportowanie LOGNAME zmienia to, co nieostrożne oprogramowanie wypisuje w swoim wyjściu; nie zmienia niczego w sprawdzaniu własności plików. Hook działający jako root z tą zmienną nadal otwiera pliki Alice jako root.

Nazwy też nie są tą samą nazwą. Logowanie dostępu zdalnego, lokalne konto Unix i nazwa pospolita certyfikatu to trzy identyfikatory, które często się pokrywają, a czasem nie. Użytkownik uwierzytelniający się jako a.smith może nie mieć w ogóle lokalnego konta albo może być mapowany na asmith przez usługę katalogową. Każdy projekt zakładający, że jeden ciąg obsługuje wszystkie trzy role, rozsypie się przy pierwszym wykonawcy.

Pierwszym pytaniem nie jest więc „który użytkownik jest połączony”, ale „które lokalne konto ma prawo wykonać tę akcję”. Odpowiedz na to z własnej bazy kont, a nie z tego, co przysłał 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

Trzecie polecenie jest tym, które ludzie pomijają. Jeśli plik docelowy należy do roota, bo utworzył go poprzedni hook, poprawnie skonfigurowana akcja użytkownika zakończy się błędem uprawnień, który wygląda jak problem z systemd i nim nie jest.

Demon zostaje tam, gdzie jest

Zmiana własnego User= usługi VPN na Alice to niewłaściwa dźwignia. Zmienia sposób, w jaki demon wiąże interfejsy, czyta klucze, zapisuje plik stanu i zarządza trasami, i przenosi całe to ryzyko do jednostki, która działała. Demon potrzebuje swoich uprawnień; późniejsza akcja nie.

Spójrz, co jednostka faktycznie robi, zanim jej dotkniesz. Dwa polecenia podają tożsamość wykonania i dokładne obowiązujące wiersze poleceń, łącznie ze wszystkim, co dodało nadpisanie:

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

Przy domyślnej instalacji z pakietu demon jest rootem, a skrypt --up to dziedziczy. Potwierdź to zamiast zakładać, bo utwardzona dystrybucja mogła przenieść go na dedykowane konto, a ręcznie napisany drop-in może uruchamiać hook przez opakowanie z innymi regułami.

Trzy nazwane części, jeden wąski wyzwalacz

Układ, który przetrwa utrzymanie, ma zdarzenie, wyzwalacz i akcję. Demon zgłasza, że nazwana sesja wstała. Wyzwalacz przyjmuje ten mały fakt i prosi o jedną ustaloną akcję. Akcja działa pod lokalnym kontem będącym właścicielem plików, których dotyka. Nic w łańcuchu nie przyjmuje dowolnej ścieżki polecenia.

Sama akcja jest zwykłą jednostką usługi, a tożsamością jest jedna dyrektywa w niej:

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

Zauważ, że User=alice dotyczy całej jednostki, a systemd dokumentuje jeden wyjątek, który warto znać: polecenie z prefiksem + działa z pełnymi uprawnieniami niezależnie od tego. Ten prefiks jest legalnym narzędziem dla jednego uprzywilejowanego kroku i wygodnym sposobem na cofnięcie separacji, którą właśnie zbudowałeś, więc każdy + w tej jednostce traktuj jako coś, co trzeba uzasadnić w przeglądzie.

Utrzymuj wejście wyzwalacza minimalne i nie-tajne. Identyfikator sesji, mapowane konto i znacznik czasu to zwykle wszystko, czego potrzeba. Nigdy nie przekazuj przez granicę fragmentu powłoki, ścieżki pliku wykonywalnego ani bloku środowiska dostarczonego przez klienta, bo strona odbierająca nie ma sposobu odróżnić wartości legalnej od wstrzykniętej.

Sprawdź tożsamość, która faktycznie działała

Przegląd konfiguracji nie jest dowodem. Po zatwierdzonym połączeniu testowym potwierdź UID, który dotknął pliku, i UID, którego demon nadal używa:

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

Pierwszy wiersz powinien pokazać Alice i znacznik czasu w twoim oknie testowym. Drugi powinien nadal pokazywać roota lub konto usługi pakietu, co dowodzi, że demon się nie przesunął. Trzeci jest najtańszym sposobem przetestowania uprawnień akcji bez czekania na prawdziwe ponowne połączenie.

Jeśli właścicielem pliku jest Alice, ale jego grupa lub tryb zmieniły się między uruchomieniami, szukaj UMask= albo różnicy uprawnień katalogu, a nie błędu logiki. Jeśli znacznik czasu w ogóle się nie przesunął, wyzwalacz nigdy nie zadziałał, a pożytecznym następnym krokiem jest journalctl -u dla jednostki akcji, a nie kolejne ponowne połączenie klienta.

Czego zdarzenie połączenia nadal nie dowodzi

Ustanowiony tunel to jeden fakt. Nie oznacza, że sesja pulpitu jest odblokowana, magistrala sesji jest osiągalna albo że prywatny zasób, którego chce akcja, jest już dostępny. Jednostka oneshot pod UID Alice ma jej tożsamość i nic z jej sesji, więc wszystko, co wymaga magistrali graficznej, musi przejść przez obsługiwany mechanizm sesji użytkownika i zdefiniować, co się dzieje, gdy jest wylogowana.

Ponowne połączenia też się powtarzają. Laptop, który zasypia w pociągu, może wygenerować tuzin zdarzeń połączenia w ciągu godziny, a hook, który za każdym razem dopisuje wiersz, zamienia plik stanu w dziennik, którego nikt nie potrafi czytać. Uczyń akcję idempotentną albo pozwól jej zastępować jeden rekord kluczowany sesją.

Na koniec ogranicz promień szkód w samej jednostce: widok systemu plików tylko do odczytu poza katalogiem stanu, żadnych nowych uprawnień i jawny limit czasu. Rozdzielenie demona i akcji użytkownika jest warte zachodu samo w sobie, ale jest o wiele więcej warte, gdy akcja nie może zrobić niczego poza tym jednym, co zatwierdziłeś.

Powiązane artykuły