From 4213575206b08b3a258546922a4301849e14f404 Mon Sep 17 00:00:00 2001 From: Wesley van Tilburg Date: Tue, 20 Oct 2026 12:10:58 +0000 Subject: [PATCH 1/2] fedora-intune: validate the onboarding JSON in the Defender script A paste into Intune that lost the first "{" line produced an unreadable mdatp_onboard.json, and Defender reported "No license found". Check that the pasted JSON parses and has onboardingInfo before writing it, and log what to fix otherwise. The placeholder also appeared twice, so a find-and-replace of it broke the script; match it by prefix so it appears only once. Co-Authored-By: Claude Opus 5.5 (1M context) --- images/fedora-intune/intune/defender-remediation.sh | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/images/fedora-intune/intune/defender-remediation.sh b/images/fedora-intune/intune/defender-remediation.sh index 7f8c75c..403f542 100755 --- a/images/fedora-intune/intune/defender-remediation.sh +++ b/images/fedora-intune/intune/defender-remediation.sh @@ -71,8 +71,16 @@ if ! wait_for_daemon; then fi if [[ -z "$(field org_id)" ]]; then - if [[ "${ONBOARDING_JSON}" == REPLACE_WITH_MDATP_ONBOARD_JSON ]]; then + # Prefix match, so the placeholder appears only once in this file and a + # find-and-replace of it can't hit this line. + if [[ "${ONBOARDING_JSON}" == REPLACE_WITH_* ]]; then log "Defender is not onboarded and this script has no onboarding JSON." + elif ! printf '%s\n' "${ONBOARDING_JSON}" \ + | python3 -c 'import json, sys; assert "onboardingInfo" in json.load(sys.stdin)' 2>/dev/null; then + # A paste that lost the first "{" or last "}" line would otherwise leave + # Defender with an unreadable onboarding file ("No license found"). + log "The onboarding JSON in this script is not valid; paste mdatp_onboard.json again, including its first '{' and last '}' lines." + exit 1 else log "Defender is not onboarded; writing ${ONBOARD_FILE}." install -d -m 0755 "$(dirname "${ONBOARD_FILE}")" -- 2.54.0 From ebe1d80f801ea99300282b09ca852401eaca5801 Mon Sep 17 00:00:00 2001 From: Wesley van Tilburg Date: Tue, 20 Oct 2026 12:19:17 +0000 Subject: [PATCH 2/2] fedora-intune: enforce Defender protection settings Defender's defaults left behavior monitoring off and every setting unmanaged. Ship /etc/opt/microsoft/mdatp/managed/mdatp_managed.json: real-time protection, behavior monitoring, PUA blocking, cloud-delivered protection at the high block level, automatic definition updates and sample submission, and scheduled scans (daily quick at 12:00, weekly full on Wednesday 12:30, low priority). Users can't change managed settings. The build fails if the file isn't valid JSON. Network protection (Insiders channels only) and tamper protection (audit-only preview on Linux) are left out. The compliance script and rules now also check behavior monitoring and cloud-delivered protection. Co-Authored-By: Claude Opus 5.5 (1M context) --- images/fedora-intune/defender.yaml | 11 +++++++ images/fedora-intune/files/mdatp_managed.json | 29 +++++++++++++++++++ .../intune/defender-compliance.json | 28 ++++++++++++++++++ .../intune/defender-compliance.sh | 6 ++++ 4 files changed, 74 insertions(+) create mode 100644 images/fedora-intune/files/mdatp_managed.json diff --git a/images/fedora-intune/defender.yaml b/images/fedora-intune/defender.yaml index 435b6a5..9c7d37b 100644 --- a/images/fedora-intune/defender.yaml +++ b/images/fedora-intune/defender.yaml @@ -26,6 +26,15 @@ 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"] + # 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 + # and sample submission, a daily quick scan at 12:00 and a weekly full scan + # on Wednesday 12:30 (low priority, up to 2 h later at random). Settings + # reference: https://learn.microsoft.com/defender-endpoint/linux-preferences + # Not set here: network protection (Insiders channels only) and tamper + # protection (audit-only preview on Linux). + - ["mdatp_managed.json", "/etc/opt/microsoft/mdatp/managed/mdatp_managed.json"] postprocess: - | @@ -42,5 +51,7 @@ postprocess: [[ -f "${pp}" ]] && break done semodule -n -i "${pp}" + # Fail the build on a typo rather than ship a config Defender can't read. + python3 -m json.tool /etc/opt/microsoft/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 diff --git a/images/fedora-intune/files/mdatp_managed.json b/images/fedora-intune/files/mdatp_managed.json new file mode 100644 index 0000000..9fa09da --- /dev/null +++ b/images/fedora-intune/files/mdatp_managed.json @@ -0,0 +1,29 @@ +{ + "antivirusEngine": { + "enforcementLevel": "real_time", + "behaviorMonitoring": "enabled", + "scanAfterDefinitionUpdate": true, + "scanArchives": true, + "disallowedThreatActions": ["allow", "restore"], + "threatTypeSettingsMergePolicy": "admin_only", + "threatTypeSettings": [ + { "key": "potentially_unwanted_application", "value": "block" }, + { "key": "archive_bomb", "value": "audit" } + ], + "scheduledScan": "enabled" + }, + "scheduledScan": { + "dailyConfiguration": { "timeOfDay": 720 }, + "weeklyConfiguration": { "dayOfWeek": 4, "scanType": "full", "timeOfDay": 750 }, + "lowPriorityScheduledScan": true, + "checkForDefinitionsUpdate": true, + "randomizeScanStartTime": 2 + }, + "cloudService": { + "enabled": true, + "diagnosticLevel": "required", + "automaticSampleSubmissionConsent": "safe", + "automaticDefinitionUpdateEnabled": true, + "cloudBlockLevel": "high" + } +} diff --git a/images/fedora-intune/intune/defender-compliance.json b/images/fedora-intune/intune/defender-compliance.json index da37223..498b98f 100644 --- a/images/fedora-intune/intune/defender-compliance.json +++ b/images/fedora-intune/intune/defender-compliance.json @@ -84,6 +84,34 @@ } ] }, + { + "SettingName": "DefenderBehaviorMonitoring", + "Operator": "IsEquals", + "DataType": "Boolean", + "Operand": true, + "MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually", + "RemediationStrings": [ + { + "Language": "en_US", + "Title": "Behavior monitoring is off.", + "Description": "Microsoft Defender behavior monitoring must be enabled. Update the device to the latest image or contact IT." + } + ] + }, + { + "SettingName": "DefenderCloudProtection", + "Operator": "IsEquals", + "DataType": "Boolean", + "Operand": true, + "MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually", + "RemediationStrings": [ + { + "Language": "en_US", + "Title": "Cloud-delivered protection is off.", + "Description": "Microsoft Defender cloud-delivered protection must be enabled. Update the device to the latest image or contact IT." + } + ] + }, { "SettingName": "DefenderDefinitionsUpToDate", "Operator": "IsEquals", diff --git a/images/fedora-intune/intune/defender-compliance.sh b/images/fedora-intune/intune/defender-compliance.sh index f5f3de7..213fcd9 100755 --- a/images/fedora-intune/intune/defender-compliance.sh +++ b/images/fedora-intune/intune/defender-compliance.sh @@ -28,6 +28,8 @@ healthy=false licensed=false onboarded=false rtp=false +behavior_monitoring=false +cloud_protection=false definitions_ok=false definitions_age=-1 version="" @@ -40,6 +42,8 @@ if [[ -x "${MDATP}" ]]; then healthy="$(bool "$(field healthy)")" licensed="$(bool "$(field licensed)")" rtp="$(bool "$(field real_time_protection_enabled)")" + [[ "$(field behavior_monitoring)" == enabled ]] && behavior_monitoring=true + cloud_protection="$(bool "$(field cloud_enabled)")" [[ -n "$(field org_id)" ]] && onboarded=true [[ "$(field definitions_status)" == up_to_date ]] && definitions_ok=true age="$(field definitions_updated_minutes_ago)" @@ -57,6 +61,8 @@ cat <