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/"$//' }