Všetky VPN pod jednou ikonou v menu bare na macOS
AnyConnect aj FortiGate pod jednou ikonou v menu bare cez SwiftBar. A prečo openfortivpn nemá dostať root cez jedno sudoers pravidlo.
Denne sa pripájam na tri VPN: Cisco AnyConnect s overením cez Duo push, AnyConnect s overením cez SMS a FortiGate, kde sa prihlasujem cez SSO v prehliadači. Tri aplikácie, tri okná, tri postupy. Keď som chcel vedieť, ktorá je práve pripojená, musel som otvoriť všetky.
Chcel som jednu ikonu v menu bare, jeden klik na pripojenie a hneď vidieť, na čo sa čaká. Najviac som sa však naučil o tom, ako dať VPN klientovi root bez toho, aby som si otvoril zadné dvierka.
Kód je verejný na GitHube ako šablóna, do ktorej doplníš vlastné VPN. Tu je hlavne to, prečo je postavený práve takto.
Z čoho sa to skladá
- SwiftBar urobí z ľubovoľného skriptu položku v menu bare. Čo skript vypíše, to je menu.
- Cisco Secure Client má popri GUI aj CLI, ktoré ovláda
expect. - openfortivpn namiesto FortiClientu, ktorý na macOS nemá CLI.
- Keychain na heslá. Skript si heslo vytiahne až vo chvíli, keď sa naň VPN klient spýta, takže v skriptoch ani v configu žiadne nie je.
Počas pripájania menu ukazuje, na čo sa čaká, napríklad „Approve the Duo push on your phone“ s tlačidlom Cancel. Keď spojenie spadne, notifikácia ponúkne Reconnect.
2FA však nechávam na sebe. Duo push schvaľujem na telefóne a SMS kód zadám do dialógu, ktorý vyskočí počas pripájania. Skript by ho vedel prečítať aj zo Správ, ale SwiftBar by potreboval Full Disk Access a obchádzal by presne to, na čo 2FA je: aby som prihlásenie potvrdil ja.
AnyConnect cez CLI
Pripojenie je jeden príkaz a pár odpovedí na výzvy. Najviac času ma stálo toto:
- Kým beží GUI, CLI sa nepripojí, ani keď je okno skryté. Aplikáciu treba naozaj ukončiť.
- Odpovede nejde poslať naraz cez pipe. Niektoré VPN servery potom zlyhajú na overení certifikátu.
- Skupinu treba vyberať číslom, nie názvom. S názvom server prihlásenie odmietne a nepovie prečo.
- Heslo nesmie prísť príliš skoro, inak ho klient zahodí. Stačí počkať pol sekundy.
- Nikdy nezapínať debug v
expect. Zapíše do logu aj heslo v čistom texte.
Root bez zadných dvierok
openfortivpn potrebuje root a SwiftBar sa na heslo pre sudo spýtať nevie. Prvé, čo asi každému napadne, je jedno pravidlo v sudoers:
jdoe ALL=(root) NOPASSWD: /opt/homebrew/bin/openfortivpn
Vyzerá nevinne, ale je to root shell pre čokoľvek, čo beží pod mojím účtom. Homebrew patrí používateľovi, takže binárku, jej knižnice aj config môže hocičo bez hesla podvrhnúť a pri ďalšom pripojení sa spustia ako root.
Namiesto toho je tu malý root helper, ktorý sa nainštaluje raz cez sudo:
- skopíruje openfortivpn aj s knižnicami na miesto, ktoré patrí rootovi,
- prijme len pár presne určených príkazov a pripojí sa len na servery zo zoznamu,
- sudoers pravidlo povoľuje presne tieto príkazy a nič iné.
Trade-off je, že po brew upgrade treba inštalátor spustiť znova. To je však zámer: nič, čo zmením ako bežný používateľ, sa k rootovi nedostane bez môjho sudo.
A ak si to chceš overiť: sudo -n -l nie je spoľahlivý test, či príkaz prejde bez hesla. Administrátorovi odpovie áno aj vtedy, keď heslo vyžaduje. Spoľahlivý test je sudo -k; sudo -n <príkaz>.
Keď nefunguje DNS
Spravujem aj DNS recursory a ich update často robím práve cez VPN. Na zariadenia chodím cez IP adresy, ale VPN sa pripája na hostname. Keby sa pri update niečo pokazilo, nechcem ostať odrezaný práve vtedy, keď to potrebujem opraviť.
Helper preto drží adresy VPN serverov v /etc/hosts. Pred každým pripojením ich overí v DNS, a keď DNS neodpovedá, použije poslednú známu adresu. Hostname ostáva rovnaký, takže overenie certifikátu funguje ďalej.
Kde začať
VPN popíšeš v vpn.conf, jeden riadok na každú. Kompletný postup aj s Keychain a root helperom je v README, skrátene:
git clone https://github.com/jozefrebjak/swiftbar-vpn.git ~/swiftbar-vpn
~/swiftbar-vpn/install.sh
$EDITOR ~/swiftbar-vpn/vpn.conf
# id type label host group user 2fa
work anyconnect Work vpn.example.com Employees jdoe duo
uni anyconnect Uni ra.example.edu External jdoe sms
work-forti forticlient Work fw.example.com:10443 - - duo
Zatiaľ sú tam AnyConnect a FortiClient, ktoré používam denne. OpenVPN a WireGuard pribudnú v dohľadnej dobe.
jozefrebjak/swiftbar-vpnVšetky VPN pod jednou ikonou v menu bare: AnyConnect a FortiClient, Duo aj SMS, zabezpečený root helper pre openfortivpn a DNS fallback v /etc/hosts.
Pozri na GitHube →Pozeral som sa aj na Tunnelblick či Viscosity, ale tie riešia hlavne OpenVPN a WireGuard, nie AnyConnect s Duo a FortiGate so SSO. Ak riešiš VPN na Macu inak alebo poznáš rozumnejší spôsob, ako dať VPN klientovi v menu bare root, ozvi sa mi na LinkedIn alebo GitHube.