fleet-agent reports clock health via 'chronyc -n tracking', but the hardened
unit runs with NoNewPrivileges (explicit + forced by DynamicUser/SystemCallFilter/
MemoryDenyWriteExecute). SELinux transitions the exec'd chronyc into chronyc_t,
and NNP forbids that domain change without process2:nnp_transition — which the
base policy doesn't grant for unconfined_service_t -> chronyc_t. Under enforcing
the exec is denied (avc: denied { nnp_transition } comm=fleet-agent, seen on
comet-beacon-54), so chronyc never runs and every chrony_* health field stays
empty (Monitor chrony row shows '—'). uptime/cpu/mem are unaffected (no exec).
Ship a minimal local module (files/stig/fleet-agent-chronyc.te) granting only
that one nnp_transition to the MORE-confined chronyc_t domain — the safe
direction the permission exists to gate — loosening nothing else in the agent
sandbox. Built + semodule -i'd in 2c-STIG alongside the existing
chrony_dhcp_dispatcher / fapolicyd_tmpfs modules. Justified in
docs/stig-deviations.md #16. Needs a workstation image rebuild + reboot; same
policy-store-rebuild reproducibility re-check as #14/#15.
39 lines
2.1 KiB
Plaintext
39 lines
2.1 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. BENCH-VERIFY: after this, the manager's Monitor chrony
|
|
# row populates (offset/source/stratum). If a FOLLOW-ON chronyc_t AVC appears on
|
|
# the bench (e.g. reaching chronyd's socket under the agent's mount namespace),
|
|
# extend here.
|
|
########################################
|
|
|
|
require {
|
|
type unconfined_service_t;
|
|
type chronyc_t;
|
|
class process2 nnp_transition;
|
|
}
|
|
|
|
allow unconfined_service_t chronyc_t:process2 nnp_transition;
|