# Lemana VPN CLI-установка корпоративного VPN `vpn.lemanapro.ru` для macOS. **Модули по умолчанию:** Core: включён; Bitwarden: включён; Touch ID: включён; DNS cleanup: включён; Swift Menu Bar app: включён; автозапуск приложения: включён; runtime-патчи: применяются автоматически перед подключением. **Credential sources:** `bitwarden` синхронизирует LDAP-пароль и TOTP seed из Bitwarden в macOS Keychain; `keychain` хранит LDAP-пароль и постоянный TOTP seed напрямую в macOS Keychain. Оба источника используют один и тот же runtime `openconnect-lite` для SSO/autofill. Репозиторий собирает в один воспроизводимый пакет то, что раньше было ручной локальной настройкой: - `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-auto`, `vpn-manual`, `vpn-manual-full`, `vpn-debug`, `vpn-fix-dns`. ## Быстрая установка ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh ``` Если установка запущена из терминала, скрипт работает как интерактивный wizard: проверит, что уже стоит, спросит как хранить credentials, предложит нужные модули и проведёт через настройку. После установки открой новый shell или выполни: ```sh exec zsh vpn ``` ## Интерактивная установка Обычный путь — запустить installer без флагов и ответить на вопросы: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh ``` Первый важный вопрос — как хранить VPN credentials: ```text Как хранить VPN credentials? 1) Bitwarden -> macOS Keychain 2) macOS Keychain: ввести LDAP password и TOTP seed сейчас 3) macOS Keychain: настрою вручную позже Выбор [1/2/3, Enter=1]: ``` Что означают варианты: - `1` — использовать Bitwarden как sync-provider: installer поставит/проверит `bw`, а при запуске `vpn` CLI переложит LDAP password и TOTP seed из Bitwarden в macOS Keychain. - `2` — бесплатный Keychain-only путь: после установки CLI спросит LDAP password и постоянный TOTP seed или `otpauth://...secret=...`, затем сохранит их в macOS Keychain. - `3` — поставить CLI/app сейчас, а credentials настроить позже командой `vpn --configure-keychain`. Дальше installer спросит только про системные модули: Touch ID для Bitwarden, sudoers, aliases, Swift Menu Bar app и автозапуск. Флаги остаются для CI, повторяемых установок и диагностики. Для обычной установки они не нужны. Проверить действия без изменений: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --dry-run ``` Пример неинтерактивного режима для автоматизации: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \ | sh -s -- --non-interactive --credential-source keychain --configure-keychain ``` Принудительно включить интерактивные вопросы: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --interactive ``` Запустить без вопросов, с выбранными флагами и дефолтами: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --non-interactive ``` Если raw URL отличается, переопредели базовый адрес: ```sh 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-auto`, `vpn-manual`, `vpn-manual-full`, `vpn-debug`, `vpn-fix-dns` | ## Статус модулей `vpn` и `vpn --status` первой строкой показывают, какие модули включены в конфиге и реально установлены на машине: ```sh vpn --status Modules: ✅ core=ok, ✅ bitwarden=on, ✅ touchid=on, ✅ dns=on, ✅ app=on, ✅ autostart=on, ✅ patches=active, ✅ keychain=password:yes/totp_seed:yes VPN disconnected ``` Emoji в human-выводе помогают быстро отличать норму, отключённый опциональный модуль и проблему: - `✅` — модуль установлен или состояние готово; - `⏭️` — модуль осознанно отключён; - `⚠️` — модуль включён, но чего-то не хватает. Значения: | Поле | Значение | | --- | --- | | `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 | | `app=on/missing` | Установлен ли `~/Applications/LemanaVPN.app` | | `autostart=on/off` | Есть ли LaunchAgent для запуска приложения при логине | | `patches=active/pending` | Применены ли runtime-патчи `openconnect-lite` | | `keychain=password:yes/totp_seed:yes` | Есть ли LDAP-пароль и TOTP seed в Keychain | JSON-режим тоже отдаёт модульный статус: ```sh vpn --status --json ``` ## Интерактивная установка Перед установкой `install.sh` печатает текущее состояние: ```text Detected state: openconnect: yes pipx: yes openconnect-lite: yes Bitwarden CLI: no Touch ID helper: no DNS cleanup: no sudoers: no/no shell aliases: no Swift: yes Menu Bar app: no LaunchAgent: no Keychain password: no Keychain TOTP seed: no ``` Если доступен терминал, скрипт ведёт установку вопросами: - выбрать credential source: Bitwarden sync, Keychain с вводом credentials сейчас, или Keychain с настройкой позже; - поставить ли Bitwarden CLI, если выбран Bitwarden и `bw` не найден; - собрать ли Touch ID helper, если выбран Bitwarden и helper не найден; - собрать ли Swift Menu Bar app, если `~/Applications/LemanaVPN.app` не найден; - включить ли автозапуск Menu Bar app при логине; - настроить ли sudoers для `openconnect` и DNS cleanup; - добавить ли алиасы в `~/.zshrc`. Флаги имеют приоритет над вопросами и нужны в основном для CI, диагностики или повторяемых unattended installs. Например, `--credential-source keychain --configure-keychain` сразу выберет Keychain-only flow, а `--no-shell` не будет предлагать алиасы. В неинтерактивной среде скрипт не задаёт вопросов и использует выбранные флаги/дефолты. Для CI или повторяемой установки лучше явно указывать `--non-interactive`. ## Логи установщика и удаления Установщик и uninstall script печатают пошаговый лог с emoji, цветом в интерактивном терминале и коротким пояснением, зачем нужен каждый шаг. Например, перед сборкой Swift-приложения установщик отдельно пишет, что `swift build` может занять время и что строки компилятора вида `[2/5] Write swift-version...` являются нормальным выводом. При удалении отдельно показывается откат runtime-патчей `openconnect-lite`, удаление sudoers/DNS wrapper, приложения, aliases и config. Отключить цвет: ```sh NO_COLOR=1 sh install.sh NO_COLOR=1 sh uninstall.sh ``` Отключить emoji: ```sh LEMANA_VPN_NO_EMOJI=1 sh install.sh LEMANA_VPN_NO_EMOJI=1 sh uninstall.sh ``` ## Модули ### Core Всегда устанавливается: - `openconnect` через Homebrew; - `pipx` через Homebrew; - `openconnect-lite` через `pipx`; - CLI `vpn-lemanapro.sh`; - `openconnect-lite` config; - DNS cleanup wrapper. ### Как работают credential sources У Lemana VPN есть два разных способа подготовить credentials, но один общий runtime-контракт: перед запуском SSO в macOS Keychain должны лежать LDAP-пароль и постоянный TOTP seed для `openconnect-lite`. Keychain entries: - service `openconnect-lite`, account `` — корпоративный LDAP/domain пароль; - service `openconnect-lite`, account `totp/` — постоянный TOTP seed. `credential_source=bitwarden` — это sync-режим. CLI открывает Bitwarden vault, читает item `LM LDAP`, берёт из него LDAP password и TOTP seed, нормализует `otpauth://...secret=...` если нужно, затем записывает оба секрета в macOS Keychain. После этого подключение идёт так же, как в остальных режимах: `openconnect-lite` читает данные из Keychain, генерирует текущий одноразовый TOTP-код из seed и заполняет Keycloak форму. `credential_source=keychain` — это бесплатный built-in режим без Bitwarden. Пользователь один раз запускает `vpn --configure-keychain` или установку с `--credential-source keychain --configure-keychain`, вводит LDAP password и постоянный TOTP seed. CLI сохраняет их напрямую в macOS Keychain и при следующих подключениях не спрашивает Bitwarden, master password или текущий OTP-код. Важно: Lemana VPN не хранит и не принимает текущий 6-значный код как постоянную настройку. Такой код живёт около 30 секунд. Для автоматического SSO нужен именно seed: raw Base32 или `otpauth://totp/...?...secret=BASE32`. Если запуск идёт из menu-bar app, интерактивного terminal prompt нет. Поэтому при пустом Keychain приложение покажет ошибку, а настройку нужно один раз выполнить в Terminal: ```sh vpn --configure-keychain ``` ### Bitwarden Включён по умолчанию. CLI при каждом запуске `vpn` пытается получить LDAP-пароль и TOTP seed из записи Bitwarden `LM LDAP`, затем записывает их в macOS Keychain для `openconnect-lite`. TOTP seed — это постоянный секрет 2FA. Сам одноразовый TOTP-код меняется каждые 30 секунд и генерируется `openconnect-lite` в момент входа. Если vault заблокирован и Touch ID helper не смог его открыть, CLI спросит `Bitwarden master password`. Это пароль от хранилища Bitwarden, а не корпоративный LDAP-пароль. Он нужен только чтобы достать LDAP password/TOTP seed из item `LM LDAP` и переложить их в macOS Keychain. Выбрать Keychain вместо Bitwarden можно прямо в installer wizard: пункт `2` вводит LDAP password и TOTP seed сразу после установки, пункт `3` оставляет настройку на потом. То же самое можно задать флагами для неинтерактивной установки: ```sh sh install.sh --non-interactive --credential-source keychain --configure-keychain ``` В этом режиме credentials нужно положить в Keychain вручную: ```sh vpn-lemanapro.sh --configure-keychain ``` Если credentials уже лежат в Keychain, подключение без Bitwarden не будет спрашивать пароль заново. CLI явно напишет, что Bitwarden отключён и используются сохранённые LDAP password/TOTP seed из macOS Keychain. ### Если Bitwarden нет Bitwarden не обязателен. Без него установка работает как обычный `openconnect-lite` profile с секретами в macOS Keychain. Интерактивная установка: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh ``` В первом вопросе выбери пункт `2`: ```text 2) macOS Keychain: ввести LDAP password и TOTP seed сейчас ``` Это встроенный бесплатный путь: не нужен Bitwarden account, платный Bitwarden TOTP или внешний password manager. Setup prompt спросит корпоративный LDAP-пароль и постоянный TOTP seed. Seed можно вставить как raw Base32 или как `otpauth://totp/...?...secret=BASE32` URI. Что понадобится: - LDAP username; - LDAP password: корпоративный LDAP/domain пароль, не мастер-пароль Bitwarden; - TOTP secret из корпоративной 2FA настройки. Важно: вводить нужно не текущие 6 цифр из authenticator-приложения, а постоянный secret. Обычно он есть в QR-коде как `secret=BASE32...` или может быть показан при ручной настройке TOTP. Не вставляй текущий 6-значный authenticator code в `vpn --configure-keychain`. Lemana VPN сохраняет в Keychain постоянный TOTP seed, а `openconnect-lite` по нему генерирует свежие одноразовые коды во время каждого SSO login. Если запуск идёт из `LemanaVPN.app`, приложение не может безопасно показать интерактивный terminal prompt для ввода LDAP/TOTP. Если Keychain пустой, приложение покажет ошибку. В этом случае один раз выполни в Terminal: ```sh vpn --configure-keychain ``` Если secret есть только в QR-коде: 1. Открой QR-код в приложении/на портале, где настраивалась 2FA. 2. Найди режим ручной настройки, где показывается secret. 3. Если доступен только QR, его нужно расшифровать любым локальным QR-сканером и взять параметр `secret`. 4. Вставь 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 sh install.sh --without-touchid ``` ### Swift Menu Bar app Включён по умолчанию. Установщик собирает Swift-приложение из исходников в репозитории и кладёт bundle в: ```sh ~/Applications/LemanaVPN.app ``` Приложение живёт в macOS status bar, запускает `~/bin/vpn-lemanapro.sh --json`, показывает состояние VPN, IP, оставшееся время сессии, health-check тоннеля и строку состояния модулей. Строка состояния модулей в меню приложения использует те же маркеры, что CLI: `✅` для готового модуля, `⏭️` для отключённого опционального модуля и `⚠️` для проблемы. Иконка строки тоже меняется: `checkmark.circle` для полностью готового набора и `exclamationmark.triangle` для неполной установки. Если в меню видно `modules unavailable: update CLI`, значит запущенное приложение обращается к старому `~/bin/vpn-lemanapro.sh`, который ещё не умеет отдавать модульный статус. Повтори установку через `curl`; установщик обновит CLI и перезапустит уже запущенное `LemanaVPN.app`. Для сборки нужен Swift 5.9+ из Xcode Command Line Tools: ```sh xcode-select --install ``` Отключить установку приложения: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --without-app ``` Оставить приложение, но отключить автозапуск: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --without-autostart ``` Ручной запуск: ```sh open ~/Applications/LemanaVPN.app ``` ## Использование ```sh vpn # подключиться vpn-auto # автоматический режим: скрытый браузер, auto-fill и submit vpn-manual # ручной режим: видимый браузер, auto-fill без submit vpn-manual-full # полностью ручной режим: видимый браузер без auto-fill и submit vpn --manual # то же самое без alias vpn --manual-full # то же самое без alias vpn --status # статус без нового подключения vpn --status --json # статус в JSON vpn-debug # видимый браузер и debug-логи vpn --manual --debug # ручной режим с debug-логами vpn-fix-dns # сбросить корпоративные DNS после аварийного завершения open ~/Applications/LemanaVPN.app # открыть Swift-приложение в menu bar ``` Режимы подключения: - `auto` — режим по умолчанию. Браузер скрытый, LDAP-пароль и TOTP берутся из Bitwarden/Keychain, Keycloak форма заполняется и отправляется автоматически. - `manual` — браузер видимый, LDAP-пароль и TOTP берутся из Bitwarden/Keychain и подставляются в поля, но кнопки входа не нажимаются. Нажимаешь сам после проверки формы. - `manual-full` — браузер видимый, auto-fill отключён полностью: поля Keycloak заполняешь и отправляешь сам. - `--manual-sso` оставлен как совместимый alias для `--manual`. - `--manual-no-autofill` оставлен как совместимый alias для `--manual-full`. Первый запуск с Bitwarden: 1. CLI проверит `bw`. 2. Если vault locked, попросит мастер-пароль. 3. Если установлен Touch ID helper, предложит сохранить мастер-пароль за Touch ID prompt. 4. Достанет `LM LDAP`, запишет LDAP-пароль и TOTP seed в Keychain. 5. Запустит `openconnect-lite` и пройдёт Keycloak SSO. ## Настройка Файл `~/.config/lemana-vpn/env`: ```sh LEMANA_VPN_USERNAME="60103293" LEMANA_VPN_CREDENTIAL_SOURCE="bitwarden" 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" ``` Для бесплатного Keychain-only источника: ```sh LEMANA_VPN_USERNAME="60103293" LEMANA_VPN_CREDENTIAL_SOURCE="keychain" LEMANA_VPN_USE_BITWARDEN="0" LEMANA_VPN_USE_TOUCHID="0" LEMANA_VPN_DNS_CLEANUP="/usr/local/sbin/lemana-vpn-dns-cleanup" ``` Для другого логина: ```sh 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 применяет их перед подключением в файле: ```sh ~/.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 без событий | | `legacy auto-fill` | Сохраняет старую рабочую схему `ApplicationWorld`, прямой `value = ...` и простой `click()` | Это ровно тот режим, на котором hidden SSO раньше стабильно проходил Keycloak | | `URL guard` | Проверяет `location.href` через `new RegExp(...)` перед auto-fill | Qt игнорирует `@include`, без guard auto-fill может кликнуть Cisco ACS и сломать SAML | | `auth redirect` | Читает 302 с `vpn.lemanapro.ru` без автоматического follow-redirect | Python `requests` может падать на TLS reset при открытии `/` на `sslvpna/b`, хотя для SAML нужен только конечный host | | `manual submit gate` | Позволяет отключить только auto-click через `LEMANA_VPN_AUTOFILL_CLICK=0` | Ручной режим видит заполненную форму, но сам решает, когда нажать вход | | `manual SSO disable` | Позволяет полностью отключить auto-fill через `vpn --manual-full` или `LEMANA_VPN_AUTOFILL_DISABLE=1` | Нужен для низкоуровневой диагностики без подстановки полей | Перед первым изменением CLI сохраняет оригинальный файл: ```sh ~/.config/lemana-vpn/patch-backups/webengine_process.py.before-lemana-vpn ``` Откат патчей выполняет uninstall script. Если backup отсутствует, автоматического rollback нет: значит файл был уже патчен старой ручной установкой или `openconnect-lite` переустановили после backup. ## Диагностика Проверить установку: ```sh command -v vpn-lemanapro.sh openconnect --version ~/.local/bin/openconnect-lite --help sudo -n /usr/local/sbin/lemana-vpn-dns-cleanup vpn --status ``` Обычный `vpn` теперь пишет путь к подробному логу `openconnect-lite`: ```sh ~/Library/Logs/LemanaVPN-openconnect-lite.log ``` Если после `Connecting to VPN (lemanapro)...` SSO завис или не видно, что происходит, смотри этот файл: ```sh tail -f ~/Library/Logs/LemanaVPN-openconnect-lite.log ``` В обычном режиме CLI также печатает heartbeat `Still waiting for SSO/openconnect-lite...` до успешного подключения, чтобы было понятно, что процесс живой. В `vpn-debug` дополнительно показываются raw-логи и видимый браузер. Если в логе повторяется один и тот же URL вида `employee.auth.lemanapro.ru/realms/employee/login-actions/authenticate`, значит hidden-браузер застрял на Keycloak до перехода в Cisco ACS. Сначала обнови и примени runtime-патчи без подключения: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh vpn-lemanapro.sh --patch-only ``` Если лог падает раньше браузера с `SSLEOFError` / `UNEXPECTED_EOF_WHILE_READING` на `sslvpna.lemanapro.ru` или `sslvpnb.lemanapro.ru`, это ломается этап определения конечного Cisco headend. Актуальный runtime-патч `auth redirect` не открывает `/` на `sslvpna/b`, а только берёт `Location` из 302 ответа `vpn.lemanapro.ru` и продолжает штатный SAML init через POST. Если SSO ломается после обновления `openconnect-lite`, запусти: ```sh vpn-debug ``` Если нужно самому посмотреть форму Keycloak, но оставить подстановку LDAP/TOTP: ```sh vpn --manual ``` В этом режиме браузер видимый, `openconnect-lite` заполняет поля из Keychain/Bitwarden, но не нажимает submit. Если нужно проверить SSO полностью вручную, без подстановки LDAP-пароля и TOTP: ```sh vpn --manual-full ``` Если установка падает на строке `install: /usr/local/sbin/...: No such file or directory`, значит на машине не было `/usr/local/sbin`. Актуальный `install.sh` создаёт эту директорию сам; достаточно повторить установку свежей командой `curl`. CLI перед подключением патчит `openconnect-lite`: - `minimal` -> `offscreen`, чтобы Qt WebEngine не падал на macOS; - добавляет `input` и `change` events для Keycloak auto-fill, сохраняя старое прямое присваивание `value = ...`; - оставляет auto-fill в старом `ApplicationWorld` и не добавляет stateful click guards/native setters; - добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS; - добавляет auth redirect patch, чтобы Python не падал на TLS reset при follow-redirect к `sslvpna/b`; - добавляет manual submit gate для видимой ручной диагностики с auto-fill, но без auto-submit. ## Удаление Рекомендуемый способ: ```sh curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/uninstall.sh | 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`; - останавливает уже запущенный процесс `LemanaVPN`, удаляет `~/Applications/LemanaVPN.app` и LaunchAgent автозапуска; - удаляет `~/.config/lemana-vpn`, если не передан `--keep-config`. Опциональные режимы: ```sh 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 ``` Ручной вариант, если нужен полный контроль: ```sh 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` удалить блок: ```sh # >>> lemana-vpn ... # <<< lemana-vpn ```