Files
lemana-vpn/README.md

578 lines
32 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 username>` — корпоративный LDAP/domain пароль;
- service `openconnect-lite`, account `totp/<LDAP username>` — постоянный 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
```