scripts: deploy_agent.sh — visudo optional, per-host credentials support
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user