Добавь интерактивный выбор модулей 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
|
curl -fsSL https://git.dokops.ru/dokril/lemana-vpn/raw/branch/main/install.sh | sh
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Если установка запущена из терминала, скрипт сначала проверит, что уже стоит, и спросит по отсутствующим опциональным модулям.
|
||||||
|
|
||||||
После установки открой новый shell или выполни:
|
После установки открой новый shell или выполни:
|
||||||
|
|
||||||
```sh
|
```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
|
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 отличается, переопредели базовый адрес:
|
Если raw URL отличается, переопредели базовый адрес:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
@@ -103,6 +117,36 @@ JSON-режим тоже отдаёт модульный статус:
|
|||||||
vpn --status --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
|
### Core
|
||||||
|
|||||||
179
install.sh
179
install.sh
@@ -18,6 +18,12 @@ INSTALL_ALIASES=1
|
|||||||
CONFIGURE_KEYCHAIN=0
|
CONFIGURE_KEYCHAIN=0
|
||||||
DRY_RUN=0
|
DRY_RUN=0
|
||||||
FORCE=0
|
FORCE=0
|
||||||
|
INTERACTIVE=auto
|
||||||
|
BITWARDEN_FORCED=0
|
||||||
|
TOUCHID_FORCED=0
|
||||||
|
SUDOERS_FORCED=0
|
||||||
|
SHELL_FORCED=0
|
||||||
|
CONFIGURE_KEYCHAIN_FORCED=0
|
||||||
|
|
||||||
usage() {
|
usage() {
|
||||||
cat <<'USAGE'
|
cat <<'USAGE'
|
||||||
@@ -35,6 +41,8 @@ Options:
|
|||||||
--raw-base-url URL Raw file base URL for curl installs
|
--raw-base-url URL Raw file base URL for curl installs
|
||||||
--no-sudoers Do not install sudoers rules
|
--no-sudoers Do not install sudoers rules
|
||||||
--no-shell Do not update ~/.zshrc aliases
|
--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
|
--minimal Same as --without-bitwarden --without-touchid
|
||||||
--dry-run Print actions without changing files
|
--dry-run Print actions without changing files
|
||||||
--force Reinstall files even when present
|
--force Reinstall files even when present
|
||||||
@@ -49,11 +57,26 @@ USAGE
|
|||||||
|
|
||||||
while [ "$#" -gt 0 ]; do
|
while [ "$#" -gt 0 ]; do
|
||||||
case "$1" in
|
case "$1" in
|
||||||
--with-bitwarden) USE_BITWARDEN=1 ;;
|
--with-bitwarden)
|
||||||
--without-bitwarden) USE_BITWARDEN=0 ;;
|
USE_BITWARDEN=1
|
||||||
--with-touchid) USE_TOUCHID=1 ;;
|
BITWARDEN_FORCED=1
|
||||||
--without-touchid) USE_TOUCHID=0 ;;
|
;;
|
||||||
--configure-keychain) CONFIGURE_KEYCHAIN=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)
|
--username)
|
||||||
shift
|
shift
|
||||||
[ "$#" -gt 0 ] || { echo "--username requires a value" >&2; exit 1; }
|
[ "$#" -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; }
|
[ "$#" -gt 0 ] || { echo "--raw-base-url requires a value" >&2; exit 1; }
|
||||||
RAW_BASE_URL="${1%/}"
|
RAW_BASE_URL="${1%/}"
|
||||||
;;
|
;;
|
||||||
--no-sudoers) INSTALL_SUDOERS=0 ;;
|
--no-sudoers)
|
||||||
--no-shell) INSTALL_ALIASES=0 ;;
|
INSTALL_SUDOERS=0
|
||||||
|
SUDOERS_FORCED=1
|
||||||
|
;;
|
||||||
|
--no-shell)
|
||||||
|
INSTALL_ALIASES=0
|
||||||
|
SHELL_FORCED=1
|
||||||
|
;;
|
||||||
|
--interactive) INTERACTIVE=1 ;;
|
||||||
|
--non-interactive) INTERACTIVE=0 ;;
|
||||||
--minimal)
|
--minimal)
|
||||||
USE_BITWARDEN=0
|
USE_BITWARDEN=0
|
||||||
USE_TOUCHID=0
|
USE_TOUCHID=0
|
||||||
|
BITWARDEN_FORCED=1
|
||||||
|
TOUCHID_FORCED=1
|
||||||
;;
|
;;
|
||||||
--dry-run) DRY_RUN=1 ;;
|
--dry-run) DRY_RUN=1 ;;
|
||||||
--force) FORCE=1 ;;
|
--force) FORCE=1 ;;
|
||||||
@@ -115,6 +148,136 @@ need_cmd() {
|
|||||||
command -v "$1" >/dev/null 2>&1 || die "Command not found: $1"
|
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() {
|
script_dir() {
|
||||||
case "$0" in
|
case "$0" in
|
||||||
*/*) cd "$(dirname "$0")" 2>/dev/null && pwd ;;
|
*/*) cd "$(dirname "$0")" 2>/dev/null && pwd ;;
|
||||||
@@ -325,6 +488,8 @@ main() {
|
|||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
trap 'rm -rf "$tmp"' EXIT INT TERM
|
trap 'rm -rf "$tmp"' EXIT INT TERM
|
||||||
|
|
||||||
|
choose_modules
|
||||||
|
|
||||||
log "Installing Lemana VPN"
|
log "Installing Lemana VPN"
|
||||||
log "Modules: bitwarden=$USE_BITWARDEN touchid=$USE_TOUCHID sudoers=$INSTALL_SUDOERS shell=$INSTALL_ALIASES"
|
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