feat(host_agent): a root helper the agent can start, for updates, reboots and drains only (#5766)
#5766 "A root helper the agent can start, for a fixed set of maintenance actions and nothing else", milestone 514. The agent runs unprivileged under NoNewPrivileges, so it can't update or reboot its host. It now leaves a request in /var/lib/steward-agent/requests, the one path its unit lets it write; steward-maint.path starts maint.py as root, which reads it without following links, accepts only update, reboot, drain or undrain, deletes it, and writes the outcome to /var/lib/steward-maint/results. The agent (1.17.0) reports each new result state once as a `maintenance` event, re-checks pending updates when an update finishes, and says in its metadata whether the helper is installed. The units and the helper are served from one copy at /plugins/host_agent/files/<name> (a fixed list), and install.sh plus the install, provision and update playbooks all fetch them, replacing three inline copies of the unit. Update agent installs the helper on existing hosts; the Agent panel says under Manage agent whether it is there. Steward handing out jobs is #5767; the cadence is #5768. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,9 @@ AGENT_VERSION="{{ agent_version }}"
|
||||
AGENT_USER="steward-agent"
|
||||
AGENT_DIR="/usr/local/lib/steward-agent"
|
||||
CONF_FILE="/etc/steward-agent.conf"
|
||||
UNIT_FILE="/etc/systemd/system/steward-agent.service"
|
||||
UNIT_DIR="/etc/systemd/system"
|
||||
UNITS="steward-agent.service steward-maint.service steward-maint.path"
|
||||
FILES_URL="${STEWARD_URL}/plugins/host_agent/files"
|
||||
|
||||
# ── preflight ────────────────────────────────────────────────────────────────
|
||||
[ "$(id -u)" = "0" ] || { echo "must run as root (use sudo)"; exit 1; }
|
||||
@@ -20,9 +22,10 @@ command -v python3 >/dev/null 2>&1 || { echo "python3 not found — install py
|
||||
|
||||
# Handle --uninstall
|
||||
if [ "${1:-}" = "--uninstall" ]; then
|
||||
systemctl disable --now steward-agent.service 2>/dev/null || true
|
||||
rm -f "$UNIT_FILE" "$CONF_FILE"
|
||||
rm -rf "$AGENT_DIR"
|
||||
systemctl disable --now steward-maint.path steward-agent.service 2>/dev/null || true
|
||||
for u in $UNITS; do rm -f "$UNIT_DIR/$u"; done
|
||||
rm -f "$CONF_FILE"
|
||||
rm -rf "$AGENT_DIR" /var/lib/steward-agent /var/lib/steward-maint
|
||||
systemctl daemon-reload
|
||||
# keep the system user — removing it risks orphaned files elsewhere
|
||||
echo "uninstalled"
|
||||
@@ -36,7 +39,7 @@ fi
|
||||
|
||||
# ── drop the agent file ──────────────────────────────────────────────────────
|
||||
mkdir -p "$AGENT_DIR"
|
||||
curl -sSL "${STEWARD_URL}/plugins/host_agent/agent.py" -o "$AGENT_DIR/agent.py"
|
||||
curl -fsSL "${STEWARD_URL}/plugins/host_agent/agent.py" -o "$AGENT_DIR/agent.py"
|
||||
chmod 0755 "$AGENT_DIR/agent.py"
|
||||
chown root:root "$AGENT_DIR/agent.py"
|
||||
|
||||
@@ -53,31 +56,24 @@ EOF
|
||||
chown "root:$AGENT_USER" "$CONF_FILE"
|
||||
chmod 0640 "$CONF_FILE"
|
||||
|
||||
# ── write systemd unit ───────────────────────────────────────────────────────
|
||||
cat > "$UNIT_FILE" <<EOF
|
||||
[Unit]
|
||||
Description=Steward host agent
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=$AGENT_USER
|
||||
ExecStart=/usr/bin/python3 $AGENT_DIR/agent.py
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
NoNewPrivileges=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ReadOnlyPaths=/proc /sys
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
# ── the maintenance helper and the units (#5766) ─────────────────────────────
|
||||
# One copy, served by Steward, the same the playbooks install. The agent may
|
||||
# write only its request directory; the helper (root) and its results don't
|
||||
# belong to it.
|
||||
curl -fsSL "${FILES_URL}/maint.py" -o "$AGENT_DIR/maint.py"
|
||||
chmod 0755 "$AGENT_DIR/maint.py"
|
||||
chown root:root "$AGENT_DIR/maint.py"
|
||||
install -d -o root -g root -m 0755 /var/lib/steward-agent /var/lib/steward-maint /var/lib/steward-maint/results
|
||||
install -d -o "$AGENT_USER" -g "$AGENT_USER" -m 0700 /var/lib/steward-agent/requests
|
||||
for u in $UNITS; do
|
||||
curl -fsSL "${FILES_URL}/$u" -o "$UNIT_DIR/$u"
|
||||
chmod 0644 "$UNIT_DIR/$u"
|
||||
done
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl enable --now steward-agent.service
|
||||
systemctl enable --now steward-maint.path
|
||||
systemctl enable steward-agent.service
|
||||
systemctl restart steward-agent.service
|
||||
|
||||
echo
|
||||
echo "Steward host agent $AGENT_VERSION installed and running."
|
||||
|
||||
@@ -78,6 +78,11 @@
|
||||
Link an Ansible target (below) to update the agent from here.
|
||||
</p>
|
||||
{% endif %}
|
||||
{# The root helper that carries out scheduled updates and reboots (#5766). #}
|
||||
<p style="font-size:0.8rem;margin:0.6rem 0 0;color:var(--text-dim);">
|
||||
{% if reg.maint_helper %}Maintenance helper installed: Steward can update and reboot this host.
|
||||
{% else %}No maintenance helper yet: Update agent installs it, so Steward can update and reboot this host.{% endif %}
|
||||
</p>
|
||||
|
||||
{% if ansible_available and target and managed_key_set %}
|
||||
<details style="margin-top:0.7rem;">
|
||||
|
||||
Reference in New Issue
Block a user