Добавь интерактивный выбор модулей VPN
This commit is contained in:
44
README.md
44
README.md
@@ -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
|
||||
|
||||
179
install.sh
179
install.sh
@@ -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
22
tests/smoke.sh
Executable 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'
|
||||
|
||||
Reference in New Issue
Block a user