scripts: deploy_agent.sh — visudo optional, per-host credentials support

This commit is contained in:
Semaphore
2026-03-13 10:11:23 -07:00
parent 8195d68746
commit a55a03de4a

View File

@@ -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