From a55a03de4a84199a462d1557cb8c60f2a6025343 Mon Sep 17 00:00:00 2001 From: Semaphore Date: Fri, 13 Mar 2026 10:11:23 -0700 Subject: [PATCH] =?UTF-8?q?scripts:=20deploy=5Fagent.sh=20=E2=80=94=20visu?= =?UTF-8?q?do=20optional,=20per-host=20credentials=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/deploy_agent.sh | 99 ++++++++++++++++++++++++++++------------- 1 file changed, 68 insertions(+), 31 deletions(-) diff --git a/scripts/deploy_agent.sh b/scripts/deploy_agent.sh index f890d92..67b9f51 100755 --- a/scripts/deploy_agent.sh +++ b/scripts/deploy_agent.sh @@ -192,7 +192,13 @@ def extract_hosts(group, target_list): hvars = hvars or {} merged = {**group_vars, **hvars} ip = merged.get('ansible_host', hostname) - target_list.append({'name': hostname, 'ip': ip}) + target_list.append({ + 'name': hostname, + 'ip': ip, + 'native_user': merged.get('deploy_native_user', ''), + 'native_pass': merged.get('deploy_native_pass', ''), + 'root_pass': merged.get('deploy_root_pass', ''), + }) children = (inv.get('all') or {}).get('children') or {} extract_hosts(children.get('linux_hosts'), linux_hosts) @@ -202,7 +208,7 @@ print(json.dumps({'linux': linux_hosts, 'windows': windows_hosts})) PYEOF ) -LINUX_HOSTS=$(echo "$HOST_DATA" | python3 -c "import json,sys; d=json.load(sys.stdin); [print(h['name']+'|'+h['ip']) for h in d['linux']]") +LINUX_HOSTS=$(echo "$HOST_DATA" | python3 -c "import json,sys; d=json.load(sys.stdin); [print('|'.join([h['name'],h['ip'],h['native_user'],h['native_pass'],h['root_pass']])) for h in d['linux']]") WINDOWS_HOSTS=$(echo "$HOST_DATA" | python3 -c "import json,sys; d=json.load(sys.stdin); [print(h['name']+'|'+h['ip']) for h in d['windows']]") LINUX_COUNT=$(echo "$LINUX_HOSTS" | grep -c '.' || true) @@ -269,11 +275,15 @@ cat > "\$SUDOERS_FILE" << SUDOEOF %sudo-nopasswd ALL=(ALL) NOPASSWD:ALL SUDOEOF chmod 440 "\$SUDOERS_FILE" -visudo -cf "\$SUDOERS_FILE" && echo "[remote] Sudoers file validated OK" || { - echo "[remote] ERROR: sudoers file invalid — removing" - rm -f "\$SUDOERS_FILE" - exit 1 -} +if command -v visudo &>/dev/null; then + visudo -cf "\$SUDOERS_FILE" && echo "[remote] Sudoers file validated OK" || { + echo "[remote] ERROR: sudoers file invalid — removing" + rm -f "\$SUDOERS_FILE" + exit 1 + } +else + echo "[remote] visudo not found — skipping validation (file written with chmod 440)" +fi # ── Deploy SSH keys to agent user ── AGENT_SSH_DIR="/home/\$AGENT_USER/.ssh" @@ -372,30 +382,41 @@ LINUX_HOSTS_FILE=$(mktemp) echo "$LINUX_HOSTS" > "$LINUX_HOSTS_FILE" trap 'rm -f "$LINUX_HOSTS_FILE"' EXIT -while IFS='|' read -r HOSTNAME HOST_IP; do +while IFS='|' read -r HOSTNAME HOST_IP HOST_NATIVE_USER HOST_NATIVE_PASS HOST_ROOT_PASS; do [[ -z "$HOSTNAME" ]] && continue ((HOSTS_TOTAL++)) || true + # Per-host credentials override globals + EFFECTIVE_NATIVE_USER="${HOST_NATIVE_USER:-$NATIVE_USER}" + EFFECTIVE_NATIVE_USER="${EFFECTIVE_NATIVE_USER:-$NATIVE_USER}" + [[ -z "$EFFECTIVE_NATIVE_USER" ]] && EFFECTIVE_NATIVE_USER="$NATIVE_USER" + + EFFECTIVE_NATIVE_PASS="${HOST_NATIVE_PASS:-}" + [[ -z "$EFFECTIVE_NATIVE_PASS" ]] && EFFECTIVE_NATIVE_PASS="$NATIVE_PASS" + + EFFECTIVE_ROOT_PASS="${HOST_ROOT_PASS:-}" + [[ -z "$EFFECTIVE_ROOT_PASS" ]] && EFFECTIVE_ROOT_PASS="$ROOT_PASS" + echo "" log_section "Host: $HOSTNAME ($HOST_IP)" if [[ "$DRY_RUN" == "true" ]]; then - log_info "DRY RUN: Would bootstrap $HOSTNAME ($HOST_IP) as $NATIVE_USER → root → create $AGENT_USER" + log_info "DRY RUN: Would bootstrap $HOSTNAME ($HOST_IP) as $EFFECTIVE_NATIVE_USER → root → create $AGENT_USER" ((HOSTS_OK++)) || true continue fi - # Add host to known_hosts — redirect stdin from /dev/null to prevent draining loop fd + # Add host to known_hosts log_info "Scanning host key..." ssh-keyscan -T 10 "$HOST_IP" >> /root/.ssh/known_hosts 2>/dev/null < /dev/null || true # Test native user SSH access - log_info "Testing SSH as $NATIVE_USER..." - if ! sshpass -p "$NATIVE_PASS" ssh -o StrictHostKeyChecking=no \ + log_info "Testing SSH as $EFFECTIVE_NATIVE_USER..." + if ! sshpass -p "$EFFECTIVE_NATIVE_PASS" ssh -o StrictHostKeyChecking=no \ -o ConnectTimeout=10 \ -o PasswordAuthentication=yes \ - "$NATIVE_USER@$HOST_IP" "echo connected" < /dev/null &>/dev/null; then - log_error "Cannot SSH to $HOSTNAME ($HOST_IP) as $NATIVE_USER — skipping" + "$EFFECTIVE_NATIVE_USER@$HOST_IP" "echo connected" < /dev/null &>/dev/null; then + log_error "Cannot SSH to $HOSTNAME ($HOST_IP) as $EFFECTIVE_NATIVE_USER — skipping" FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — SSH connection failed") ((HOSTS_FAILED++)) || true continue @@ -409,25 +430,40 @@ while IFS='|' read -r HOSTNAME HOST_IP; do "$AGENT_USER" \ "$SKIP_SSHD") - # Base64-encode the script so it can be passed cleanly through su - root -c - # su -c replaces stdin so 'bash -s' heredoc approach doesn't work REMOTE_SCRIPT_B64=$(echo "$REMOTE_SCRIPT" | base64 -w 0) - # Execute via su - on remote host - log_info "Executing bootstrap via su - root..." - BOOTSTRAP_OUTPUT=$(sshpass -p "$NATIVE_PASS" ssh \ - -o StrictHostKeyChecking=no \ - -o ConnectTimeout=10 \ - -o PasswordAuthentication=yes \ - "$NATIVE_USER@$HOST_IP" \ - "echo '$ROOT_PASS' | su - root -c 'echo $REMOTE_SCRIPT_B64 | base64 -d | bash'" \ - < /dev/null 2>&1) || { - log_error "Bootstrap script failed on $HOSTNAME" - echo "$BOOTSTRAP_OUTPUT" | sed 's/^/ /' - FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — bootstrap script failed") - ((HOSTS_FAILED++)) || true - continue - } + # Execute — if native user is root, run directly; otherwise use su - + log_info "Executing bootstrap..." + if [[ "$EFFECTIVE_NATIVE_USER" == "root" ]]; then + BOOTSTRAP_OUTPUT=$(sshpass -p "$EFFECTIVE_NATIVE_PASS" ssh \ + -o StrictHostKeyChecking=no \ + -o ConnectTimeout=10 \ + -o PasswordAuthentication=yes \ + "root@$HOST_IP" \ + "echo $REMOTE_SCRIPT_B64 | base64 -d | bash" \ + < /dev/null 2>&1) || { + log_error "Bootstrap script failed on $HOSTNAME" + echo "$BOOTSTRAP_OUTPUT" | sed 's/^/ /' + FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — bootstrap script failed") + ((HOSTS_FAILED++)) || true + continue + } + else + log_info "Escalating via su - root..." + BOOTSTRAP_OUTPUT=$(sshpass -p "$EFFECTIVE_NATIVE_PASS" ssh \ + -o StrictHostKeyChecking=no \ + -o ConnectTimeout=10 \ + -o PasswordAuthentication=yes \ + "$EFFECTIVE_NATIVE_USER@$HOST_IP" \ + "echo '$EFFECTIVE_ROOT_PASS' | su - root -c 'echo $REMOTE_SCRIPT_B64 | base64 -d | bash'" \ + < /dev/null 2>&1) || { + log_error "Bootstrap script failed on $HOSTNAME" + echo "$BOOTSTRAP_OUTPUT" | sed 's/^/ /' + FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — bootstrap script failed") + ((HOSTS_FAILED++)) || true + continue + } + fi # Show remote output echo "$BOOTSTRAP_OUTPUT" | grep "\[remote\]" | sed 's/^/ /' @@ -478,3 +514,4 @@ echo "" if [[ $HOSTS_FAILED -gt 0 ]]; then exit 1 fi +