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.
45 lines
2.5 KiB
Plaintext
45 lines
2.5 KiB
Plaintext
# Raw module-policy syntax (compiled with `checkmodule -M -m`), NOT the refpolicy
|
|
# `policy_module()` m4 macro — that macro is only expanded by the refpolicy
|
|
# Makefile and checkmodule rejects it. See chrony-dhcp-dispatcher.te for the why.
|
|
module fleet_agent_chronyc 1.0.0;
|
|
|
|
########################################
|
|
# Let the fleet-agent's chrony probe transition to chronyc_t under NoNewPrivileges.
|
|
#
|
|
# fleet-agent (telemetry) shells out to `chronyc -n tracking` to report the host's
|
|
# clock offset / NTP source / stratum. The agent runs as unconfined_service_t, and
|
|
# the shipped chrony policy has a type_transition so executing chronyc moves the
|
|
# child into the confined chronyc_t domain. BUT the fleet-agent unit is hardened
|
|
# with NoNewPrivileges (set explicitly AND forced by DynamicUser + SystemCallFilter
|
|
# + MemoryDenyWriteExecute), and SELinux forbids an NNP process from transitioning
|
|
# domains unless process2:nnp_transition is granted for that source->target pair.
|
|
# The shipped policy has no such grant, so under enforcing the exec is DENIED:
|
|
# avc: denied { nnp_transition } comm="fleet-agent"
|
|
# scontext=...:unconfined_service_t tcontext=...:chronyc_t tclass=process2
|
|
# chronyc never runs, chronyTracking() returns empty, and every chrony_* health
|
|
# field stays unset (the UI shows chrony as "—"). uptime/cpu/mem are unaffected —
|
|
# they read /proc directly and need no domain transition.
|
|
#
|
|
# This single allow lets the transition proceed to the MORE-confined chronyc_t
|
|
# domain (the safe direction NNP's nnp_transition permission exists to gate), with
|
|
# no loosening of the agent's own sandbox. Minimal + auditable; justified in
|
|
# docs/stig-deviations.md #16.
|
|
#
|
|
# NB this is necessary but NOT sufficient: the agent reaches chronyd over the
|
|
# LOOPBACK UDP command socket (`chronyc -h 127.0.0.1 tracking`; the image binds it
|
|
# via `bindcmdaddress 127.0.0.1`, see stig-deviations #17) because the unix socket
|
|
# is unusable under the agent's sandbox. Under enforcing, once chronyc runs in
|
|
# chronyc_t it must also be allowed to reach chronyd_t on UDP 323 — the base chrony
|
|
# policy is expected to cover chronyc_t's command traffic, but BENCH-VERIFY on the
|
|
# first enforcing boot and, if a FOLLOW-ON chronyc_t AVC appears (e.g. a corenet
|
|
# udp_socket/name_connect denial to chronyd), extend here.
|
|
########################################
|
|
|
|
require {
|
|
type unconfined_service_t;
|
|
type chronyc_t;
|
|
class process2 nnp_transition;
|
|
}
|
|
|
|
allow unconfined_service_t chronyc_t:process2 nnp_transition;
|