Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
99400fb03e | ||
|
|
ca115922bd | ||
|
|
082ae219b1 | ||
|
|
49b360dffa | ||
|
|
811025773e | ||
|
|
111d3bea66 | ||
|
|
4d780e900a | ||
|
|
e8cfa8d4cf | ||
|
|
5504f4e7aa | ||
|
|
561036582c | ||
|
|
86039eeb9d | ||
|
|
3afef0d821 | ||
|
|
07434cedab |
No files matched your search
@@ -26,6 +26,18 @@ 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"]
|
||||||
|
# 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", "/usr/share/mdatp-managed/mdatp_managed.json"]
|
||||||
|
# Installed into /etc/opt/microsoft/mdatp/managed/ whenever Defender starts;
|
||||||
|
# see the drop-in.
|
||||||
|
- ["mdatp-managed-config.conf", "/usr/lib/systemd/system/mdatp.service.d/20-managed-config.conf"]
|
||||||
|
|
||||||
postprocess:
|
postprocess:
|
||||||
- |
|
- |
|
||||||
@@ -42,5 +54,7 @@ postprocess:
|
|||||||
[[ -f "${pp}" ]] && break
|
[[ -f "${pp}" ]] && break
|
||||||
done
|
done
|
||||||
semodule -n -i "${pp}"
|
semodule -n -i "${pp}"
|
||||||
|
# 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
|
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
|
||||||
@@ -5,16 +5,23 @@
|
|||||||
# "44". Himmelblau reports VERSION_ID as the device's OS version and has no
|
# "44". Himmelblau reports VERSION_ID as the device's OS version and has no
|
||||||
# setting for it. The rest of the system keeps the stock VERSION_ID, which
|
# setting for it. The rest of the system keeps the stock VERSION_ID, which
|
||||||
# toolbox, flatpak and dnf expect to be the plain release.
|
# toolbox, flatpak and dnf expect to be the plain release.
|
||||||
|
#
|
||||||
|
# PRETTY_NAME becomes just NAME: libhimmelblau reports "PRETTY_NAME VERSION_ID"
|
||||||
|
# as the OS version string, and Fedora's PRETTY_NAME already contains the
|
||||||
|
# version ("Fedora Linux 44.20261020.0 (Forty Four) 44.20261020.0"). Nothing
|
||||||
|
# else in Himmelblau reads PRETTY_NAME.
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
SRC=/usr/lib/os-release
|
SRC=/usr/lib/os-release
|
||||||
DEST=/run/himmelblau-os-release
|
DEST=/run/himmelblau-os-release
|
||||||
|
|
||||||
version="$(. "${SRC}" && echo "${OSTREE_VERSION:-${IMAGE_VERSION:-}}")"
|
version="$(. "${SRC}" && echo "${OSTREE_VERSION:-${IMAGE_VERSION:-}}")"
|
||||||
|
name="$(. "${SRC}" && echo "${NAME:-Linux}")"
|
||||||
|
|
||||||
tmp="$(mktemp "${DEST}.XXXXXX")"
|
tmp="$(mktemp "${DEST}.XXXXXX")"
|
||||||
if [[ -n "${version}" ]]; then
|
if [[ -n "${version}" ]]; then
|
||||||
sed "s/^VERSION_ID=.*/VERSION_ID=${version}/" "${SRC}" > "${tmp}"
|
sed -e "s/^VERSION_ID=.*/VERSION_ID=${version}/" \
|
||||||
|
-e "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${name}\"/" "${SRC}" > "${tmp}"
|
||||||
else
|
else
|
||||||
# No image version (not an ostree/bootc deployment): pass it through as-is.
|
# No image version (not an ostree/bootc deployment): pass it through as-is.
|
||||||
cat "${SRC}" > "${tmp}"
|
cat "${SRC}" > "${tmp}"
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
# Install the image's enforced Defender settings each time Defender starts.
|
||||||
|
# The image can't ship them in /etc directly: mdatp-firstboot creates
|
||||||
|
# /etc/opt/microsoft/mdatp on the device, so on updates that directory counts
|
||||||
|
# as local and ostree's /etc merge never adds new files under it. Copying on
|
||||||
|
# every start also picks up changes from newer images and undoes local edits.
|
||||||
|
[Service]
|
||||||
|
ExecStartPre=+/usr/bin/install -D -Z -m 0644 /usr/share/mdatp-managed/mdatp_managed.json /etc/opt/microsoft/mdatp/managed/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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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",
|
"SettingName": "DefenderDefinitionsUpToDate",
|
||||||
"Operator": "IsEquals",
|
"Operator": "IsEquals",
|
||||||
|
|||||||
@@ -2,20 +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
|
||||||
|
|
||||||
# Print one `mdatp health` field without quotes or a trailing " [managed]".
|
# Defender won't answer `mdatp health` started from inside himmelblaud-tasks
|
||||||
# Unlicensed or unonboarded, mdatp prints "ATTENTION: ..." lines first.
|
# (it can't read that process while checking the caller), so ask through
|
||||||
|
# systemd, which runs mdatp as its own child. Elsewhere, ask mdatp directly.
|
||||||
|
health="$(systemd-run --quiet --wait --pipe --collect "${MDATP}" health 2>/dev/null)"
|
||||||
|
grep -q '^healthy' <<< "${health}" || health="$("${MDATP}" health 2>/dev/null)"
|
||||||
|
|
||||||
|
# Print one field of the `mdatp health` output ("name : value") without
|
||||||
|
# quotes or a trailing " [managed]".
|
||||||
field() {
|
field() {
|
||||||
"${MDATP}" health --field "$1" 2>/dev/null | 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() {
|
||||||
@@ -28,6 +34,8 @@ healthy=false
|
|||||||
licensed=false
|
licensed=false
|
||||||
onboarded=false
|
onboarded=false
|
||||||
rtp=false
|
rtp=false
|
||||||
|
behavior_monitoring=false
|
||||||
|
cloud_protection=false
|
||||||
definitions_ok=false
|
definitions_ok=false
|
||||||
definitions_age=-1
|
definitions_age=-1
|
||||||
version=""
|
version=""
|
||||||
@@ -40,6 +48,8 @@ if [[ -x "${MDATP}" ]]; then
|
|||||||
healthy="$(bool "$(field healthy)")"
|
healthy="$(bool "$(field healthy)")"
|
||||||
licensed="$(bool "$(field licensed)")"
|
licensed="$(bool "$(field licensed)")"
|
||||||
rtp="$(bool "$(field real_time_protection_enabled)")"
|
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
|
[[ -n "$(field org_id)" ]] && onboarded=true
|
||||||
[[ "$(field definitions_status)" == up_to_date ]] && definitions_ok=true
|
[[ "$(field definitions_status)" == up_to_date ]] && definitions_ok=true
|
||||||
age="$(field definitions_updated_minutes_ago)"
|
age="$(field definitions_updated_minutes_ago)"
|
||||||
@@ -57,6 +67,8 @@ cat <<EOF
|
|||||||
"DefenderLicensed": ${licensed},
|
"DefenderLicensed": ${licensed},
|
||||||
"DefenderOnboarded": ${onboarded},
|
"DefenderOnboarded": ${onboarded},
|
||||||
"DefenderRealTimeProtection": ${rtp},
|
"DefenderRealTimeProtection": ${rtp},
|
||||||
|
"DefenderBehaviorMonitoring": ${behavior_monitoring},
|
||||||
|
"DefenderCloudProtection": ${cloud_protection},
|
||||||
"DefenderDefinitionsUpToDate": ${definitions_ok},
|
"DefenderDefinitionsUpToDate": ${definitions_ok},
|
||||||
"DefenderDefinitionsAgeDays": ${definitions_age},
|
"DefenderDefinitionsAgeDays": ${definitions_age},
|
||||||
"DefenderVersion": "${version}"
|
"DefenderVersion": "${version}"
|
||||||
|
|||||||
@@ -71,8 +71,16 @@ if ! wait_for_daemon; then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ -z "$(field org_id)" ]]; then
|
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."
|
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
|
else
|
||||||
log "Defender is not onboarded; writing ${ONBOARD_FILE}."
|
log "Defender is not onboarded; writing ${ONBOARD_FILE}."
|
||||||
install -d -m 0755 "$(dirname "${ONBOARD_FILE}")"
|
install -d -m 0755 "$(dirname "${ONBOARD_FILE}")"
|
||||||
|
|||||||
Reference in new issue
Block a user