fedora-intune: query Defender through systemd-run instead of a snapshot #8
No files matched your search
@@ -26,11 +26,6 @@ add-files:
|
|||||||
- ["mdatp-firstboot.service", "/usr/lib/systemd/system/mdatp-firstboot.service"]
|
- ["mdatp-firstboot.service", "/usr/lib/systemd/system/mdatp-firstboot.service"]
|
||||||
- ["mdatp-firstboot.conf", "/usr/lib/systemd/system/mdatp.service.d/10-firstboot.conf"]
|
- ["mdatp-firstboot.conf", "/usr/lib/systemd/system/mdatp.service.d/10-firstboot.conf"]
|
||||||
- ["50-defender.preset", "/usr/lib/systemd/system-preset/50-defender.preset"]
|
- ["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
|
# Enforced Defender settings (shown as "[managed]" in `mdatp health`; local
|
||||||
# changes are refused): real-time protection, behavior monitoring, PUA
|
# changes are refused): real-time protection, behavior monitoring, PUA
|
||||||
# blocking, cloud protection at the "high" block level, automatic updates
|
# blocking, cloud protection at the "high" block level, automatic updates
|
||||||
@@ -62,4 +57,4 @@ postprocess:
|
|||||||
# Fail the build on a typo rather than ship a config Defender can't read.
|
# 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
|
python3 -m json.tool /usr/share/mdatp-managed/mdatp_managed.json > /dev/null
|
||||||
rpm -q --qf '%{POSTIN}' mdatp > /usr/libexec/mdatp-postinst
|
rpm -q --qf '%{POSTIN}' mdatp > /usr/libexec/mdatp-postinst
|
||||||
chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst /usr/libexec/mdatp-health-snapshot
|
chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst
|
||||||
@@ -2,5 +2,3 @@
|
|||||||
enable mdatp-firstboot.service
|
enable mdatp-firstboot.service
|
||||||
enable mdatp.service
|
enable mdatp.service
|
||||||
enable mde_netfilter_v2.socket
|
enable mde_netfilter_v2.socket
|
||||||
# Health snapshot for the Intune compliance script.
|
|
||||||
enable mdatp-health-snapshot.timer
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Save `mdatp health` fields to /run/mdatp-health/<field> 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/<himmelblaud_tasks pid>/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
|
|
||||||
)
|
|
||||||
|
|
||||||
# Right after boot the daemon may not answer yet; give it up to 2 minutes so
|
|
||||||
# the first snapshot isn't a false "not running".
|
|
||||||
for _ in $(seq 1 24); do
|
|
||||||
[[ "$("${MDATP}" health --field healthy 2>/dev/null | tail -n1)" =~ ^(true|false)$ ]] && break
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
|
|
||||||
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
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Save Microsoft Defender health for the Intune compliance check
|
|
||||||
After=mdatp.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/libexec/mdatp-health-snapshot
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Save Microsoft Defender health every 5 minutes
|
|
||||||
|
|
||||||
[Timer]
|
|
||||||
# Early, so the compliance check at the first sign-in after boot finds a
|
|
||||||
# snapshot; Himmelblau doesn't check again for 8 hours.
|
|
||||||
OnBootSec=20s
|
|
||||||
OnUnitActiveSec=5min
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=timers.target
|
|
||||||
@@ -2,30 +2,26 @@
|
|||||||
# Intune custom compliance discovery script for Linux: is Microsoft Defender
|
# Intune custom compliance discovery script for Linux: is Microsoft Defender
|
||||||
# for Endpoint (mdatp) installed, onboarded, licensed and protecting?
|
# for Endpoint (mdatp) installed, onboarded, licensed and protecting?
|
||||||
#
|
#
|
||||||
# Upload in Intune > Devices > Compliance > Scripts (Linux), and pair it with
|
# Upload in Intune > Endpoint security > Device compliance > Scripts (Linux),
|
||||||
# defender-compliance.json in a Linux compliance policy (Custom compliance).
|
# and pair it with defender-compliance.json in a Linux compliance policy
|
||||||
# Himmelblau runs it as root inside himmelblaud-tasks.service and sends the
|
# (Custom compliance). Himmelblau runs it as root inside
|
||||||
# JSON on stdout to Intune, which evaluates it against the rules. Every setting
|
# himmelblaud-tasks.service and sends the JSON on stdout to Intune, which
|
||||||
# is always emitted, so a missing or stopped Defender reads as non-compliant
|
# evaluates it against the rules. Every setting is always emitted, so a missing
|
||||||
# instead of as a script error.
|
# or stopped Defender reads as non-compliant instead of as a script error.
|
||||||
|
|
||||||
MDATP=/usr/bin/mdatp
|
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
|
# Defender won't answer `mdatp health` started from inside himmelblaud-tasks
|
||||||
raw_field() { cat "${SNAPSHOT}/$1" 2>/dev/null; }
|
# (it can't read that process while checking the caller), so ask through
|
||||||
else
|
# systemd, which runs mdatp as its own child. Elsewhere, ask mdatp directly.
|
||||||
raw_field() { "${MDATP}" health --field "$1" 2>/dev/null; }
|
health="$(systemd-run --quiet --wait --pipe --collect "${MDATP}" health 2>/dev/null)"
|
||||||
fi
|
grep -q '^healthy' <<< "${health}" || health="$("${MDATP}" health 2>/dev/null)"
|
||||||
|
|
||||||
# Print one `mdatp health` field without quotes or a trailing " [managed]".
|
# Print one field of the `mdatp health` output ("name : value") without
|
||||||
# Unlicensed or unonboarded, mdatp prints "ATTENTION: ..." lines first.
|
# quotes or a trailing " [managed]".
|
||||||
field() {
|
field() {
|
||||||
raw_field "$1" | grep -v '^ATTENTION' | tail -n1 \
|
grep -m1 "^$1 *:" <<< "${health}" \
|
||||||
| sed -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//'
|
| sed -e 's/^[^:]*: *//' -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//'
|
||||||
}
|
}
|
||||||
|
|
||||||
bool() {
|
bool() {
|
||||||
|
|||||||
Reference in new issue
Block a user