From 111d3bea660ef62d977a130db727a1106f1052cc Mon Sep 17 00:00:00 2001 From: Wesley van Tilburg Date: Tue, 20 Oct 2026 13:23:17 +0000 Subject: [PATCH] fedora-intune: give the Intune compliance check a Defender health snapshot Himmelblau runs the custom compliance script inside himmelblaud-tasks, and Defender refuses to answer `mdatp health` there: the daemon can't read /proc//cmdline while checking the caller ("Permission denied" in microsoft_defender_err.log). The script saw no daemon, and every Defender rule failed although Defender was healthy. Add mdatp-health-snapshot.timer, which saves the health fields to /run/mdatp-health every 5 minutes as root, outside that service. The compliance script reads the snapshot when it's under 20 minutes old and otherwise asks mdatp directly, so it still works on other distros. Co-Authored-By: Claude Opus 5.5 (1M context) --- images/fedora-intune/defender.yaml | 7 +++- images/fedora-intune/files/50-defender.preset | 2 + .../fedora-intune/files/mdatp-health-snapshot | 40 +++++++++++++++++++ .../files/mdatp-health-snapshot.service | 7 ++++ .../files/mdatp-health-snapshot.timer | 9 +++++ .../intune/defender-compliance.sh | 12 +++++- 6 files changed, 75 insertions(+), 2 deletions(-) create mode 100644 images/fedora-intune/files/mdatp-health-snapshot create mode 100644 images/fedora-intune/files/mdatp-health-snapshot.service create mode 100644 images/fedora-intune/files/mdatp-health-snapshot.timer diff --git a/images/fedora-intune/defender.yaml b/images/fedora-intune/defender.yaml index 73561d3..ce12384 100644 --- a/images/fedora-intune/defender.yaml +++ b/images/fedora-intune/defender.yaml @@ -26,6 +26,11 @@ add-files: - ["mdatp-firstboot.service", "/usr/lib/systemd/system/mdatp-firstboot.service"] - ["mdatp-firstboot.conf", "/usr/lib/systemd/system/mdatp.service.d/10-firstboot.conf"] - ["50-defender.preset", "/usr/lib/systemd/system-preset/50-defender.preset"] + # Defender health for the Intune compliance script, which can't query + # Defender itself from inside himmelblaud-tasks; see the script. + - ["mdatp-health-snapshot", "/usr/libexec/mdatp-health-snapshot"] + - ["mdatp-health-snapshot.service", "/usr/lib/systemd/system/mdatp-health-snapshot.service"] + - ["mdatp-health-snapshot.timer", "/usr/lib/systemd/system/mdatp-health-snapshot.timer"] # Enforced Defender settings (shown as "[managed]" in `mdatp health`; local # changes are refused): real-time protection, behavior monitoring, PUA # blocking, cloud protection at the "high" block level, automatic updates @@ -57,4 +62,4 @@ postprocess: # Fail the build on a typo rather than ship a config Defender can't read. python3 -m json.tool /usr/share/mdatp-managed/mdatp_managed.json > /dev/null rpm -q --qf '%{POSTIN}' mdatp > /usr/libexec/mdatp-postinst - chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst + chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst /usr/libexec/mdatp-health-snapshot diff --git a/images/fedora-intune/files/50-defender.preset b/images/fedora-intune/files/50-defender.preset index dec91a5..d16089b 100644 --- a/images/fedora-intune/files/50-defender.preset +++ b/images/fedora-intune/files/50-defender.preset @@ -2,3 +2,5 @@ enable mdatp-firstboot.service enable mdatp.service enable mde_netfilter_v2.socket +# Health snapshot for the Intune compliance script. +enable mdatp-health-snapshot.timer diff --git a/images/fedora-intune/files/mdatp-health-snapshot b/images/fedora-intune/files/mdatp-health-snapshot new file mode 100644 index 0000000..acdf0d7 --- /dev/null +++ b/images/fedora-intune/files/mdatp-health-snapshot @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +# Save `mdatp health` fields to /run/mdatp-health/ for the Intune +# custom compliance script (intune/defender-compliance.sh). +# +# That script runs inside himmelblaud-tasks.service, and Defender refuses to +# answer it there: the daemon checks the calling process tree, can't read +# /proc//cmdline ("Permission denied" in +# microsoft_defender_err.log), and the script sees no daemon. This runs from a +# timer outside that service instead. Each file holds the raw +# `mdatp health --field` output; the directory is replaced as a whole. +set -euo pipefail + +MDATP=/usr/bin/mdatp +DEST=/run/mdatp-health +FIELDS=( + healthy + licensed + org_id + real_time_protection_enabled + behavior_monitoring + cloud_enabled + definitions_status + definitions_updated_minutes_ago + app_version +) + +tmp="$(mktemp -d "${DEST}.XXXXXX")" +trap 'rm -rf "${tmp}"' EXIT +for field in "${FIELDS[@]}"; do + "${MDATP}" health --field "${field}" > "${tmp}/${field}" 2>/dev/null || true +done +chmod 0755 "${tmp}" +chmod 0644 "${tmp}"/* + +# Swap in the new snapshot; readers see either the old or the new one. +rm -rf "${DEST}.old" +[[ -d "${DEST}" ]] && mv "${DEST}" "${DEST}.old" +mv "${tmp}" "${DEST}" +rm -rf "${DEST}.old" +trap - EXIT diff --git a/images/fedora-intune/files/mdatp-health-snapshot.service b/images/fedora-intune/files/mdatp-health-snapshot.service new file mode 100644 index 0000000..3a06042 --- /dev/null +++ b/images/fedora-intune/files/mdatp-health-snapshot.service @@ -0,0 +1,7 @@ +[Unit] +Description=Save Microsoft Defender health for the Intune compliance check +After=mdatp.service + +[Service] +Type=oneshot +ExecStart=/usr/libexec/mdatp-health-snapshot diff --git a/images/fedora-intune/files/mdatp-health-snapshot.timer b/images/fedora-intune/files/mdatp-health-snapshot.timer new file mode 100644 index 0000000..0d934e1 --- /dev/null +++ b/images/fedora-intune/files/mdatp-health-snapshot.timer @@ -0,0 +1,9 @@ +[Unit] +Description=Save Microsoft Defender health every 5 minutes + +[Timer] +OnBootSec=2min +OnUnitActiveSec=5min + +[Install] +WantedBy=timers.target diff --git a/images/fedora-intune/intune/defender-compliance.sh b/images/fedora-intune/intune/defender-compliance.sh index 213fcd9..78f4163 100755 --- a/images/fedora-intune/intune/defender-compliance.sh +++ b/images/fedora-intune/intune/defender-compliance.sh @@ -10,11 +10,21 @@ # instead of as a script error. MDATP=/usr/bin/mdatp +# Under Himmelblau, Defender won't answer `mdatp health` from this script, so +# a root timer may save the fields in /run/mdatp-health (the fedora-intune +# image does). Used when under 20 minutes old, otherwise mdatp is asked. +SNAPSHOT=/run/mdatp-health + +if [[ -n "$(find "${SNAPSHOT}/healthy" -mmin -20 2>/dev/null)" ]]; then + raw_field() { cat "${SNAPSHOT}/$1" 2>/dev/null; } +else + raw_field() { "${MDATP}" health --field "$1" 2>/dev/null; } +fi # Print one `mdatp health` field without quotes or a trailing " [managed]". # Unlicensed or unonboarded, mdatp prints "ATTENTION: ..." lines first. field() { - "${MDATP}" health --field "$1" 2>/dev/null | grep -v '^ATTENTION' | tail -n1 \ + raw_field "$1" | grep -v '^ATTENTION' | tail -n1 \ | sed -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//' } -- 2.54.0