Add terminal dialogs for all enrollment choices and inputs

This commit is contained in:
2026-10-04 12:20:27 +02:00
parent 31b9714a4e
commit 5071a60aa4
+106 -7
View File
@@ -6,14 +6,52 @@ umask 077
fail() { printf 'FEHLER: %s\n' "$*" >&2; exit 1; }
say() { printf '%s\n' "$*"; }
ask() { printf '%s ' "$1" >&2; IFS= read -r REPLY <&3 || fail 'Eingabe abgebrochen.'; }
UI_TOOL=''; UI_HEIGHT=20; UI_WIDTH=76
init_ui() {
local dimensions rows=24 columns=80
UI_TOOL=''
if command -v whiptail >/dev/null 2>&1; then UI_TOOL=whiptail
elif command -v dialog >/dev/null 2>&1; then UI_TOOL=dialog; fi
dimensions=$(stty size <&3 2>/dev/null) || dimensions=''
if [[ $dimensions =~ ^([0-9]+)[[:space:]]+([0-9]+)$ ]]; then
rows=${BASH_REMATCH[1]}; columns=${BASH_REMATCH[2]}
fi
if [[ -z $UI_TOOL || ${TERM:-dumb} == dumb || $rows -lt 16 || $columns -lt 60 ]]; then
UI_TOOL=''
say 'Dialogoberflaeche nicht verfuegbar (whiptail/dialog oder ausreichend grosses Terminal fehlt); Texteingabe verwenden.'
return 0
fi
UI_HEIGHT=$((rows-4)); UI_WIDTH=$((columns-4))
if [[ $UI_HEIGHT -gt 30 ]]; then UI_HEIGHT=30; fi
if [[ $UI_WIDTH -gt 110 ]]; then UI_WIDTH=110; fi
}
ui_capture() {
# The UI uses the controlling terminal, even if this script's stdin is a pipe.
# Secrets/selections are captured on a separate descriptor, never stdout/logs.
REPLY=$("$UI_TOOL" --output-fd 4 "$@" 4>&1 1>&3 2>&3 <&3)
}
ask() {
if [[ -n $UI_TOOL ]]; then
ui_capture --title 'LXC Enrollment - Eingabe' --ok-button 'Weiter' --cancel-button 'Abbrechen' \
--inputbox "$1" "$UI_HEIGHT" "$UI_WIDTH" "${2:-}" || fail 'Eingabe abgebrochen oder Dialog fehlgeschlagen.'
else
printf '%s ' "$1" >&2
IFS= read -r REPLY <&3 || fail 'Eingabe abgebrochen.'
fi
}
secret() {
if [[ -n $UI_TOOL ]]; then
ui_capture --title 'LXC Enrollment - Verdeckte Eingabe' --ok-button 'Weiter' --cancel-button 'Abbrechen' \
--passwordbox "$1" "$UI_HEIGHT" "$UI_WIDTH" || fail 'Eingabe abgebrochen oder Dialog fehlgeschlagen.'
return 0
fi
printf '%s ' "$1" >&2
IFS= read -r -s REPLY <&3 || fail 'Eingabe abgebrochen.'
printf '\n' >&2
}
installed_label() { if [[ $1 == 1 ]]; then printf 'vorhanden'; else printf 'fehlt'; fi; }
choice_hint() { if [[ $1 == 1 ]]; then printf '[J/n]'; else printf '[j/N]'; fi; }
yesno_label() { if [[ $1 == 1 ]]; then printf 'Ja'; else printf 'Nein'; fi; }
LIST=0; DRY=0; MANAGER=''; VERSION=''; LOGIN_SERVER=''; SELECTION=''; VERSION_EXPLICIT=0
SSH_ENABLED=1; SSH_USER=''; SSH_SUDO=0; SSH_NOPASSWD=0; SAVE_SETTINGS=1
@@ -45,6 +83,8 @@ Nur laufende Debian-/Ubuntu-LXCs mit systemd. Vorhandene Tailscale-/Wazuh-
Registrierungen bleiben unveraendert. Optional Tailscale SSH/Nutzer/Sudo fuer die Auswahl.
Lokale Einstellungen: /etc/lxc-enroll/.vars (JSON, ohne Geheimnisse).
Keine Host-Agent-Installation. Geheimnisse werden lokal abgefragt.
Dialogoberflaeche fuer Auswahl, Eingaben und Geheimnisse (whiptail/dialog).
LXC-Checkliste: Leertaste markiert. Ohne Dialogwerkzeug: Texteingabe.
HELP
exit 0 ;;
*) fail "Unbekannte Option: $1" ;;
@@ -70,7 +110,20 @@ trap 'exit 130' INT
trap 'exit 143' TERM
yes_no() {
local prompt=$1 default=$2
local prompt=$1 default=$2 rc
local -a options=()
if [[ -n ${UI_TOOL:-} ]]; then
[[ $default != 0 ]] || options+=(--defaultno)
# Keep keyboard hints only in text mode; buttons show Ja/Nein in the UI.
prompt=${prompt% \[J/n\]}; prompt=${prompt% \[j/N\]}
if ui_capture --title 'LXC Enrollment - Auswahl' --yes-button 'Ja' --no-button 'Nein' \
"${options[@]}" --yesno "$prompt" "$UI_HEIGHT" "$UI_WIDTH"; then REPLY=1
else
rc=$?
case "$rc" in 1) REPLY=0;; *) fail 'Auswahl abgebrochen oder Dialog fehlgeschlagen.';; esac
fi
return 0
fi
ask "$prompt"
case "$REPLY" in '' ) REPLY=$default;; 1|j|J|ja|Ja|y|Y|yes|Yes) REPLY=1;; 0|n|N|nein|Nein|no|No) REPLY=0;; *) fail 'Bitte j oder n eingeben (auch 1/0 moeglich).';; esac
}
@@ -166,6 +219,36 @@ PROBE
done
}
choose_lxcs() {
local menu_height id rc
local -a items=()
if [[ -z $UI_TOOL ]]; then
ask 'CTIDs auswaehlen (Leerzeichen, oder all; leer = abbrechen):'
SELECTION=$REPLY
[[ -n $SELECTION ]]
return
fi
menu_height=$((UI_HEIGHT-9))
if [[ $menu_height -gt ${#IDS[@]} ]]; then menu_height=${#IDS[@]}; fi
for id in "${IDS[@]}"; do
items+=("$id" "${NAMES[$id]} | Tailscale: $(installed_label "${TS[$id]}") | Wazuh: $(installed_label "${WZ[$id]}") | TUN: $(installed_label "${TUN[$id]}")" OFF)
done
# UI reads/writes the controlling terminal. Only CTIDs go to captured fd 4.
# Never eval/source the dialog output; the normal selection validation follows.
if ui_capture --title 'LXC Enrollment - Container auswaehlen' \
--ok-button 'Weiter' --cancel-button 'Abbrechen' \
--separate-output --checklist \
'Pfeile: navigieren | Leertaste: markieren | Tab: Schaltflaeche | Enter: bestaetigen' \
"$UI_HEIGHT" "$UI_WIDTH" "$menu_height" "${items[@]}"; then
SELECTION=${REPLY//$'\n'/ }
[[ -n $SELECTION ]]
return
else
rc=$?
case "$rc" in 1|255) return 1;; *) fail "LXC-Checkliste fehlgeschlagen (Status $rc).";; esac
fi
}
ensure_tun() {
local id=$1 cfg slot=-1 n
cfg=$(pct config "$id") || return 1
@@ -359,16 +442,17 @@ deploy_one() {
}
main() {
local id input wanted found need_ts=0 need_wz=0 restart_ids='' ok='' failed='' auto_version password
local id input wanted found need_ts=0 need_wz=0 restart_ids='' ok='' failed='' auto_version password plan=''
scan
[[ $LIST == 0 ]] || return 0
[[ ${#IDS[@]} -gt 0 ]] || { say 'Keine passenden LXCs gefunden.'; return 0; }
exec 3<>/dev/tty || fail 'Interaktives Terminal fehlt. --list kann ohne Terminal laufen.'
init_ui
load_settings
yes_no "Tailscale SSH fuer alle ausgewaehlten LXCs aktivieren? $(choice_hint "$SSH_ENABLED")" "$SSH_ENABLED"
SSH_ENABLED=$REPLY
if [[ $SSH_ENABLED == 1 ]]; then
ask "SSH-Nutzername${SSH_USER:+ [$SSH_USER]}:"
ask "SSH-Nutzername${SSH_USER:+ [$SSH_USER]}:" "$SSH_USER"
SSH_USER=${REPLY:-$SSH_USER}
[[ $SSH_USER =~ ^[a-z_][a-z0-9_-]{0,30}$ && $SSH_USER != root ]] || fail 'Bitte einen normalen Linux-Nutzernamen verwenden.'
yes_no "Nutzer zu sudo hinzufuegen? $(choice_hint "$SSH_SUDO")" "$SSH_SUDO"; SSH_SUDO=$REPLY
@@ -377,7 +461,9 @@ main() {
fi
fi
yes_no 'Einstellungen ohne Geheimnisse lokal in .vars speichern? [J/n]' 1; SAVE_SETTINGS=$REPLY
if [[ -z $SELECTION ]]; then ask 'CTIDs auswaehlen (Leerzeichen, oder all; leer = abbrechen):'; SELECTION=$REPLY; fi
if [[ -z $SELECTION ]]; then
choose_lxcs || { say 'Abgebrochen; keine Container ausgewaehlt.'; return 0; }
fi
[[ -n $SELECTION ]] || return 0
if [[ $SELECTION == all ]]; then SELECTED=("${IDS[@]}"); else
SELECTION=${SELECTION//,/ }
@@ -393,6 +479,7 @@ main() {
say ''; say 'Geplante Aenderungen:'
for id in "${SELECTED[@]}"; do
say "LXC $id ${NAMES[$id]}: Tailscale $(installed_label "${TS[$id]}"), Wazuh $(installed_label "${WZ[$id]}")."
plan+="LXC $id ${NAMES[$id]}: Tailscale $(installed_label "${TS[$id]}"), Wazuh $(installed_label "${WZ[$id]}")"$'\n'
if [[ ${TS[$id]} == 0 || $RESUME_TS == 1 ]]; then
need_ts=1
if [[ ${TUN[$id]} == 0 ]]; then restart_ids+=" $id"; fi
@@ -411,7 +498,7 @@ main() {
exec 8>/run/lxc-enroll.lock
flock -n 8 || fail 'Ein anderer Enrollment-Lauf ist bereits aktiv.'
if [[ $need_wz == 1 ]]; then
if [[ -z $MANAGER ]]; then ask 'Erreichbare Wazuh-Manager-Adresse (LAN-IP oder Headscale-IP):'; MANAGER=$REPLY; fi
if [[ -z $MANAGER ]]; then ask 'Erreichbare Wazuh-Manager-Adresse (IP oder DNS-Name):'; MANAGER=$REPLY; fi
[[ $MANAGER =~ ^[A-Za-z0-9][A-Za-z0-9.:-]*$ ]] || fail 'Manager-Adresse ungueltig.'
if [[ ${#MANAGERS[@]} == 1 ]]; then
auto_version=$(pct exec "${MANAGERS[0]}" -- dpkg-query -W '-f=${Version}' wazuh-manager) || fail 'Manager-Version nicht lesbar.'
@@ -435,7 +522,19 @@ main() {
else
say 'Neue Clients behalten lokales DNS bei. Der Manager-Name muss ueber diesen Resolver aufloesbar sein.'
fi
ask 'Nur diese fehlenden Komponenten installieren? Bitte INSTALL eingeben:'
if [[ -n $UI_TOOL ]]; then
local -a scroll_option=()
if [[ $UI_TOOL == whiptail ]]; then scroll_option=(--scrolltext); else scroll_option=(--scrollbar); fi
plan+=$'\n'"Wazuh-Manager: ${MANAGER:-nicht benoetigt}; neue Agent-Version: ${VERSION:-keine}"$'\n'
plan+="Headscale: ${LOGIN_SERVER:-nicht benoetigt}; DNS uebernehmen: $(yesno_label "$TS_ACCEPT_DNS")"$'\n'
plan+="Tailscale SSH: $(yesno_label "$SSH_ENABLED"); Nutzer: ${SSH_USER:-keiner}; Sudo: $(yesno_label "$SSH_SUDO"); Sudo ohne Passwort: $(yesno_label "$SSH_NOPASSWD")"$'\n'
if [[ $SSH_ENABLED == 1 ]]; then plan+='Tailscale-SSH-Sitzungen koennen unterbrochen werden. Zugriff braucht eine passende Headscale-Policy.'$'\n'; fi
plan+="TUN ergaenzen und neu starten:${restart_ids:- keine}"$'\n'
if [[ $RESUME_TS == 1 ]]; then plan+='Wiederaufnahme: noch nicht angemeldetes Tailscale anmelden.'$'\n'; fi
ui_capture --title 'LXC Enrollment - Geplante Aenderungen' --ok-button 'Pruefen und weiter' \
"${scroll_option[@]}" --msgbox "$plan" "$UI_HEIGHT" "$UI_WIDTH" || fail 'Pruefung abgebrochen.'
fi
ask 'Nur diese fehlenden Komponenten und gewaehlten Einstellungen einrichten? Bitte INSTALL eingeben:'
[[ $REPLY == INSTALL ]] || { say 'Abgebrochen.'; return 0; }
if [[ -n $restart_ids ]]; then
ask "Diese Container werden kurz unterbrochen:$restart_ids. Bitte NEUSTART eingeben:"