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 {}
|
hvars = hvars or {}
|
||||||
merged = {**group_vars, **hvars}
|
merged = {**group_vars, **hvars}
|
||||||
ip = merged.get('ansible_host', hostname)
|
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 {}
|
children = (inv.get('all') or {}).get('children') or {}
|
||||||
extract_hosts(children.get('linux_hosts'), linux_hosts)
|
extract_hosts(children.get('linux_hosts'), linux_hosts)
|
||||||
@@ -202,7 +208,7 @@ print(json.dumps({'linux': linux_hosts, 'windows': windows_hosts}))
|
|||||||
PYEOF
|
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']]")
|
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)
|
LINUX_COUNT=$(echo "$LINUX_HOSTS" | grep -c '.' || true)
|
||||||
@@ -269,11 +275,15 @@ cat > "\$SUDOERS_FILE" << SUDOEOF
|
|||||||
%sudo-nopasswd ALL=(ALL) NOPASSWD:ALL
|
%sudo-nopasswd ALL=(ALL) NOPASSWD:ALL
|
||||||
SUDOEOF
|
SUDOEOF
|
||||||
chmod 440 "\$SUDOERS_FILE"
|
chmod 440 "\$SUDOERS_FILE"
|
||||||
visudo -cf "\$SUDOERS_FILE" && echo "[remote] Sudoers file validated OK" || {
|
if command -v visudo &>/dev/null; then
|
||||||
|
visudo -cf "\$SUDOERS_FILE" && echo "[remote] Sudoers file validated OK" || {
|
||||||
echo "[remote] ERROR: sudoers file invalid — removing"
|
echo "[remote] ERROR: sudoers file invalid — removing"
|
||||||
rm -f "\$SUDOERS_FILE"
|
rm -f "\$SUDOERS_FILE"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
echo "[remote] visudo not found — skipping validation (file written with chmod 440)"
|
||||||
|
fi
|
||||||
|
|
||||||
# ── Deploy SSH keys to agent user ──
|
# ── Deploy SSH keys to agent user ──
|
||||||
AGENT_SSH_DIR="/home/\$AGENT_USER/.ssh"
|
AGENT_SSH_DIR="/home/\$AGENT_USER/.ssh"
|
||||||
@@ -372,30 +382,41 @@ LINUX_HOSTS_FILE=$(mktemp)
|
|||||||
echo "$LINUX_HOSTS" > "$LINUX_HOSTS_FILE"
|
echo "$LINUX_HOSTS" > "$LINUX_HOSTS_FILE"
|
||||||
trap 'rm -f "$LINUX_HOSTS_FILE"' EXIT
|
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
|
[[ -z "$HOSTNAME" ]] && continue
|
||||||
((HOSTS_TOTAL++)) || true
|
((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 ""
|
echo ""
|
||||||
log_section "Host: $HOSTNAME ($HOST_IP)"
|
log_section "Host: $HOSTNAME ($HOST_IP)"
|
||||||
|
|
||||||
if [[ "$DRY_RUN" == "true" ]]; then
|
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
|
((HOSTS_OK++)) || true
|
||||||
continue
|
continue
|
||||||
fi
|
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..."
|
log_info "Scanning host key..."
|
||||||
ssh-keyscan -T 10 "$HOST_IP" >> /root/.ssh/known_hosts 2>/dev/null < /dev/null || true
|
ssh-keyscan -T 10 "$HOST_IP" >> /root/.ssh/known_hosts 2>/dev/null < /dev/null || true
|
||||||
|
|
||||||
# Test native user SSH access
|
# Test native user SSH access
|
||||||
log_info "Testing SSH as $NATIVE_USER..."
|
log_info "Testing SSH as $EFFECTIVE_NATIVE_USER..."
|
||||||
if ! sshpass -p "$NATIVE_PASS" ssh -o StrictHostKeyChecking=no \
|
if ! sshpass -p "$EFFECTIVE_NATIVE_PASS" ssh -o StrictHostKeyChecking=no \
|
||||||
-o ConnectTimeout=10 \
|
-o ConnectTimeout=10 \
|
||||||
-o PasswordAuthentication=yes \
|
-o PasswordAuthentication=yes \
|
||||||
"$NATIVE_USER@$HOST_IP" "echo connected" < /dev/null &>/dev/null; then
|
"$EFFECTIVE_NATIVE_USER@$HOST_IP" "echo connected" < /dev/null &>/dev/null; then
|
||||||
log_error "Cannot SSH to $HOSTNAME ($HOST_IP) as $NATIVE_USER — skipping"
|
log_error "Cannot SSH to $HOSTNAME ($HOST_IP) as $EFFECTIVE_NATIVE_USER — skipping"
|
||||||
FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — SSH connection failed")
|
FAILED_HOSTS+=("$HOSTNAME ($HOST_IP) — SSH connection failed")
|
||||||
((HOSTS_FAILED++)) || true
|
((HOSTS_FAILED++)) || true
|
||||||
continue
|
continue
|
||||||
@@ -409,18 +430,17 @@ while IFS='|' read -r HOSTNAME HOST_IP; do
|
|||||||
"$AGENT_USER" \
|
"$AGENT_USER" \
|
||||||
"$SKIP_SSHD")
|
"$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)
|
REMOTE_SCRIPT_B64=$(echo "$REMOTE_SCRIPT" | base64 -w 0)
|
||||||
|
|
||||||
# Execute via su - on remote host
|
# Execute — if native user is root, run directly; otherwise use su -
|
||||||
log_info "Executing bootstrap via su - root..."
|
log_info "Executing bootstrap..."
|
||||||
BOOTSTRAP_OUTPUT=$(sshpass -p "$NATIVE_PASS" ssh \
|
if [[ "$EFFECTIVE_NATIVE_USER" == "root" ]]; then
|
||||||
|
BOOTSTRAP_OUTPUT=$(sshpass -p "$EFFECTIVE_NATIVE_PASS" ssh \
|
||||||
-o StrictHostKeyChecking=no \
|
-o StrictHostKeyChecking=no \
|
||||||
-o ConnectTimeout=10 \
|
-o ConnectTimeout=10 \
|
||||||
-o PasswordAuthentication=yes \
|
-o PasswordAuthentication=yes \
|
||||||
"$NATIVE_USER@$HOST_IP" \
|
"root@$HOST_IP" \
|
||||||
"echo '$ROOT_PASS' | su - root -c 'echo $REMOTE_SCRIPT_B64 | base64 -d | bash'" \
|
"echo $REMOTE_SCRIPT_B64 | base64 -d | bash" \
|
||||||
< /dev/null 2>&1) || {
|
< /dev/null 2>&1) || {
|
||||||
log_error "Bootstrap script failed on $HOSTNAME"
|
log_error "Bootstrap script failed on $HOSTNAME"
|
||||||
echo "$BOOTSTRAP_OUTPUT" | sed 's/^/ /'
|
echo "$BOOTSTRAP_OUTPUT" | sed 's/^/ /'
|
||||||
@@ -428,6 +448,22 @@ while IFS='|' read -r HOSTNAME HOST_IP; do
|
|||||||
((HOSTS_FAILED++)) || true
|
((HOSTS_FAILED++)) || true
|
||||||
continue
|
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
|
# Show remote output
|
||||||
echo "$BOOTSTRAP_OUTPUT" | grep "\[remote\]" | sed 's/^/ /'
|
echo "$BOOTSTRAP_OUTPUT" | grep "\[remote\]" | sed 's/^/ /'
|
||||||
@@ -478,3 +514,4 @@ echo ""
|
|||||||
if [[ $HOSTS_FAILED -gt 0 ]]; then
|
if [[ $HOSTS_FAILED -gt 0 ]]; then
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user