diff --git a/README.md b/README.md index 8eacfcc..b8c7ad9 100644 --- a/README.md +++ b/README.md @@ -391,6 +391,20 @@ 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-логи и видимый браузер. + Если SSO ломается после обновления `openconnect-lite`, запусти: ```sh diff --git a/app/Sources/LemanaVPN/VPNManager.swift b/app/Sources/LemanaVPN/VPNManager.swift index 3f2f802..05c7ea6 100644 --- a/app/Sources/LemanaVPN/VPNManager.swift +++ b/app/Sources/LemanaVPN/VPNManager.swift @@ -367,6 +367,12 @@ class VPNManager: ObservableObject { case "keychain_required": log(" LDAP credentials are missing or incomplete") return + case "log": + if let message = event.message { log(" \(message)") } + return + case "waiting": + if let message = event.message { log(" \(message)") } + return case "connecting": state = .connecting case "connected": diff --git a/bin/vpn-lemanapro.sh b/bin/vpn-lemanapro.sh index 503d82b..5262472 100755 --- a/bin/vpn-lemanapro.sh +++ b/bin/vpn-lemanapro.sh @@ -27,6 +27,9 @@ BW_KC_ACCOUNT_MASTER="${LEMANA_VPN_BW_KC_ACCOUNT_MASTER:-bw-master}" STATUS_DIR="${LEMANA_VPN_STATUS_DIR:-$HOME/.local/state/vpn-lemanapro}" STATUS_FILE="$STATUS_DIR/status.json" PATCH_BACKUP_DIR="${LEMANA_VPN_PATCH_BACKUP_DIR:-$CONFIG_DIR/patch-backups}" +CONNECT_LOG_DIR="${LEMANA_VPN_LOG_DIR:-$HOME/Library/Logs}" +CONNECT_LOG_FILE="${LEMANA_VPN_CONNECT_LOG:-$CONNECT_LOG_DIR/LemanaVPN-openconnect-lite.log}" +CONNECT_WAIT_SECONDS="${LEMANA_VPN_CONNECT_WAIT_SECONDS:-20}" DEBUG=false JSON_MODE=false @@ -72,6 +75,45 @@ _clear_status() { _write_status "{\"pid\":$$,\"state\":\"disconnected\",\"updated_at\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" } +_prepare_connection_log() { + mkdir -p "$CONNECT_LOG_DIR" + { + printf '\n==== Lemana VPN openconnect-lite session %s ====\n' "$(date '+%Y-%m-%d %H:%M:%S')" + printf 'Command: %s --browser-display-mode hidden\n' "$OC_BIN" + } >> "$CONNECT_LOG_FILE" + chmod 600 "$CONNECT_LOG_FILE" 2>/dev/null || true +} + +_log_connection_line() { + printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$1" >> "$CONNECT_LOG_FILE" +} + +_show_connection_log_tail() { + [[ -f "$CONNECT_LOG_FILE" ]] || return 0 + printf 'Last openconnect-lite log lines (%s):\n' "$CONNECT_LOG_FILE" >&2 + tail -n 40 "$CONNECT_LOG_FILE" >&2 || true +} + +_progress_pid="" + +_start_connect_progress() { + ( + while sleep "$CONNECT_WAIT_SECONDS"; do + _emit '{"event":"waiting","message":"Still waiting for SSO/openconnect-lite"}' \ + "Still waiting for SSO/openconnect-lite... log: $CONNECT_LOG_FILE" + done + ) & + _progress_pid="$!" +} + +_stop_connect_progress() { + if [[ -n "${_progress_pid:-}" ]]; then + kill "$_progress_pid" >/dev/null 2>&1 || true + wait "$_progress_pid" 2>/dev/null || true + _progress_pid="" + fi +} + _json_get() { local key="$1" python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('$key',''))" 2>/dev/null || true @@ -556,8 +598,16 @@ _dns_cleanup() { _filter_output() { local vpn_ip="" while IFS= read -r line; do + _log_connection_line "$line" + if $DEBUG; then printf '%s\n' "$line" + elif ! $JSON_MODE; then + case "$line" in + *ERROR*|*Error*|*error*|*Failed*|*failed*|*Traceback*|*SAML*|*saml*|*Keycloak*|*keycloak*|*Cisco*|*auth*|*Auth*) + printf '[openconnect-lite] %s\n' "$line" + ;; + esac fi if [[ "$line" =~ Configured\ as\ ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+) ]]; then @@ -618,7 +668,9 @@ _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 +_prepare_connection_log +_emit '{"event":"log","message":"openconnect-lite log is enabled"}' "openconnect-lite log: $CONNECT_LOG_FILE" +trap '_stop_connect_progress; _dns_cleanup; _clear_status' EXIT display_mode="hidden" log_level="" @@ -629,16 +681,19 @@ fi reconnect_count=0 while true; do + _start_connect_progress QTWEBENGINE_CHROMIUM_FLAGS="--disable-gpu" \ "$OC_BIN" --browser-display-mode "$display_mode" $log_level 2>&1 \ | _filter_output exit_code=${PIPESTATUS[0]} + _stop_connect_progress if [[ $exit_code -eq 0 ]]; then _emit '{"event":"disconnected","reason":"user"}' "VPN disconnected" break fi + _show_connection_log_tail reconnect_count=$((reconnect_count + 1)) _emit "{\"event\":\"reconnecting\",\"attempt\":$reconnect_count,\"delay\":5}" "VPN exited with $exit_code. Reconnecting in 5s..." sleep 5 diff --git a/tests/smoke.sh b/tests/smoke.sh index dd06a49..6e3f1f7 100755 --- a/tests/smoke.sh +++ b/tests/smoke.sh @@ -32,6 +32,8 @@ fi status_json="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status --json)" printf '%s\n' "$status_json" | grep -q '"modules":' printf '%s\n' "$status_json" | grep -q '"app":' +grep -q 'LemanaVPN-openconnect-lite.log' "$ROOT/bin/vpn-lemanapro.sh" +grep -q '"event":"waiting"' "$ROOT/bin/vpn-lemanapro.sh" status_text="$(bash "$ROOT/bin/vpn-lemanapro.sh" --status)" printf '%s\n' "$status_text" | grep -q 'Modules:'