Добавь интерактивный выбор модулей VPN

This commit is contained in:
2026-05-19 12:32:25 +03:00
parent e91c5f7861
commit d999be49ee
3 changed files with 238 additions and 7 deletions

View File

@@ -19,6 +19,8 @@ CLI-установка корпоративного VPN `vpn.lemanapro.ru` дл
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
```
Если установка запущена из терминала, скрипт сначала проверит, что уже стоит, и спросит по отсутствующим опциональным модулям.
После установки открой новый shell или выполни:
```sh
@@ -52,6 +54,18 @@ curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.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 -- --interactive
```
Запустить без вопросов, с выбранными флагами и дефолтами:
```sh
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh -s -- --non-interactive
```
Если raw URL отличается, переопредели базовый адрес:
```sh
@@ -103,6 +117,36 @@ JSON-режим тоже отдаёт модульный статус:
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
Keychain password: no
Keychain TOTP seed: no
```
Если доступен терминал (`/dev/tty`), скрипт спросит только по тому, чего не хватает:
- поставить ли Bitwarden CLI, если `bw` не найден;
- собрать ли Touch ID helper, если его нет и Bitwarden включён;
- настроить ли sudoers для `openconnect` и DNS cleanup;
- добавить ли алиасы в `~/.zshrc`;
- записать ли LDAP-пароль и TOTP seed в Keychain, если Bitwarden отключён.
Флаги имеют приоритет над вопросами. Например, `--without-bitwarden` не будет спрашивать про Bitwarden, а `--no-shell` не будет предлагать алиасы.
В неинтерактивной среде скрипт не задаёт вопросов и использует выбранные флаги/дефолты. Для CI или повторяемой установки лучше явно указывать `--non-interactive`.
## Модули
### Core

View File

@@ -18,6 +18,12 @@ INSTALL_ALIASES=1
CONFIGURE_KEYCHAIN=0
DRY_RUN=0
FORCE=0
INTERACTIVE=auto
BITWARDEN_FORCED=0
TOUCHID_FORCED=0
SUDOERS_FORCED=0
SHELL_FORCED=0
CONFIGURE_KEYCHAIN_FORCED=0
usage() {
cat <<'USAGE'
@@ -35,6 +41,8 @@ Options:
--raw-base-url URL Raw file base URL for curl installs
--no-sudoers Do not install sudoers rules
--no-shell Do not update ~/.zshrc aliases
--interactive Ask before installing optional missing modules
--non-interactive Use selected/default modules without prompts
--minimal Same as --without-bitwarden --without-touchid
--dry-run Print actions without changing files
--force Reinstall files even when present
@@ -49,11 +57,26 @@ USAGE
while [ "$#" -gt 0 ]; do
case "$1" in
--with-bitwarden) USE_BITWARDEN=1 ;;
--without-bitwarden) USE_BITWARDEN=0 ;;
--with-touchid) USE_TOUCHID=1 ;;
--without-touchid) USE_TOUCHID=0 ;;
--configure-keychain) CONFIGURE_KEYCHAIN=1 ;;
--with-bitwarden)
USE_BITWARDEN=1
BITWARDEN_FORCED=1
;;
--without-bitwarden)
USE_BITWARDEN=0
BITWARDEN_FORCED=1
;;
--with-touchid)
USE_TOUCHID=1
TOUCHID_FORCED=1
;;
--without-touchid)
USE_TOUCHID=0
TOUCHID_FORCED=1
;;
--configure-keychain)
CONFIGURE_KEYCHAIN=1
CONFIGURE_KEYCHAIN_FORCED=1
;;
--username)
shift
[ "$#" -gt 0 ] || { echo "--username requires a value" >&2; exit 1; }
@@ -69,11 +92,21 @@ while [ "$#" -gt 0 ]; do
[ "$#" -gt 0 ] || { echo "--raw-base-url requires a value" >&2; exit 1; }
RAW_BASE_URL="${1%/}"
;;
--no-sudoers) INSTALL_SUDOERS=0 ;;
--no-shell) INSTALL_ALIASES=0 ;;
--no-sudoers)
INSTALL_SUDOERS=0
SUDOERS_FORCED=1
;;
--no-shell)
INSTALL_ALIASES=0
SHELL_FORCED=1
;;
--interactive) INTERACTIVE=1 ;;
--non-interactive) INTERACTIVE=0 ;;
--minimal)
USE_BITWARDEN=0
USE_TOUCHID=0
BITWARDEN_FORCED=1
TOUCHID_FORCED=1
;;
--dry-run) DRY_RUN=1 ;;
--force) FORCE=1 ;;
@@ -115,6 +148,136 @@ need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Command not found: $1"
}
has_tty() {
[ -r /dev/tty ] && [ -w /dev/tty ]
}
interactive_enabled() {
case "$INTERACTIVE" in
1) has_tty ;;
0) return 1 ;;
auto) has_tty ;;
*) return 1 ;;
esac
}
yes_no() {
prompt="$1"
default_answer="$2"
[ "$default_answer" = "y" ] || [ "$default_answer" = "n" ] || die "Invalid yes_no default: $default_answer"
if ! interactive_enabled; then
[ "$default_answer" = "y" ]
return $?
fi
if [ "$default_answer" = "y" ]; then
suffix="[Y/n]"
else
suffix="[y/N]"
fi
while :; do
printf '%s %s ' "$prompt" "$suffix" > /dev/tty
IFS= read -r answer < /dev/tty || answer=""
case "$answer" in
"") [ "$default_answer" = "y" ]; return $? ;;
y|Y|yes|YES|Yes|д|Д|да|Да|ДА) return 0 ;;
n|N|no|NO|No|н|Н|нет|Нет|НЕТ) return 1 ;;
*) printf 'Введите y или n.\n' > /dev/tty ;;
esac
done
}
bool_word() {
if "$@" >/dev/null 2>&1; then
printf 'yes'
else
printf 'no'
fi
}
keychain_has() {
security find-generic-password -s "$1" -a "$2" >/dev/null 2>&1
}
zsh_aliases_installed() {
[ -f "$HOME/.zshrc" ] && grep -q '^# >>> lemana-vpn$' "$HOME/.zshrc"
}
print_detected_state() {
log "Detected state:"
log " openconnect: $(bool_word command -v openconnect)"
log " pipx: $(bool_word command -v pipx)"
log " openconnect-lite: $(bool_word test -x "$HOME/.local/bin/openconnect-lite")"
log " Bitwarden CLI: $(bool_word command -v bw)"
log " Touch ID helper: $(bool_word test -x "$INSTALL_BIN_DIR/keychain-fingerprint")"
log " DNS cleanup: $(bool_word test -x "$DNS_CLEANUP")"
log " sudoers: $(bool_word test -f /etc/sudoers.d/lemana-vpn-openconnect)/$(bool_word test -f /etc/sudoers.d/lemana-vpn-dns)"
log " shell aliases: $(bool_word zsh_aliases_installed)"
log " Keychain password: $(bool_word keychain_has openconnect-lite "$USERNAME")"
log " Keychain TOTP seed: $(bool_word keychain_has openconnect-lite "totp/$USERNAME")"
}
choose_modules() {
print_detected_state
if ! interactive_enabled; then
log "Interactive prompts: off"
return 0
fi
log "Interactive prompts: on"
if [ "$BITWARDEN_FORCED" -eq 0 ] && ! command -v bw >/dev/null 2>&1; then
if yes_no "Bitwarden CLI не найден. Поставить модуль Bitwarden?" y; then
USE_BITWARDEN=1
else
USE_BITWARDEN=0
fi
fi
if [ "$TOUCHID_FORCED" -eq 0 ]; then
if [ "$USE_BITWARDEN" -eq 1 ]; then
if ! [ -x "$INSTALL_BIN_DIR/keychain-fingerprint" ]; then
if yes_no "Touch ID helper не найден. Собрать и установить?" y; then
USE_TOUCHID=1
else
USE_TOUCHID=0
fi
fi
else
USE_TOUCHID=0
fi
fi
if [ "$SUDOERS_FORCED" -eq 0 ]; then
if ! [ -f /etc/sudoers.d/lemana-vpn-openconnect ] || ! [ -f /etc/sudoers.d/lemana-vpn-dns ]; then
if yes_no "Настроить sudoers для VPN/DNS без повторного sudo-пароля?" y; then
INSTALL_SUDOERS=1
else
INSTALL_SUDOERS=0
fi
fi
fi
if [ "$SHELL_FORCED" -eq 0 ] && ! zsh_aliases_installed; then
if yes_no "Добавить алиасы vpn/vpn-debug/vpn-fix-dns в ~/.zshrc?" y; then
INSTALL_ALIASES=1
else
INSTALL_ALIASES=0
fi
fi
if [ "$CONFIGURE_KEYCHAIN_FORCED" -eq 0 ] && [ "$USE_BITWARDEN" -eq 0 ]; then
if ! keychain_has openconnect-lite "$USERNAME" || ! keychain_has openconnect-lite "totp/$USERNAME"; then
if yes_no "Bitwarden отключён, а Keychain credentials неполные. Записать LDAP-пароль и TOTP seed после установки?" y; then
CONFIGURE_KEYCHAIN=1
fi
fi
fi
}
script_dir() {
case "$0" in
*/*) cd "$(dirname "$0")" 2>/dev/null && pwd ;;
@@ -325,6 +488,8 @@ main() {
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT INT TERM
choose_modules
log "Installing Lemana VPN"
log "Modules: bitwarden=$USE_BITWARDEN touchid=$USE_TOUCHID sudoers=$INSTALL_SUDOERS shell=$INSTALL_ALIASES"

22
tests/smoke.sh Executable file
View File

@@ -0,0 +1,22 @@
#!/bin/sh
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "$TMP_DIR"' EXIT INT TERM
export HOME="$TMP_DIR/home"
export LEMANA_VPN_BIN_DIR="$HOME/bin"
export LEMANA_VPN_CONFIG_DIR="$HOME/.config/lemana-vpn"
export OPENCONNECT_LITE_CONFIG_DIR="$HOME/.config/openconnect-lite"
mkdir -p "$HOME"
output="$(cd "$ROOT" && sh install.sh --dry-run --non-interactive --minimal)"
printf '%s\n' "$output" | grep -q 'Detected state:'
printf '%s\n' "$output" | grep -q 'Interactive prompts: off'
printf '%s\n' "$output" | grep -q 'Modules: bitwarden=0 touchid=0 sudoers=1 shell=1'
printf '%s\n' "$output" | grep -q 'sudo install -d -m 755 -o root -g wheel /usr/local/sbin'
printf 'smoke ok\n'