diff --git a/lxc-enroll.sh b/lxc-enroll.sh index 986d097..18584a8 100644 --- a/lxc-enroll.sh +++ b/lxc-enroll.sh @@ -283,6 +283,8 @@ DIR=$1; MANAGER=$2; AGENT_VERSION=$3; LOGIN=$4; CT_HOSTNAME=$5; WANT_TS=$6; WANT SSH_ENABLED=$8; SSH_USER=$9; SSH_SUDO=${10}; SSH_NOPASSWD=${11} TS_ACCEPT_DNS=${12} RESUME_TS=${13} +STEP='Vorbereitung' +trap 'rc=$?; printf "FEHLER: %s (Status %s, Worker-Zeile %s).\n" "$STEP" "$rc" "$LINENO" >&2; exit "$rc"' ERR case "$TS_ACCEPT_DNS" in 1) DNS_FLAG=true;; 0) DNS_FLAG=false;; *) exit 1;; esac trap 'rm -f -- "$DIR/headscale.key" "$DIR/enrollment.pass" "$DIR/sudo.pass"' EXIT export DEBIAN_FRONTEND=noninteractive @@ -309,11 +311,13 @@ if [[ $WANT_TS == 0 && $RESUME_TS == 1 ]]; then fi [[ $JOIN_TS == 1 || $WANT_WZ == 1 || $SSH_ENABLED == 1 ]] || { echo 'Komponenten inzwischen vorhanden; nichts geaendert.'; exit 0; } +STEP='APT-Grundpakete' apt-get -o DPkg::Lock::Timeout=120 update apt-get -o DPkg::Lock::Timeout=120 install -y --no-install-recommends ca-certificates curl gnupg install -d -m 755 /usr/share/keyrings /etc/apt/sources.list.d if [[ $WANT_TS == 1 ]]; then + STEP='Tailscale-Installation' # Isolate distribution variables: os-release also defines NAME and VERSION. os_details=$( . /etc/os-release; printf '%s|%s' "$ID" "$VERSION_CODENAME") IFS='|' read -r DISTRO_ID DISTRO_CODENAME <<<"$os_details" @@ -329,6 +333,7 @@ if [[ $WANT_TS == 1 ]]; then systemctl enable --now tailscaled fi if [[ $JOIN_TS == 1 ]]; then + STEP='Headscale-Anmeldung' [[ -c /dev/net/tun && -s $DIR/headscale.key ]] || { echo 'TUN oder Headscale-Schluessel fehlt.' >&2; exit 1; } # File reference keeps the key out of command arguments/history. timeout 90 tailscale up --login-server="$LOGIN" --auth-key="file:$DIR/headscale.key" --hostname="$CT_HOSTNAME" --accept-dns="$DNS_FLAG" --timeout=60s @@ -339,9 +344,11 @@ fi # Explicitly accepted DNS settings may also enable MagicDNS on an existing client. # Do not disable or reauthenticate an existing client when this option is declined. +STEP='Headscale-DNS' if [[ $TS_ACCEPT_DNS == 1 && $WANT_TS == 0 ]]; then tailscale set --accept-dns=true; fi if [[ $WANT_WZ == 1 ]]; then + STEP='Wazuh-Installation und Verbindung' # A Headscale manager IP only becomes reachable after the new client joins. for port in 1514 1515; do timeout 5 bash -c 'exec 7<>/dev/tcp/$1/$2' -- "$MANAGER" "$port" || { @@ -383,6 +390,7 @@ if [[ $WANT_WZ == 1 ]]; then fi if [[ $SSH_ENABLED == 1 ]]; then + STEP='Tailscale-SSH und Nutzer' if [[ $SSH_SUDO == 1 ]]; then apt-get -o DPkg::Lock::Timeout=120 install -y --no-install-recommends sudo; fi new_user=0 if id "$SSH_USER" >/dev/null 2>&1; then @@ -415,29 +423,40 @@ fi WORKER } +deploy_step() { + local id=$1 step=$2 rc + shift 2 + if "$@"; then return 0 + else + rc=$? + say "LXC $id: $step fehlgeschlagen (Status $rc)." >&2 + return 1 + fi +} + deploy_one() { local id=$1 dir ts wz - [[ $(pct status "$id") == 'status: running' ]] || return 1 + [[ $(pct status "$id") == 'status: running' ]] || { say "LXC $id: Container nicht mehr laufend." >&2; return 1; } if pct config "$id" | grep -q '^lock:'; then say "LXC $id inzwischen gesperrt."; return 1; fi ts=$((1-${TS[$id]})); wz=$((1-${WZ[$id]})) - if [[ ( $ts == 1 || $RESUME_TS == 1 ) && ${TUN[$id]} == 0 ]]; then ensure_tun "$id" || return 1; fi - dir=$(pct exec "$id" -- mktemp -d /run/lxc-enroll.XXXXXXXX) || return 1 - [[ $dir =~ ^/run/lxc-enroll\.[A-Za-z0-9]+$ ]] || return 1 + if [[ ( $ts == 1 || $RESUME_TS == 1 ) && ${TUN[$id]} == 0 ]]; then deploy_step "$id" 'TUN-Einrichtung/Neustart' ensure_tun "$id" || return 1; fi + dir=$(deploy_step "$id" 'Temporäres Verzeichnis' pct exec "$id" -- mktemp -d /run/lxc-enroll.XXXXXXXX) || return 1 + [[ $dir =~ ^/run/lxc-enroll\.[A-Za-z0-9]+$ ]] || { say "LXC $id: Unerwarteter temporaerer Pfad." >&2; return 1; } REMOTE_DIRS[$id]=$dir - pct push "$id" "$TMP/worker.sh" "$dir/worker.sh" --user 0 --group 0 --perms 600 || return 1 - if [[ $ts == 1 || $RESUME_TS == 1 ]]; then pct push "$id" "$TMP/headscale.key" "$dir/headscale.key" --user 0 --group 0 --perms 600 || return 1; fi - if [[ $wz == 1 && -s $TMP/enrollment.pass ]]; then pct push "$id" "$TMP/enrollment.pass" "$dir/enrollment.pass" --user 0 --group 0 --perms 600 || return 1; fi + deploy_step "$id" 'Worker-Datei kopieren' pct push "$id" "$TMP/worker.sh" "$dir/worker.sh" --user 0 --group 0 --perms 600 || return 1 + if [[ $ts == 1 || $RESUME_TS == 1 ]]; then deploy_step "$id" 'Headscale-Schluessel kopieren' pct push "$id" "$TMP/headscale.key" "$dir/headscale.key" --user 0 --group 0 --perms 600 || return 1; fi + if [[ $wz == 1 && -s $TMP/enrollment.pass ]]; then deploy_step "$id" 'Enrollment-Passwort kopieren' pct push "$id" "$TMP/enrollment.pass" "$dir/enrollment.pass" --user 0 --group 0 --perms 600 || return 1; fi if [[ $SSH_ENABLED == 1 ]]; then - if [[ -s $TMP/sudo.pass ]]; then pct push "$id" "$TMP/sudo.pass" "$dir/sudo.pass" --user 0 --group 0 --perms 600 || return 1; fi + if [[ -s $TMP/sudo.pass ]]; then deploy_step "$id" 'Sudo-Passwort kopieren' pct push "$id" "$TMP/sudo.pass" "$dir/sudo.pass" --user 0 --group 0 --perms 600 || return 1; fi fi # Credentials are files, never pct arguments. bash receives only non-secret settings. - pct exec "$id" -- bash "$dir/worker.sh" "$dir" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "${NAMES[$id]}" "$ts" "$wz" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" "$TS_ACCEPT_DNS" "$RESUME_TS" || return 1 + deploy_step "$id" 'Einrichtung im Container' pct exec "$id" -- bash "$dir/worker.sh" "$dir" "$MANAGER" "$VERSION" "$LOGIN_SERVER" "${NAMES[$id]}" "$ts" "$wz" "$SSH_ENABLED" "$SSH_USER" "$SSH_SUDO" "$SSH_NOPASSWD" "$TS_ACCEPT_DNS" "$RESUME_TS" || return 1 if [[ $ts == 1 || $RESUME_TS == 1 ]]; then pct exec "$id" -- tailscale status --json | python3 -c 'import json,sys; d=json.load(sys.stdin); sys.exit(0 if d.get("BackendState")=="Running" and d.get("Self",{}).get("Online") is True else 1)' || { say "LXC $id: Headscale-Verbindung nicht als online bestaetigt."; return 1; } fi - pct exec "$id" -- rm -rf -- "$dir" || return 1 + deploy_step "$id" 'Temporaere Dateien entfernen' pct exec "$id" -- rm -rf -- "$dir" || return 1 REMOTE_DIRS[$id]='' }