Lemana VPN
CLI-установка корпоративного VPN vpn.lemanapro.ru для macOS.
Модули по умолчанию: Core: включён; Bitwarden: включён; Touch ID: включён; DNS cleanup: включён; Swift Menu Bar app: включён; автозапуск приложения: включён; runtime-патчи: применяются автоматически перед подключением.
Репозиторий собирает в один воспроизводимый пакет то, что раньше было ручной локальной настройкой:
openconnectкак VPN-клиент;openconnect-liteдля SAML SSO через Keycloak;- опциональный Bitwarden CLI для LDAP-пароля и TOTP seed;
- опциональный Touch ID helper для мастер-пароля Bitwarden;
- Swift Menu Bar app
LemanaVPN.app; - безопасный DNS cleanup через root-owned wrapper;
- алиасы
vpn,vpn-debug,vpn-fix-dns.
Быстрая установка
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
Если установка запущена из терминала, скрипт сначала проверит, что уже стоит, и спросит по отсутствующим опциональным модулям.
После установки открой новый shell или выполни:
exec zsh
vpn
Варианты установки
Полная установка, режим по умолчанию:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
Без Touch ID, но с Bitwarden:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --without-touchid
Минимальная установка без Bitwarden и Touch ID. В macOS Keychain вручную будут записаны LDAP-пароль и TOTP secret. Не текущий 30-секундный TOTP-код, а постоянный seed из настройки 2FA.
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --minimal --configure-keychain
Проверить действия без изменений:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --dry-run
Принудительно включить интерактивные вопросы:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --interactive
Запустить без вопросов, с выбранными флагами и дефолтами:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --non-interactive
Если raw URL отличается, переопредели базовый адрес:
curl -fsSL https://example.org/dokril/lemana-vpn/raw/branch/main/install.sh \
| LEMANA_VPN_RAW_BASE_URL=https://example.org/dokril/lemana-vpn/raw/branch/main sh
Что ставится
| Путь | Назначение |
|---|---|
~/bin/vpn-lemanapro.sh |
Основной CLI для подключения, статуса и sync секретов |
~/bin/uninstall-lemana-vpn.sh |
Локальный uninstall helper |
~/bin/keychain-fingerprint |
Опциональный Touch ID helper для мастер-пароля Bitwarden |
~/Applications/LemanaVPN.app |
Swift Menu Bar app для подключения из status bar |
~/Library/LaunchAgents/ru.dokops.LemanaVPN.plist |
Автозапуск Menu Bar app при логине |
~/.config/lemana-vpn/env |
Локальная конфигурация модулей |
~/.config/lemana-vpn/patch-backups/ |
Backup исходника openconnect-lite перед runtime-патчами |
~/.config/openconnect-lite/config.toml |
Профиль SSO и auto-fill правила Keycloak |
/usr/local/sbin/lemana-vpn-dns-cleanup |
Root-owned wrapper для сброса только корпоративных DNS |
/etc/sudoers.d/lemana-vpn-openconnect |
NOPASSWD только для openconnect |
/etc/sudoers.d/lemana-vpn-dns |
NOPASSWD только для DNS cleanup wrapper |
~/.zshrc |
Идемпотентный блок алиасов vpn, vpn-debug, vpn-fix-dns |
Статус модулей
vpn и vpn --status первой строкой показывают, какие модули включены в конфиге и реально установлены на машине:
vpn --status
Modules: core=ok, bitwarden=on, touchid=on, dns=on, patches=active, keychain=password:yes/totp_seed:yes
VPN disconnected
Значения:
| Поле | Значение |
|---|---|
core=ok |
Есть openconnect, openconnect-lite и config |
bitwarden=on |
Модуль включён и bw установлен |
bitwarden=off |
Модуль отключён через --without-bitwarden или LEMANA_VPN_USE_BITWARDEN=0 |
bitwarden=missing |
Модуль включён, но bw не найден |
touchid=on/off/missing |
Состояние Touch ID helper |
dns=on/missing |
Наличие DNS cleanup wrapper |
patches=active/pending |
Применены ли runtime-патчи openconnect-lite |
keychain=password:yes/totp_seed:yes |
Есть ли LDAP-пароль и TOTP seed в Keychain |
JSON-режим тоже отдаёт модульный статус:
vpn --status --json
Интерактивная установка
Перед установкой install.sh печатает текущее состояние:
Detected state:
openconnect: yes
pipx: yes
openconnect-lite: yes
Bitwarden CLI: no
Touch ID helper: no
Swift: yes
Menu Bar app: no
LaunchAgent: no
DNS cleanup: no
sudoers: no/no
shell aliases: no
Keychain password: no
Keychain TOTP seed: no
Если доступен терминал (/dev/tty), скрипт спросит только по тому, чего не хватает:
- поставить ли Bitwarden CLI, если
bwне найден; - собрать ли Touch ID helper, если его нет и Bitwarden включён;
- собрать ли Swift Menu Bar app, если
~/Applications/LemanaVPN.appне найден; - включить ли автозапуск Menu Bar app при логине;
- настроить ли sudoers для
openconnectи DNS cleanup; - добавить ли алиасы в
~/.zshrc; - записать ли LDAP-пароль и TOTP seed в Keychain, если Bitwarden отключён.
Флаги имеют приоритет над вопросами. Например, --without-bitwarden не будет спрашивать про Bitwarden, а --no-shell не будет предлагать алиасы.
В неинтерактивной среде скрипт не задаёт вопросов и использует выбранные флаги/дефолты. Для CI или повторяемой установки лучше явно указывать --non-interactive.
Модули
Core
Всегда устанавливается:
openconnectчерез Homebrew;pipxчерез Homebrew;openconnect-liteчерезpipx;- CLI
vpn-lemanapro.sh; openconnect-liteconfig;- DNS cleanup wrapper.
Bitwarden
Включён по умолчанию. CLI при каждом запуске vpn пытается получить LDAP-пароль и TOTP seed из записи Bitwarden LM LDAP, затем записывает их в macOS Keychain для openconnect-lite.
TOTP seed — это постоянный секрет 2FA. Сам одноразовый TOTP-код меняется каждые 30 секунд и генерируется openconnect-lite в момент входа.
Отключить:
sh install.sh --without-bitwarden
В этом режиме credentials нужно положить в Keychain вручную:
vpn-lemanapro.sh --configure-keychain
Если Bitwarden нет
Bitwarden не обязателен. Без него установка работает как обычный openconnect-lite profile с секретами в macOS Keychain.
Установка:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
| sh -s -- --without-bitwarden --without-touchid --configure-keychain
Что понадобится:
- LDAP username;
- LDAP password;
- TOTP secret из корпоративной 2FA настройки.
Важно: вводить нужно не текущие 6 цифр из authenticator-приложения, а постоянный secret. Обычно он есть в QR-коде как secret=BASE32... или может быть показан при ручной настройке TOTP.
Если secret есть только в QR-коде:
- Открой QR-код в приложении/на портале, где настраивалась 2FA.
- Найди режим ручной настройки, где показывается secret.
- Если доступен только QR, его нужно расшифровать любым локальным QR-сканером и взять параметр
secret. - Вставь secret в prompt
TOTP secret (BASE32...).
Если TOTP secret получить нельзя, автоматический headless-вход невозможен: openconnect-lite не сможет сам генерировать свежий TOTP-код на каждом входе.
Touch ID
Включён по умолчанию. Установщик собирает keychain-fingerprint из https://github.com/dss99911/keychain-fingerprint.git и кладёт бинарник в ~/bin/keychain-fingerprint.
Важно: этот helper показывает системный Touch ID prompt перед чтением мастер-пароля Bitwarden, но это не аппаратный Keychain ACL. Это удобный локальный гейт поверх записи Keychain.
Отключить:
sh install.sh --without-touchid
Swift Menu Bar app
Включён по умолчанию. Установщик собирает Swift-приложение из исходников в репозитории и кладёт bundle в:
~/Applications/LemanaVPN.app
Приложение живёт в macOS status bar, запускает ~/bin/vpn-lemanapro.sh --json, показывает состояние VPN, IP, оставшееся время сессии, health-check тоннеля и строку состояния модулей.
Для сборки нужен Swift 5.9+ из Xcode Command Line Tools:
xcode-select --install
Отключить установку приложения:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --without-app
Оставить приложение, но отключить автозапуск:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --without-autostart
Ручной запуск:
open ~/Applications/LemanaVPN.app
Использование
vpn # подключиться
vpn --status # статус без нового подключения
vpn --status --json # статус в JSON
vpn-debug # видимый браузер и debug-логи
vpn-fix-dns # сбросить корпоративные DNS после аварийного завершения
open ~/Applications/LemanaVPN.app # открыть Swift-приложение в menu bar
Первый запуск с Bitwarden:
- CLI проверит
bw. - Если vault locked, попросит мастер-пароль.
- Если установлен Touch ID helper, предложит сохранить мастер-пароль за Touch ID prompt.
- Достанет
LM LDAP, запишет LDAP-пароль и TOTP seed в Keychain. - Запустит
openconnect-liteи пройдёт Keycloak SSO.
Настройка
Файл ~/.config/lemana-vpn/env:
LEMANA_VPN_USERNAME="60103293"
LEMANA_VPN_BW_ITEM="LM LDAP"
LEMANA_VPN_USE_BITWARDEN="1"
LEMANA_VPN_USE_TOUCHID="1"
LEMANA_VPN_DNS_CLEANUP="/usr/local/sbin/lemana-vpn-dns-cleanup"
Для другого логина:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
| sh -s -- --username 12345678
Bitwarden item
Нужна запись:
- название:
LM LDAP; - username: корпоративный LDAP логин;
- password: LDAP пароль;
- TOTP:
otpauth://...secret=BASE32...или raw BASE32 secret.
Это не 6-значный одноразовый код. В Bitwarden должен лежать постоянный TOTP secret, из которого коды генерируются автоматически.
Почему DNS wrapper, а не wildcard sudoers
Старый вариант давал NOPASSWD на networksetup -setdnsservers *. Это слишком широкое право: любой локальный процесс пользователя мог поменять DNS на произвольный сервер.
Новый вариант разрешает sudo только на /usr/local/sbin/lemana-vpn-dns-cleanup. Wrapper сбрасывает DNS только если текущий DNS начинается с 10., то есть похож на корпоративный VPN DNS.
Runtime-патчи openconnect-lite
openconnect-lite работает, но для текущей macOS + Keycloak SSO цепочки ему нужны три runtime-патча. CLI применяет их перед подключением в файле:
~/.local/pipx/venvs/openconnect-lite/lib/python*/site-packages/openconnect_lite/browser/webengine_process.py
Патчи:
| Патч | Что меняет | Зачем |
|---|---|---|
minimal -> offscreen |
Меняет Qt platform mode для скрытого браузера | minimal падает с Qt WebEngine на macOS |
input/change events |
После value = ... отправляет DOM events |
Keycloak не реагирует на прямую запись value |
URL guard |
Проверяет location.href через new RegExp(...) перед auto-fill |
Qt игнорирует @include, без guard auto-fill может кликнуть Cisco ACS и сломать SAML |
Перед первым изменением CLI сохраняет оригинальный файл:
~/.config/lemana-vpn/patch-backups/webengine_process.py.before-lemana-vpn
Откат патчей выполняет uninstall script. Если backup отсутствует, автоматического rollback нет: значит файл был уже патчен старой ручной установкой или openconnect-lite переустановили после backup.
Диагностика
Проверить установку:
command -v vpn-lemanapro.sh
openconnect --version
~/.local/bin/openconnect-lite --help
sudo -n /usr/local/sbin/lemana-vpn-dns-cleanup
vpn --status
Если SSO ломается после обновления openconnect-lite, запусти:
vpn-debug
Если установка падает на строке install: /usr/local/sbin/...: No such file or directory, значит на машине не было /usr/local/sbin. Актуальный install.sh создаёт эту директорию сам; достаточно повторить установку свежей командой curl.
CLI перед подключением патчит openconnect-lite:
minimal->offscreen, чтобы Qt WebEngine не падал на macOS;- добавляет
inputиchangeevents для Keycloak auto-fill; - добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS.
Удаление
Рекомендуемый способ:
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/uninstall.sh | sh
Или локально:
uninstall-lemana-vpn.sh
Что делает uninstall:
- восстанавливает
openconnect-liteиз backup, если backup есть; - удаляет
vpn-lemanapro.shиuninstall-lemana-vpn.sh; - удаляет sudoers rules и DNS cleanup wrapper;
- удаляет блок
lemana-vpnиз~/.zshrc; - удаляет
~/.config/openconnect-lite/config.toml; - удаляет
~/Applications/LemanaVPN.appи LaunchAgent автозапуска; - удаляет
~/.config/lemana-vpn, если не передан--keep-config.
Опциональные режимы:
uninstall-lemana-vpn.sh --dry-run
uninstall-lemana-vpn.sh --keep-config
uninstall-lemana-vpn.sh --keep-app
uninstall-lemana-vpn.sh --remove-keychain
uninstall-lemana-vpn.sh --remove-touchid-helper
uninstall-lemana-vpn.sh --remove-openconnect-lite
Ручной вариант, если нужен полный контроль:
rm -f ~/bin/vpn-lemanapro.sh ~/bin/uninstall-lemana-vpn.sh
rm -rf ~/.config/lemana-vpn
rm -f ~/.config/openconnect-lite/config.toml
sudo rm -f /usr/local/sbin/lemana-vpn-dns-cleanup
sudo rm -f /etc/sudoers.d/lemana-vpn-openconnect /etc/sudoers.d/lemana-vpn-dns
Из ~/.zshrc удалить блок:
# >>> lemana-vpn
...
# <<< lemana-vpn