331 lines
14 KiB
Markdown
331 lines
14 KiB
Markdown
# Lemana VPN
|
||
|
||
CLI-установка корпоративного VPN `vpn.lemanapro.ru` для macOS.
|
||
|
||
**Модули по умолчанию:** Core: включён; Bitwarden: включён; Touch ID: включён; DNS cleanup: включён; runtime-патчи: применяются автоматически перед подключением.
|
||
|
||
Репозиторий собирает в один воспроизводимый пакет то, что раньше было ручной локальной настройкой:
|
||
|
||
- `openconnect` как VPN-клиент;
|
||
- `openconnect-lite` для SAML SSO через Keycloak;
|
||
- опциональный Bitwarden CLI для LDAP-пароля и TOTP seed;
|
||
- опциональный Touch ID helper для мастер-пароля Bitwarden;
|
||
- безопасный DNS cleanup через root-owned wrapper;
|
||
- алиасы `vpn`, `vpn-debug`, `vpn-fix-dns`.
|
||
|
||
## Быстрая установка
|
||
|
||
```sh
|
||
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
|
||
```
|
||
|
||
После установки открой новый shell или выполни:
|
||
|
||
```sh
|
||
exec zsh
|
||
vpn
|
||
```
|
||
|
||
## Варианты установки
|
||
|
||
Полная установка, режим по умолчанию:
|
||
|
||
```sh
|
||
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
|
||
```
|
||
|
||
Без Touch ID, но с Bitwarden:
|
||
|
||
```sh
|
||
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.
|
||
|
||
```sh
|
||
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --minimal --configure-keychain
|
||
```
|
||
|
||
Проверить действия без изменений:
|
||
|
||
```sh
|
||
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --dry-run
|
||
```
|
||
|
||
Если 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 |
|
||
| `~/.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` первой строкой показывают, какие модули включены в конфиге и реально установлены на машине:
|
||
|
||
```sh
|
||
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-режим тоже отдаёт модульный статус:
|
||
|
||
```sh
|
||
vpn --status --json
|
||
```
|
||
|
||
## Модули
|
||
|
||
### Core
|
||
|
||
Всегда устанавливается:
|
||
|
||
- `openconnect` через Homebrew;
|
||
- `pipx` через Homebrew;
|
||
- `openconnect-lite` через `pipx`;
|
||
- CLI `vpn-lemanapro.sh`;
|
||
- `openconnect-lite` config;
|
||
- DNS cleanup wrapper.
|
||
|
||
### Bitwarden
|
||
|
||
Включён по умолчанию. CLI при каждом запуске `vpn` пытается получить LDAP-пароль и TOTP seed из записи Bitwarden `LM LDAP`, затем записывает их в macOS Keychain для `openconnect-lite`.
|
||
|
||
TOTP seed — это постоянный секрет 2FA. Сам одноразовый TOTP-код меняется каждые 30 секунд и генерируется `openconnect-lite` в момент входа.
|
||
|
||
Отключить:
|
||
|
||
```sh
|
||
sh install.sh --without-bitwarden
|
||
```
|
||
|
||
В этом режиме credentials нужно положить в Keychain вручную:
|
||
|
||
```sh
|
||
vpn-lemanapro.sh --configure-keychain
|
||
```
|
||
|
||
### Если Bitwarden нет
|
||
|
||
Bitwarden не обязателен. Без него установка работает как обычный `openconnect-lite` profile с секретами в macOS Keychain.
|
||
|
||
Установка:
|
||
|
||
```sh
|
||
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-коде:
|
||
|
||
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
|
||
```
|
||
|
||
## Использование
|
||
|
||
```sh
|
||
vpn # подключиться
|
||
vpn --status # статус без нового подключения
|
||
vpn --status --json # статус в JSON
|
||
vpn-debug # видимый браузер и debug-логи
|
||
vpn-fix-dns # сбросить корпоративные DNS после аварийного завершения
|
||
```
|
||
|
||
Первый запуск с 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_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"
|
||
```
|
||
|
||
Для другого логина:
|
||
|
||
```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 |
|
||
| `URL guard` | Проверяет `location.href` через `new RegExp(...)` перед auto-fill | Qt игнорирует `@include`, без guard auto-fill может кликнуть Cisco ACS и сломать SAML |
|
||
|
||
Перед первым изменением 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
|
||
```
|
||
|
||
Если SSO ломается после обновления `openconnect-lite`, запусти:
|
||
|
||
```sh
|
||
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` и `change` events для Keycloak auto-fill;
|
||
- добавляет URL guard, чтобы auto-fill не кликал submit на Cisco ACS.
|
||
|
||
## Удаление
|
||
|
||
Рекомендуемый способ:
|
||
|
||
```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`;
|
||
- удаляет `~/.config/lemana-vpn`, если не передан `--keep-config`.
|
||
|
||
Опциональные режимы:
|
||
|
||
```sh
|
||
uninstall-lemana-vpn.sh --dry-run
|
||
uninstall-lemana-vpn.sh --keep-config
|
||
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
|
||
```
|