agent: query chrony over loopback UDP command socket (unix socket unusable under sandbox)

Root cause of the blank chrony fields (found on comet-beacon-54): chrony 4.x
(RHEL 9) defaults its command interface to the UNIX socket only
(/run/chrony/chronyd.sock), usable by root or the chrony group with a writable
/run/chrony. The fleet-agent unit is hardened — DynamicUser (group tss, not
chrony) + ProtectSystem=strict (/run read-only) — so it cannot open the unix
socket nor create chronyc's reply socket. Bench proof: normal-user 'chronyc
tracking' -> 506 Cannot talk to daemon; only sudo worked; 'chronyc -h 127.0.0.1'
failed and ss showed no UDP :323. So the probe always failed and every chrony_*
field stayed empty. SELinux permissive did NOT help — it was never (only) an
SELinux problem; the nnp_transition module (prev commit) is still needed for the
enforcing exec transition.

Fix: bind chronyd's command socket on 127.0.0.1 (bindcmdaddress in chrony.conf,
idempotent in the 2c provisioner) and point the probe at it (chronyc -n -h
127.0.0.1 tracking). UDP needs no fs write or group and rides the unit's
already-allowed AF_INET. Loopback + monitoring-only (state changes still need the
root unix socket), so exposure is minimal. Rejected the unix-socket route (chrony
group + ReadWritePaths=/run/chrony) because /run/chrony is 0750 (group has no
write). Documented in docs/stig-deviations.md #17; .te bench note updated for the
enforcing UDP path. Needs a workstation image rebuild + reboot.
This commit is contained in:
Audiobuzz
2026-07-10 09:02:05 +00:00
parent e6fd4a2dde
commit 394ad10824
4 changed files with 71 additions and 5 deletions
+11
View File
@@ -405,6 +405,17 @@ build {
"restorecon /etc/NetworkManager/dispatcher.d/10-chrony-dhcp-ntp",
"sed -i -E 's/^[[:space:]]*(pool|server)[[:space:]]/#&/' /etc/chrony.conf",
"grep -q '^sourcedir /run/chrony-dhcp' /etc/chrony.conf || echo 'sourcedir /run/chrony-dhcp' >> /etc/chrony.conf",
# Bind chronyd's command socket on LOOPBACK UDP so the fleet-agent can query
# clock health (`chronyc -h 127.0.0.1 tracking`). chrony 4.x (RHEL 9) defaults
# the command interface to the UNIX socket /run/chrony/chronyd.sock ONLY
# (root or the chrony group + a writable /run/chrony for chronyc's reply
# socket). The agent runs hardened — DynamicUser (not in chrony group),
# ProtectSystem=strict (/run read-only) — so it can't use the unix socket; a
# loopback UDP command socket needs no fs write or group and rides the unit's
# already-allowed AF_INET. Localhost-only + monitoring-only (state changes
# still require the root unix socket), so the exposure is minimal. Documented
# in docs/stig-deviations.md #17.
"grep -q '^bindcmdaddress 127.0.0.1' /etc/chrony.conf || echo 'bindcmdaddress 127.0.0.1' >> /etc/chrony.conf",
# Inject the fleet NIC drivers into the dracut config
"sed -i 's/@NIC_DRIVERS@/${var.nic_drivers}/' /etc/dracut.conf.d/90-diskless.conf",