Уточни подключение без Bitwarden
This commit is contained in:
14
README.md
14
README.md
@@ -200,6 +200,8 @@ LEMANA_VPN_NO_EMOJI=1 sh uninstall.sh
|
|||||||
|
|
||||||
TOTP seed — это постоянный секрет 2FA. Сам одноразовый TOTP-код меняется каждые 30 секунд и генерируется `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.
|
||||||
|
|
||||||
Отключить:
|
Отключить:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -212,6 +214,8 @@ sh install.sh --without-bitwarden
|
|||||||
vpn-lemanapro.sh --configure-keychain
|
vpn-lemanapro.sh --configure-keychain
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Если credentials уже лежат в Keychain, подключение без Bitwarden не будет спрашивать пароль заново. CLI явно напишет, что Bitwarden отключён и используются сохранённые LDAP password/TOTP seed из macOS Keychain.
|
||||||
|
|
||||||
### Если Bitwarden нет
|
### Если Bitwarden нет
|
||||||
|
|
||||||
Bitwarden не обязателен. Без него установка работает как обычный `openconnect-lite` profile с секретами в macOS Keychain.
|
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 username;
|
||||||
- LDAP password;
|
- LDAP password: корпоративный LDAP/domain пароль, не мастер-пароль Bitwarden;
|
||||||
- TOTP secret из корпоративной 2FA настройки.
|
- TOTP secret из корпоративной 2FA настройки.
|
||||||
|
|
||||||
Важно: вводить нужно не текущие 6 цифр из authenticator-приложения, а постоянный secret. Обычно он есть в QR-коде как `secret=BASE32...` или может быть показан при ручной настройке TOTP.
|
Важно: вводить нужно не текущие 6 цифр из authenticator-приложения, а постоянный secret. Обычно он есть в QR-коде как `secret=BASE32...` или может быть показан при ручной настройке TOTP.
|
||||||
|
|
||||||
|
Если запуск идёт из `LemanaVPN.app`, приложение не может безопасно показать интерактивный terminal prompt для ввода LDAP/TOTP. Если Keychain пустой, приложение покажет ошибку. В этом случае один раз выполни в Terminal:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
vpn --configure-keychain
|
||||||
|
```
|
||||||
|
|
||||||
Если secret есть только в QR-коде:
|
Если secret есть только в QR-коде:
|
||||||
|
|
||||||
1. Открой QR-код в приложении/на портале, где настраивалась 2FA.
|
1. Открой QR-код в приложении/на портале, где настраивалась 2FA.
|
||||||
@@ -416,7 +426,7 @@ uninstall-lemana-vpn.sh
|
|||||||
- удаляет sudoers rules и DNS cleanup wrapper;
|
- удаляет sudoers rules и DNS cleanup wrapper;
|
||||||
- удаляет блок `lemana-vpn` из `~/.zshrc`;
|
- удаляет блок `lemana-vpn` из `~/.zshrc`;
|
||||||
- удаляет `~/.config/openconnect-lite/config.toml`;
|
- удаляет `~/.config/openconnect-lite/config.toml`;
|
||||||
- удаляет `~/Applications/LemanaVPN.app` и LaunchAgent автозапуска;
|
- останавливает уже запущенный процесс `LemanaVPN`, удаляет `~/Applications/LemanaVPN.app` и LaunchAgent автозапуска;
|
||||||
- удаляет `~/.config/lemana-vpn`, если не передан `--keep-config`.
|
- удаляет `~/.config/lemana-vpn`, если не передан `--keep-config`.
|
||||||
|
|
||||||
Опциональные режимы:
|
Опциональные режимы:
|
||||||
|
|||||||
@@ -361,6 +361,12 @@ class VPNManager: ObservableObject {
|
|||||||
case "bw_synced":
|
case "bw_synced":
|
||||||
log(" Credentials synced to Keychain")
|
log(" Credentials synced to Keychain")
|
||||||
return
|
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":
|
case "connecting":
|
||||||
state = .connecting
|
state = .connecting
|
||||||
case "connected":
|
case "connected":
|
||||||
|
|||||||
@@ -355,18 +355,75 @@ if totp_secret:
|
|||||||
PY
|
PY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_can_prompt() {
|
||||||
|
[[ -t 0 ]]
|
||||||
|
}
|
||||||
|
|
||||||
_configure_keychain() {
|
_configure_keychain() {
|
||||||
local password totp_secret
|
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'
|
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'
|
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
|
return 1
|
||||||
fi
|
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"
|
_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() {
|
_bw_cache_session() {
|
||||||
@@ -410,8 +467,13 @@ _bw_unlock() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
local manual_pw
|
local manual_pw
|
||||||
_emit '{"event":"bw_manual"}' "Unlocking Bitwarden vault..."
|
if ! _can_prompt; then
|
||||||
read -rsp "Bitwarden master password: " manual_pw
|
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'
|
printf '\n'
|
||||||
if [[ -z "$manual_pw" ]]; then
|
if [[ -z "$manual_pw" ]]; then
|
||||||
printf 'Empty Bitwarden password. Using existing Keychain credentials.\n' >&2
|
printf 'Empty Bitwarden password. Using existing Keychain credentials.\n' >&2
|
||||||
@@ -428,7 +490,7 @@ _bw_unlock() {
|
|||||||
|
|
||||||
if [[ "$USE_TOUCHID" == "1" && -x "$KC_FP" ]]; then
|
if [[ "$USE_TOUCHID" == "1" && -x "$KC_FP" ]]; then
|
||||||
local save_choice
|
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
|
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 '%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' \
|
&& printf 'Saved. Next unlock can use Touch ID.\n' \
|
||||||
@@ -544,19 +606,19 @@ if $CONFIGURE_KEYCHAIN_MODE; then
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
trap '_dns_cleanup; _clear_status' EXIT
|
|
||||||
|
|
||||||
if ! $JSON_MODE; then
|
if ! $JSON_MODE; then
|
||||||
_module_status_human
|
_module_status_human
|
||||||
else
|
else
|
||||||
printf '{"event":"modules","modules":%s}\n' "$(_module_status_json)"
|
printf '{"event":"modules","modules":%s}\n' "$(_module_status_json)"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
_patch_oc
|
|
||||||
_sync_bitwarden
|
_sync_bitwarden
|
||||||
|
_ensure_keychain_credentials
|
||||||
|
_patch_oc
|
||||||
|
|
||||||
_emit '{"event":"connecting"}' "Connecting to VPN (lemanapro)..."
|
_emit '{"event":"connecting"}' "Connecting to VPN (lemanapro)..."
|
||||||
_write_status "{\"pid\":$$,\"state\":\"connecting\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
_write_status "{\"pid\":$$,\"state\":\"connecting\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}"
|
||||||
|
trap '_dns_cleanup; _clear_status' EXIT
|
||||||
|
|
||||||
display_mode="hidden"
|
display_mode="hidden"
|
||||||
log_level=""
|
log_level=""
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ uninstall_output="$(
|
|||||||
printf '%s\n' "$uninstall_output" | grep -q 'Начинаю удаление Lemana VPN'
|
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 'Проверяю runtime-патчи openconnect-lite'
|
||||||
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю sudoers и DNS cleanup wrapper'
|
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'
|
printf '%s\n' "$uninstall_output" | grep -q 'Удаляю VPN-записи из macOS Keychain'
|
||||||
|
|
||||||
if printf '%s\n' "$uninstall_output" | grep -q "$esc"; then
|
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
|
exit 1
|
||||||
fi
|
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"
|
fake_pwd="$TMP_DIR/fake-pwd"
|
||||||
mkdir -p "$fake_pwd/bin"
|
mkdir -p "$fake_pwd/bin"
|
||||||
printf 'stale local cli\n' > "$fake_pwd/bin/vpn-lemanapro.sh"
|
printf 'stale local cli\n' > "$fake_pwd/bin/vpn-lemanapro.sh"
|
||||||
|
|||||||
@@ -247,10 +247,16 @@ main() {
|
|||||||
|
|
||||||
if [ "$KEEP_APP" -eq 0 ]; then
|
if [ "$KEEP_APP" -eq 0 ]; then
|
||||||
log_step "Удаляю Menu Bar app"
|
log_step "Удаляю Menu Bar app"
|
||||||
log_detail "Сначала отключаю LaunchAgent, затем удаляю $APP_DIR."
|
log_detail "Сначала останавливаю уже запущенное LemanaVPN, затем отключаю LaunchAgent и удаляю $APP_DIR."
|
||||||
if [ "$DRY_RUN" -eq 0 ]; then
|
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
|
launchctl unload "$LAUNCH_AGENT" >/dev/null 2>&1 || true
|
||||||
else
|
else
|
||||||
|
printf '+ killall LemanaVPN # if running\n'
|
||||||
printf '+ launchctl unload %s\n' "$LAUNCH_AGENT"
|
printf '+ launchctl unload %s\n' "$LAUNCH_AGENT"
|
||||||
fi
|
fi
|
||||||
run rm -f "$LAUNCH_AGENT"
|
run rm -f "$LAUNCH_AGENT"
|
||||||
|
|||||||
Reference in New Issue
Block a user