Уточни подключение без Bitwarden

This commit is contained in:
2026-05-19 18:31:06 +03:00
parent 417138b3b1
commit cdc0207e94
5 changed files with 118 additions and 14 deletions

View File

@@ -200,6 +200,8 @@ LEMANA_VPN_NO_EMOJI=1 sh uninstall.sh
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.
Отключить:
```sh
@@ -212,6 +214,8 @@ sh install.sh --without-bitwarden
vpn-lemanapro.sh --configure-keychain
```
Если credentials уже лежат в Keychain, подключение без Bitwarden не будет спрашивать пароль заново. CLI явно напишет, что Bitwarden отключён и используются сохранённые LDAP password/TOTP seed из macOS Keychain.
### Если Bitwarden нет
Bitwarden не обязателен. Без него установка работает как обычный `openconnect-lite` profile с секретами в macOS Keychain.
@@ -226,11 +230,17 @@ curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh \
Что понадобится:
- LDAP username;
- LDAP password;
- LDAP password: корпоративный LDAP/domain пароль, не мастер-пароль Bitwarden;
- TOTP secret из корпоративной 2FA настройки.
Важно: вводить нужно не текущие 6 цифр из authenticator-приложения, а постоянный secret. Обычно он есть в QR-коде как `secret=BASE32...` или может быть показан при ручной настройке TOTP.
Если запуск идёт из `LemanaVPN.app`, приложение не может безопасно показать интерактивный terminal prompt для ввода LDAP/TOTP. Если Keychain пустой, приложение покажет ошибку. В этом случае один раз выполни в Terminal:
```sh
vpn --configure-keychain
```
Если secret есть только в QR-коде:
1. Открой QR-код в приложении/на портале, где настраивалась 2FA.
@@ -416,7 +426,7 @@ uninstall-lemana-vpn.sh
- удаляет sudoers rules и DNS cleanup wrapper;
- удаляет блок `lemana-vpn` из `~/.zshrc`;
- удаляет `~/.config/openconnect-lite/config.toml`;
- удаляет `~/Applications/LemanaVPN.app` и LaunchAgent автозапуска;
- останавливает уже запущенный процесс `LemanaVPN`, удаляет `~/Applications/LemanaVPN.app` и LaunchAgent автозапуска;
- удаляет `~/.config/lemana-vpn`, если не передан `--keep-config`.
Опциональные режимы:

View File

@@ -361,6 +361,12 @@ class VPNManager: ObservableObject {
case "bw_synced":
log(" Credentials synced to Keychain")
return
case "keychain_ready":
log(" LDAP credentials are ready in macOS Keychain")
return
case "keychain_required":
log(" LDAP credentials are missing or incomplete")
return
case "connecting":
state = .connecting
case "connected":

View File

@@ -355,18 +355,75 @@ if totp_secret:
PY
}
_can_prompt() {
[[ -t 0 ]]
}
_configure_keychain() {
local password totp_secret
read -rsp "LDAP password: " password
local password_present=false totp_present=false
_keychain_has openconnect-lite "$KC_USERNAME" && password_present=true
_keychain_has openconnect-lite "totp/$KC_USERNAME" && totp_present=true
printf 'Manual LDAP credentials setup for Lemana VPN\n'
printf 'User: %s\n' "$KC_USERNAME"
printf 'LDAP password: your corporate LDAP/domain password, not the Bitwarden master password.\n'
printf 'TOTP seed: permanent BASE32 secret from 2FA setup, not the current 6-digit code.\n'
printf 'Saved values go to macOS Keychain service openconnect-lite.\n\n'
if $password_present; then
read -rsp "Corporate LDAP password for $KC_USERNAME [leave empty to keep saved password]: " password
else
read -rsp "Corporate LDAP password for $KC_USERNAME: " password
fi
printf '\n'
read -rsp "TOTP secret (BASE32, optional if already stored): " totp_secret
if $totp_present; then
read -rsp "TOTP seed BASE32 [leave empty to keep saved seed]: " totp_secret
else
read -rsp "TOTP seed BASE32 from 2FA setup: " totp_secret
fi
printf '\n'
if [[ -z "$password" ]]; then
printf 'Empty password, nothing was saved.\n' >&2
if [[ -z "$password" && "$password_present" != "true" ]]; then
printf 'LDAP password is required because no saved password was found.\n' >&2
return 1
fi
if [[ -z "$totp_secret" && "$totp_present" != "true" ]]; then
printf 'TOTP seed is required because no saved seed was found. Use the BASE32 setup secret, not the current 6-digit code.\n' >&2
return 1
fi
_store_keychain "$password" "$totp_secret"
printf 'Credentials saved to macOS Keychain for openconnect-lite/%s.\n' "$KC_USERNAME"
printf 'Credentials are ready in macOS Keychain for openconnect-lite/%s.\n' "$KC_USERNAME"
}
_ensure_keychain_credentials() {
local password_present=false totp_present=false
_keychain_has openconnect-lite "$KC_USERNAME" && password_present=true
_keychain_has openconnect-lite "totp/$KC_USERNAME" && totp_present=true
if [[ "$password_present" == "true" && "$totp_present" == "true" ]]; then
if [[ "$USE_BITWARDEN" == "1" ]]; then
_emit '{"event":"keychain_ready","source":"keychain"}' "LDAP credentials are ready in macOS Keychain for $KC_USERNAME."
else
_emit '{"event":"keychain_ready","source":"keychain","bitwarden":false}' "Bitwarden is disabled. Using saved LDAP password and TOTP seed from macOS Keychain for $KC_USERNAME."
fi
return 0
fi
if [[ "$USE_BITWARDEN" == "1" ]]; then
_emit '{"event":"keychain_required","bitwarden":true}' "Bitwarden sync did not produce complete Keychain credentials."
else
_emit '{"event":"keychain_required","bitwarden":false}' "Bitwarden is disabled and saved LDAP credentials are incomplete."
fi
if ! _can_prompt; then
_emit '{"event":"error","message":"LDAP credentials are missing. Run vpn --configure-keychain in Terminal, or reinstall with --configure-keychain."}' \
"LDAP credentials are missing. Run: vpn --configure-keychain"
return 1
fi
_configure_keychain
}
_bw_cache_session() {
@@ -410,8 +467,13 @@ _bw_unlock() {
fi
local manual_pw
_emit '{"event":"bw_manual"}' "Unlocking Bitwarden vault..."
read -rsp "Bitwarden master password: " manual_pw
if ! _can_prompt; then
printf 'Bitwarden vault is locked and no interactive terminal is available. Using existing Keychain credentials.\n' >&2
return 1
fi
_emit '{"event":"bw_manual"}' "Bitwarden vault is locked. Enter Bitwarden master password to sync LDAP credentials."
read -rsp "Bitwarden master password (not LDAP password): " manual_pw
printf '\n'
if [[ -z "$manual_pw" ]]; then
printf 'Empty Bitwarden password. Using existing Keychain credentials.\n' >&2
@@ -428,7 +490,7 @@ _bw_unlock() {
if [[ "$USE_TOUCHID" == "1" && -x "$KC_FP" ]]; then
local save_choice
read -rp "Save Bitwarden master password behind Touch ID? [Y/n] " save_choice
read -rp "Save Bitwarden master password behind Touch ID for next VPN unlock? [Y/n] " save_choice
if [[ "${save_choice:-y}" =~ ^[Yy]?$ ]]; then
printf '%s' "$manual_pw" | "$KC_FP" set "$BW_KC_SERVICE" "$BW_KC_ACCOUNT_MASTER" >/dev/null 2>&1 \
&& printf 'Saved. Next unlock can use Touch ID.\n' \
@@ -544,19 +606,19 @@ if $CONFIGURE_KEYCHAIN_MODE; then
exit 0
fi
trap '_dns_cleanup; _clear_status' EXIT
if ! $JSON_MODE; then
_module_status_human
else
printf '{"event":"modules","modules":%s}\n' "$(_module_status_json)"
fi
_patch_oc
_sync_bitwarden
_ensure_keychain_credentials
_patch_oc
_emit '{"event":"connecting"}' "Connecting to VPN (lemanapro)..."
_write_status "{\"pid\":$$,\"state\":\"connecting\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
trap '_dns_cleanup; _clear_status' EXIT
display_mode="hidden"
log_level=""

View File

@@ -52,6 +52,7 @@ uninstall_output="$(
printf '%s\n' "$uninstall_output" | grep -q 'Начинаю удаление Lemana VPN'
printf '%s\n' "$uninstall_output" | grep -q 'Проверяю runtime-патчи openconnect-lite'
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю sudoers и DNS cleanup wrapper'
printf '%s\n' "$uninstall_output" | grep -q 'killall LemanaVPN # if running'
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю VPN-записи из macOS Keychain'
if printf '%s\n' "$uninstall_output" | grep -q "$esc"; then
@@ -59,6 +60,25 @@ if printf '%s\n' "$uninstall_output" | grep -q "$esc"; then
exit 1
fi
missing_user="lemana-smoke-missing-$$"
set +e
manual_output="$(
HOME="$HOME" \
LEMANA_VPN_USERNAME="$missing_user" \
LEMANA_VPN_USE_BITWARDEN=0 \
bash "$ROOT/bin/vpn-lemanapro.sh" --json 2>&1
)"
manual_code=$?
set -e
[ "$manual_code" -ne 0 ]
printf '%s\n' "$manual_output" | grep -q '"event":"keychain_required"'
printf '%s\n' "$manual_output" | grep -q 'vpn --configure-keychain'
if printf '%s\n' "$manual_output" | grep -q 'Cleaning up VPN DNS'; then
echo "missing manual credentials should fail before VPN cleanup trap is installed" >&2
exit 1
fi
fake_pwd="$TMP_DIR/fake-pwd"
mkdir -p "$fake_pwd/bin"
printf 'stale local cli\n' > "$fake_pwd/bin/vpn-lemanapro.sh"

View File

@@ -247,10 +247,16 @@ main() {
if [ "$KEEP_APP" -eq 0 ]; then
log_step "Удаляю Menu Bar app"
log_detail "Сначала отключаю LaunchAgent, затем удаляю $APP_DIR."
log_detail "Сначала останавливаю уже запущенное LemanaVPN, затем отключаю LaunchAgent и удаляю $APP_DIR."
if [ "$DRY_RUN" -eq 0 ]; then
if pgrep -x LemanaVPN >/dev/null 2>&1; then
log_info "Stopping running LemanaVPN process"
killall LemanaVPN >/dev/null 2>&1 || true
sleep 1
fi
launchctl unload "$LAUNCH_AGENT" >/dev/null 2>&1 || true
else
printf '+ killall LemanaVPN # if running\n'
printf '+ launchctl unload %s\n' "$LAUNCH_AGENT"
fi
run rm -f "$LAUNCH_AGENT"