fix(bootstrap): AGENT_USER parameter for subodev/ultixfield systems

agent-bootstrap.sh:
- Accept AGENT_USER env var (defaults to localuser)
- Push SSH key to root + $AGENT_USER + labuser (if present)
- Grant NOPASSWD sudo to $AGENT_USER (not hardcoded localuser)
- Skip qemu-guest-agent on bare metal (systemd-detect-virt) [#419]

bootstrap-all.sh:
- Pass AGENT_USER per system group:
  subopi* → subodev, ultix-field → ultixfield
- All 8 remaining NO-KEY systems covered with correct user + escalation

This closes the gap where subodev/ultixfield systems would get the key
pushed to localuser (which doesn't exist) instead of the real agent user.

Refs [#403], [#419]

💘 Generated with Crush

Assisted-by: Crush:glm-5.2
This commit is contained in:
2026-08-10 15:38:30 -05:00
parent 13f4e44671
commit dbecf87806
2 changed files with 46 additions and 42 deletions
+23 -22
View File
@@ -1,31 +1,31 @@
#!/bin/sh #!/bin/sh
# agent-bootstrap.sh # agent-bootstrap.sh
# #
# Run INSIDE a guest (via noVNC console login, or any root shell) to bring the # Run INSIDE a guest (via SSH, console, or guest-agent) to bring the
# system fully under agent management in one shot: # system fully under agent management in one shot:
# 1. install + enable qemu-guest-agent (so Proxmox can reach the guest) # 1. install + enable qemu-guest-agent (VMs only, skipped on bare metal)
# 2. push the agent SSH key to root + localuser # 2. push the agent SSH key to root + AGENT_USER (+ labuser if present)
# 3. grant localuser passwordless sudo # 3. grant AGENT_USER passwordless sudo
# #
# After this runs once, the agent has SSH+sudo immediately. No reboot needed # AGENT_USER defaults to "localuser". Override for systems with a different
# for the SSH key; the guest-agent channel activates as soon as the service starts. # unprivileged agent user:
# AGENT_USER=subodev bash agent-bootstrap.sh
#
# After this runs once, the agent has SSH+sudo immediately.
# #
# Usage (from a root shell in the guest): # Usage (from a root shell in the guest):
# bash agent-bootstrap.sh # bash agent-bootstrap.sh
# Or one-liner (paste into console after login): # AGENT_USER=subodev bash agent-bootstrap.sh
# apt-get update && apt-get install -y qemu-guest-agent && systemctl enable --now qemu-guest-agent && \
# mkdir -p /root/.ssh /home/localuser/.ssh && chmod 700 /root/.ssh /home/localuser/.ssh && \
# KEY='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWms/uCXnjjo4KyxHBcYI2TDHe8OZ2wle6W/0hSRQLu reachableceo@ultix-streaming' && \
# for u in root localuser; do AK=$(getent passwd "$u"|cut -d: -f6)/.ssh/authorized_keys; touch "$AK"; chmod 600 "$AK"; grep -qF "$KEY" "$AK" || echo "$KEY" >> "$AK"; chown "$u": "$AK"; done && \
# id localuser >/dev/null 2>&1 && { echo 'localuser ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/010-agent; chmod 440 /etc/sudoers.d/010-agent; }; \
# echo BOOTSTRAP-DONE
set -eu set -eu
KEY='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWms/uCXnjjo4KyxHBcYI2TDHe8OZ2wle6W/0hSRQLu reachableceo@ultix-streaming' KEY='ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIIWms/uCXnjjo4KyxHBcYI2TDHe8OZ2wle6W/0hSRQLu reachableceo@ultix-streaming'
AGENT_USER="${AGENT_USER:-localuser}"
# 1. guest-agent # 1. guest-agent (skip on bare metal — no virtio-serial device)
if ! command -v qemu-ga >/dev/null 2>&1; then if command -v systemd-detect-virt >/dev/null 2>&1 && \
[ "$(systemd-detect-virt --vm 2>/dev/null || echo none)" != "none" ]; then
if ! command -v qemu-ga >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then if command -v apt-get >/dev/null 2>&1; then
apt-get update apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install -y qemu-guest-agent DEBIAN_FRONTEND=noninteractive apt-get install -y qemu-guest-agent
@@ -36,12 +36,13 @@ if ! command -v qemu-ga >/dev/null 2>&1; then
else else
echo "WARN: no supported package manager; skipping agent install" >&2 echo "WARN: no supported package manager; skipping agent install" >&2
fi fi
fi fi
systemctl enable --now qemu-guest-agent 2>/dev/null || \ systemctl enable --now qemu-guest-agent 2>/dev/null || \
systemctl enable --now qemu-ga 2>/dev/null || true systemctl enable --now qemu-ga 2>/dev/null || true
fi
# 2. SSH key for root + all unprivileged agents (localuser, labuser) # 2. SSH key for root + AGENT_USER + labuser (if present)
for u in root localuser labuser; do for u in root "$AGENT_USER" labuser; do
if ! getent passwd "$u" >/dev/null 2>&1; then continue; fi if ! getent passwd "$u" >/dev/null 2>&1; then continue; fi
H=$(getent passwd "$u" | cut -d: -f6) H=$(getent passwd "$u" | cut -d: -f6)
mkdir -p "$H/.ssh"; chmod 700 "$H/.ssh" mkdir -p "$H/.ssh"; chmod 700 "$H/.ssh"
@@ -50,9 +51,9 @@ for u in root localuser labuser; do
chown -R "$u": "$H/.ssh" chown -R "$u": "$H/.ssh"
done done
# 3. passwordless sudo for localuser ONLY (per policy — labuser gets no sudo) # 3. passwordless sudo for AGENT_USER only
if getent passwd localuser >/dev/null 2>&1 && [ -d /etc/sudoers.d ]; then if getent passwd "$AGENT_USER" >/dev/null 2>&1 && [ -d /etc/sudoers.d ]; then
echo 'localuser ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/010-agent echo "${AGENT_USER} ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/010-agent
chmod 440 /etc/sudoers.d/010-agent chmod 440 /etc/sudoers.d/010-agent
fi fi
+14 -11
View File
@@ -5,6 +5,9 @@
# sudo → Ubuntu systems (no root password, localuser has sudo) # sudo → Ubuntu systems (no root password, localuser has sudo)
# su → Debian systems (root has a password) # su → Debian systems (root has a password)
# #
# Passes AGENT_USER so agent-bootstrap.sh pushes the key + sudo to the
# correct unprivileged user per system type.
#
# You'll enter passwords interactively. Idempotent: safe to re-run. # You'll enter passwords interactively. Idempotent: safe to re-run.
set -u set -u
cd "$(dirname "$0")" || exit 1 cd "$(dirname "$0")" || exit 1
@@ -13,24 +16,24 @@ SCRIPT=agent-bootstrap.sh
SSH_OPTS=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10) SSH_OPTS=(-o StrictHostKeyChecking=accept-new -o ConnectTimeout=10)
run_with_sudo() { run_with_sudo() {
local name="$1" ip="$2" user="$3" local name="$1" ip="$2" user="$3" agent_user="${4:-localuser}"
echo "========================================" echo "========================================"
echo " $name ($ip) — $user (sudo)" echo " $name ($ip) — $user (sudo, agent=${agent_user})"
echo "========================================" echo "========================================"
scp "${SSH_OPTS[@]}" "$SCRIPT" "${user}@${ip}:/tmp/" \ scp "${SSH_OPTS[@]}" "$SCRIPT" "${user}@${ip}:/tmp/" \
&& ssh -t "${SSH_OPTS[@]}" "${user}@${ip}" "sudo bash /tmp/$SCRIPT" \ && ssh -t "${SSH_OPTS[@]}" "${user}@${ip}" "sudo AGENT_USER=${agent_user} bash /tmp/$SCRIPT" \
&& echo " -> $name DONE" \ && echo " -> $name DONE" \
|| echo " -> $name FAILED" || echo " -> $name FAILED"
echo echo
} }
run_with_su() { run_with_su() {
local name="$1" ip="$2" user="$3" local name="$1" ip="$2" user="$3" agent_user="${4:-localuser}"
echo "========================================" echo "========================================"
echo " $name ($ip) — $user (su)" echo " $name ($ip) — $user (su, agent=${agent_user})"
echo "========================================" echo "========================================"
scp "${SSH_OPTS[@]}" "$SCRIPT" "${user}@${ip}:/tmp/" \ scp "${SSH_OPTS[@]}" "$SCRIPT" "${user}@${ip}:/tmp/" \
&& ssh -t "${SSH_OPTS[@]}" "${user}@${ip}" "su -c 'bash /tmp/$SCRIPT'" \ && ssh -t "${SSH_OPTS[@]}" "${user}@${ip}" "su -c 'AGENT_USER=${agent_user} bash /tmp/$SCRIPT'" \
&& echo " -> $name DONE" \ && echo " -> $name DONE" \
|| echo " -> $name FAILED" || echo " -> $name FAILED"
echo echo
@@ -44,17 +47,17 @@ run_with_sudo sectestbed-cloudron 100.97.140.105 localuser
run_with_sudo kali-tsys 100.82.30.115 localuser run_with_sudo kali-tsys 100.82.30.115 localuser
run_with_sudo pfvsvrpi 100.91.151.113 localuser run_with_sudo pfvsvrpi 100.91.151.113 localuser
# === Debian: subodev + su (root password reset) === # === Debian: subodev + su ===
echo "### Debian: subodev + su ###" echo "### Debian: subodev + su ###"
echo echo
run_with_su subopi3 100.93.17.77 subodev run_with_su subopi3 100.93.17.77 subodev subodev
run_with_su subopi-dev-3 100.64.231.65 subodev run_with_su subopi-dev-3 100.64.231.65 subodev subodev
run_with_su subopi-dev-4 100.65.224.85 subodev run_with_su subopi-dev-4 100.65.224.85 subodev subodev
# === Debian: ultixfield + su === # === Debian: ultixfield + su ===
echo "### Debian: ultixfield + su ###" echo "### Debian: ultixfield + su ###"
echo echo
run_with_su ultix-field 100.115.233.124 ultixfield run_with_su ultix-field 100.115.233.124 ultixfield ultixfield
echo "========================================" echo "========================================"
echo "Done. Tell the agent to re-run access-matrix.sh to verify." echo "Done. Tell the agent to re-run access-matrix.sh to verify."