Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fadf549b5d |
No files matched your search
@@ -2,12 +2,6 @@ name: Build containers
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
|
||||||
image:
|
|
||||||
description: Image to build
|
|
||||||
type: choice
|
|
||||||
options: [all, fedora-cosmic, fedora-remote, fedora-intune]
|
|
||||||
default: all
|
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: ["main"]
|
branches: ["main"]
|
||||||
push:
|
push:
|
||||||
@@ -21,17 +15,11 @@ jobs:
|
|||||||
changes:
|
changes:
|
||||||
name: Compute changes
|
name: Compute changes
|
||||||
runs-on: job-v2
|
runs-on: job-v2
|
||||||
# The runner has no git: without it actions/checkout falls back to a
|
|
||||||
# tarball with no history, `git diff` fails and every image is rebuilt.
|
|
||||||
container:
|
|
||||||
image: "quay.io/fedora-ostree-desktops/buildroot:44"
|
|
||||||
outputs:
|
outputs:
|
||||||
cosmic: ${{ steps.changes.outputs.cosmic }}
|
cosmic: ${{ steps.changes.outputs.cosmic }}
|
||||||
remote: ${{ steps.changes.outputs.remote }}
|
remote: ${{ steps.changes.outputs.remote }}
|
||||||
intune: ${{ steps.changes.outputs.intune }}
|
intune: ${{ steps.changes.outputs.intune }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install tools
|
|
||||||
run: dnf install -y nodejs git
|
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -39,53 +27,17 @@ jobs:
|
|||||||
- name: Detect changed images
|
- name: Detect changed images
|
||||||
id: changes
|
id: changes
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
|
||||||
EVENT: ${{ github.event_name }}
|
|
||||||
BEFORE: ${{ github.event.before }}
|
|
||||||
SHA: ${{ github.sha }}
|
|
||||||
BASE_REF: ${{ github.base_ref }}
|
|
||||||
DISPATCH_IMAGE: ${{ inputs.image }}
|
|
||||||
run: |
|
run: |
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
# Diff the event's commits; any failure rebuilds everything, but
|
if [[ "${{ github.event_name }}" == "push" \
|
||||||
# say so instead of hiding it.
|
&& -n "${{ github.event.before }}" \
|
||||||
diff_or_all() {
|
&& "${{ github.event.before }}" != "0000000000000000000000000000000000000000" ]]; then
|
||||||
if ! changed="$(git diff --name-only "$@")"; then
|
changed="$(git diff --name-only "${{ github.event.before }}" "${{ github.sha }}" || true)"
|
||||||
echo "::warning::git diff $* failed; rebuilding every image"
|
elif [[ "${{ github.event_name }}" == "pull_request" ]]; then
|
||||||
changed=""
|
changed="$(git diff --name-only "origin/${{ github.base_ref }}...HEAD" || true)"
|
||||||
elif [[ -z "${changed}" ]]; then
|
else
|
||||||
# Nothing changed (e.g. an empty merge): build nothing rather
|
changed=""
|
||||||
# than everything.
|
fi
|
||||||
changed="(none)"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
case "${EVENT}" in
|
|
||||||
push)
|
|
||||||
if [[ -n "${BEFORE}" && "${BEFORE}" != 0000000000000000000000000000000000000000 ]]; then
|
|
||||||
diff_or_all "${BEFORE}" "${SHA}"
|
|
||||||
else
|
|
||||||
echo "::warning::push without a previous commit; rebuilding every image"
|
|
||||||
changed=""
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
pull_request)
|
|
||||||
diff_or_all "origin/${BASE_REF}...HEAD"
|
|
||||||
;;
|
|
||||||
workflow_dispatch)
|
|
||||||
# Pretend the chosen image's directory changed; "all" (or no
|
|
||||||
# input) rebuilds everything.
|
|
||||||
if [[ -n "${DISPATCH_IMAGE}" && "${DISPATCH_IMAGE}" != all ]]; then
|
|
||||||
changed="images/${DISPATCH_IMAGE}/"
|
|
||||||
else
|
|
||||||
changed=""
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
# schedule: the weekly rebuild picks up package updates for all.
|
|
||||||
changed=""
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
printf 'Changed paths:\n%s\n' "${changed:-(all)}"
|
|
||||||
eval "$(printf '%s\n' "${changed}" | ./ci-changes.sh)"
|
eval "$(printf '%s\n' "${changed}" | ./ci-changes.sh)"
|
||||||
echo "cosmic=${cosmic}" >> "$GITHUB_OUTPUT"
|
echo "cosmic=${cosmic}" >> "$GITHUB_OUTPUT"
|
||||||
echo "remote=${remote}" >> "$GITHUB_OUTPUT"
|
echo "remote=${remote}" >> "$GITHUB_OUTPUT"
|
||||||
@@ -152,8 +104,6 @@ jobs:
|
|||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Log in to registry
|
- name: Log in to registry
|
||||||
# Only the push step needs it, and PR builds get no secrets.
|
|
||||||
if: github.event_name != 'pull_request'
|
|
||||||
env:
|
env:
|
||||||
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
|
||||||
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
REGISTRY_TOKEN: ${{ secrets.REGISTRY_TOKEN }}
|
||||||
|
|||||||
+2
-3
@@ -1,8 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# Given changed paths on stdin, print `cosmic=<bool>`, `remote=<bool>` and
|
# Given changed paths on stdin, print `cosmic=<bool>`, `remote=<bool>` and
|
||||||
# `intune=<bool>` so the workflow can skip images that didn't change. Shared
|
# `intune=<bool>` so the workflow can skip images that didn't change. Shared files or build tooling
|
||||||
# files or build tooling changes rebuild everything; empty input
|
# changes rebuild everything; empty input (schedule/manual) rebuilds everything.
|
||||||
# (schedule/manual) rebuilds everything.
|
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
changed="$(cat || true)"
|
changed="$(cat || true)"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ BUILD_MODE=upstream
|
|||||||
|
|
||||||
UPSTREAM_REPO="https://gitlab.com/fedora/ostree/ci-test.git"
|
UPSTREAM_REPO="https://gitlab.com/fedora/ostree/ci-test.git"
|
||||||
|
|
||||||
# Stable only; Microsoft's Defender repo (fedora/44) has no rawhide build.
|
# Stable only; Microsoft builds the Intune app against RHEL, not rawhide.
|
||||||
declare -A UPSTREAM_REFS=(
|
declare -A UPSTREAM_REFS=(
|
||||||
[44]=f44
|
[44]=f44
|
||||||
)
|
)
|
||||||
@@ -45,7 +45,7 @@ exclude-packages:
|
|||||||
add-files:
|
add-files:
|
||||||
- ["50-workstation.preset", "/usr/lib/systemd/system-preset/50-workstation.preset"]
|
- ["50-workstation.preset", "/usr/lib/systemd/system-preset/50-workstation.preset"]
|
||||||
# Key-only SSH for local accounts.
|
# Key-only SSH for local accounts.
|
||||||
- ["10-workstation-sshd.conf", "/etc/ssh/sshd_config.d/10-workstation-sshd.conf"]
|
- ["10-intune-sshd.conf", "/etc/ssh/sshd_config.d/10-intune-sshd.conf"]
|
||||||
|
|
||||||
postprocess:
|
postprocess:
|
||||||
# uresourced breaks the GDM greeter's user manager; see fedora-remote.
|
# uresourced breaks the GDM greeter's user manager; see fedora-remote.
|
||||||
|
|||||||
@@ -1,60 +1,39 @@
|
|||||||
# Microsoft Defender for Endpoint on Linux (mdatp), Fedora 44 build, from the
|
# Microsoft Defender for Endpoint on Linux (mdatp), RHEL 10 build.
|
||||||
# microsoft-fedora44 repo (repos/microsoft.repo).
|
|
||||||
#
|
#
|
||||||
# mdatp's %post does the real install: units, SELinux module, and per-machine
|
# mdatp's %post does the real install: it writes mdatp.service and the
|
||||||
# state under /var/opt/microsoft/mdatp (machine GUID, definitions database,
|
# mde_netfilter_v2 units, loads its SELinux module and creates a machine GUID
|
||||||
# logs). In a compose /var is read-only, and that state must not be shared
|
# and state under /var/opt/microsoft/mdatp. In a compose the daemon cannot
|
||||||
# between machines anyway, so the %post is saved into the image and run once on
|
# start and /var is not shipped, so the units and SELinux module are handled
|
||||||
# first boot by mdatp-firstboot.service. mdatp.service waits for it.
|
# here and the per-machine state is left to the first start on the device.
|
||||||
#
|
#
|
||||||
# Onboarding is per tenant and not part of the image: run the onboarding script
|
# Onboarding is per tenant and not part of the image: run the onboarding script
|
||||||
# from the Defender portal on the device.
|
# from the Defender portal on the device (see the Intune test doc).
|
||||||
|
|
||||||
repo-packages:
|
repo-packages:
|
||||||
- repo: microsoft-fedora44
|
- repo: microsoft-rhel10
|
||||||
packages:
|
packages:
|
||||||
- mdatp
|
- mdatp
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
# The %post is a bash script; Defender's inventory scripts and the
|
# Used by the Defender TVM/inventory scripts and the onboarding script.
|
||||||
# onboarding script are Python.
|
|
||||||
- bash
|
|
||||||
- python3
|
- python3
|
||||||
|
|
||||||
add-files:
|
add-files:
|
||||||
- ["mdatp-firstboot", "/usr/libexec/mdatp-firstboot"]
|
|
||||||
- ["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"]
|
- ["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:
|
||||||
- |
|
- |
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -xeuo pipefail
|
set -xeuo pipefail
|
||||||
# The %post wrote the units during the compose; fail the build if a newer
|
conf=/opt/microsoft/mdatp/conf
|
||||||
# mdatp stops doing that, rather than shipping a Defender that never starts.
|
# Units the %post copies into place; install them if it did not get there.
|
||||||
for unit in mdatp.service mde_netfilter_v2.service mde_netfilter_v2.socket; do
|
for unit in mdatp.service mde_netfilter_v2.service mde_netfilter_v2.socket; do
|
||||||
test -f "/usr/lib/systemd/system/${unit}"
|
if [[ ! -f "/usr/lib/systemd/system/${unit}" ]]; then
|
||||||
|
install -m 0644 "${conf}/${unit}" "/usr/lib/systemd/system/${unit}"
|
||||||
|
fi
|
||||||
done
|
done
|
||||||
# rpm-ostree keeps /opt content in /usr/lib/opt until the commit.
|
semodule -n -i "${conf}/selinux_policies/out/mdatp_service.pp"
|
||||||
for root in /usr/lib/opt /opt; do
|
# Never ship a machine GUID or onboarding state baked in at build time.
|
||||||
pp="${root}/microsoft/mdatp/conf/selinux_policies/out/mdatp_service.pp"
|
ls -laR /etc/opt/microsoft /var/opt/microsoft 2>/dev/null || true
|
||||||
[[ -f "${pp}" ]] && break
|
rm -rf /var/opt/microsoft/mdatp
|
||||||
done
|
rm -f /etc/opt/microsoft/mdatp/mdatp_onboard.json
|
||||||
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
|
|
||||||
chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Report the full image version (VERSION_ID=44.20261019.0) to Entra ID and
|
|
||||||
# Intune instead of "44"; see /usr/libexec/himmelblau-os-release. Only this
|
|
||||||
# service's mount namespace sees the replacement os-release.
|
|
||||||
[Unit]
|
|
||||||
Requires=himmelblau-os-release.service
|
|
||||||
After=himmelblau-os-release.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
BindReadOnlyPaths=/run/himmelblau-os-release:/etc/os-release
|
|
||||||
File renamed without changes.
@@ -1,4 +1,3 @@
|
|||||||
# Defender: first-boot setup, the daemon and its network filter.
|
# Defender daemon and its network filter (socket-activated).
|
||||||
enable mdatp-firstboot.service
|
|
||||||
enable mdatp.service
|
enable mdatp.service
|
||||||
enable mde_netfilter_v2.socket
|
enable mde_netfilter_v2.socket
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Intune compliance check at login and on a timer, for every user.
|
|
||||||
enable himmelblau-compliance-check.service
|
|
||||||
enable himmelblau-compliance-check.timer
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# Himmelblau daemons; they pull in their sockets with Requires=.
|
|
||||||
enable himmelblaud.service
|
|
||||||
enable himmelblaud-tasks.service
|
|
||||||
enable himmelblau-hsm-pin-init.service
|
|
||||||
# Runs the Intune custom configuration scripts Himmelblau puts in /etc/cron.d.
|
|
||||||
enable crond.service
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Intune daemon (socket-activated) and the device identity broker.
|
||||||
|
enable intune-daemon.socket
|
||||||
|
enable microsoft-identity-device-broker.service
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"ExtensionInstallForcelist": [
|
|
||||||
"jlnfnnolkbjieggibinobhkjdfbpcohn;https://clients2.google.com/service/update2/crx"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "linux_entra_sso",
|
|
||||||
"description": "Entra ID SSO via Microsoft Identity Broker",
|
|
||||||
"path": "/usr/bin/linux-entra-sso",
|
|
||||||
"type": "stdio",
|
|
||||||
"allowed_origins": [
|
|
||||||
"chrome-extension://jlnfnnolkbjieggibinobhkjdfbpcohn/"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Write the os-release that the Himmelblau daemons see (10-os-release.conf):
|
|
||||||
# the image's /usr/lib/os-release with VERSION_ID set to the full image version
|
|
||||||
# (OSTREE_VERSION, e.g. 44.20261019.0), so Intune shows the build and not just
|
|
||||||
# "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
|
|
||||||
# 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
|
|
||||||
|
|
||||||
SRC=/usr/lib/os-release
|
|
||||||
DEST=/run/himmelblau-os-release
|
|
||||||
|
|
||||||
version="$(. "${SRC}" && echo "${OSTREE_VERSION:-${IMAGE_VERSION:-}}")"
|
|
||||||
name="$(. "${SRC}" && echo "${NAME:-Linux}")"
|
|
||||||
|
|
||||||
tmp="$(mktemp "${DEST}.XXXXXX")"
|
|
||||||
if [[ -n "${version}" ]]; then
|
|
||||||
sed -e "s/^VERSION_ID=.*/VERSION_ID=${version}/" \
|
|
||||||
-e "s/^PRETTY_NAME=.*/PRETTY_NAME=\"${name}\"/" "${SRC}" > "${tmp}"
|
|
||||||
else
|
|
||||||
# No image version (not an ostree/bootc deployment): pass it through as-is.
|
|
||||||
cat "${SRC}" > "${tmp}"
|
|
||||||
fi
|
|
||||||
chmod 0644 "${tmp}"
|
|
||||||
mv -f "${tmp}" "${DEST}"
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Generate the os-release reported by Himmelblau to Intune
|
|
||||||
DefaultDependencies=no
|
|
||||||
After=local-fs.target
|
|
||||||
Before=himmelblaud.service himmelblaud-tasks.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/libexec/himmelblau-os-release
|
|
||||||
RemainAfterExit=yes
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
# Himmelblau configuration. See himmelblau.conf(5) and
|
|
||||||
# https://himmelblau-idm.org/docs/reference/himmelblau-conf/
|
|
||||||
#
|
|
||||||
# No tenant settings are needed: `domain` defaults to the domain of the first
|
|
||||||
# user who signs in, and that user joins the device to Entra ID. Restrict who
|
|
||||||
# may sign in with pam_allow_groups (group object IDs or UPNs).
|
|
||||||
|
|
||||||
[global]
|
|
||||||
# Enroll the device in Intune and apply/report compliance policies.
|
|
||||||
apply_policy = true
|
|
||||||
# Fedora's admin group; members of sudo_groups (Entra group object IDs) are
|
|
||||||
# added to it.
|
|
||||||
local_sudo_group = wheel
|
|
||||||
# Linux admins: members get wheel (sudo) at sign-in, and lose it again within
|
|
||||||
# local_groups_reconcile_interval after leaving the group.
|
|
||||||
sudo_groups = c3b1cfcc-7834-47eb-948b-a22204276453
|
|
||||||
# Home directories named after the account (e.g. /home/jdoe), with a UPN alias.
|
|
||||||
home_attr = CN
|
|
||||||
home_alias = SPN
|
|
||||||
use_etc_skel = true
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
# Finish the Defender (mdatp) install on first boot by running the package's
|
|
||||||
# own %post, saved at build time: it creates the machine GUID, state, logs and
|
|
||||||
# definitions database under /var and then enables and starts mdatp.service.
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
MARKER=/var/lib/mdatp-firstboot.done
|
|
||||||
|
|
||||||
[[ -f "${MARKER}" ]] && exit 0
|
|
||||||
|
|
||||||
# mdatp.service only starts once the marker exists (10-firstboot.conf), and the
|
|
||||||
# %post starts it, so create the marker first and drop it again on failure.
|
|
||||||
touch "${MARKER}"
|
|
||||||
if ! bash /usr/libexec/mdatp-postinst 1; then
|
|
||||||
rm -f "${MARKER}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Don't start Defender before mdatp-firstboot.service has created its state.
|
|
||||||
[Unit]
|
|
||||||
ConditionPathExists=/var/lib/mdatp-firstboot.done
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
[Unit]
|
|
||||||
Description=Finish the Microsoft Defender for Endpoint install on first boot
|
|
||||||
Documentation=https://learn.microsoft.com/en-us/defender-endpoint/microsoft-defender-endpoint-linux
|
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
ConditionPathExists=!/var/lib/mdatp-firstboot.done
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
Type=oneshot
|
|
||||||
ExecStart=/usr/libexec/mdatp-firstboot
|
|
||||||
RemainAfterExit=yes
|
|
||||||
TimeoutStartSec=15min
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# 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
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Himmelblau: Entra ID sign-in (PAM/NSS), Entra join, Hello PIN, Intune
|
|
||||||
# enrollment and compliance, and browser SSO.
|
|
||||||
|
|
||||||
repo-packages:
|
|
||||||
- repo: himmelblau
|
|
||||||
packages:
|
|
||||||
- himmelblau
|
|
||||||
- pam-himmelblau
|
|
||||||
- nss-himmelblau
|
|
||||||
- himmelblau-selinux
|
|
||||||
# Shows a QR code on the login screen for the MFA/device-code prompts.
|
|
||||||
- himmelblau-qr-greeter
|
|
||||||
# Entra SSO in Edge (native messaging host) via the Himmelblau broker.
|
|
||||||
- himmelblau-broker
|
|
||||||
- himmelblau-sso
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- authselect
|
|
||||||
- nss-altfiles
|
|
||||||
# himmelblau-selinux compiles its policy module at install time.
|
|
||||||
- make
|
|
||||||
- selinux-policy-devel
|
|
||||||
# Intune custom configuration scripts are installed as /etc/cron.d jobs.
|
|
||||||
- cronie
|
|
||||||
|
|
||||||
add-files:
|
|
||||||
- ["himmelblau.conf", "/etc/himmelblau/himmelblau.conf"]
|
|
||||||
- ["50-himmelblau.preset", "/usr/lib/systemd/system-preset/50-himmelblau.preset"]
|
|
||||||
- ["50-himmelblau-user.preset", "/usr/lib/systemd/user-preset/50-himmelblau-user.preset"]
|
|
||||||
# Report the full image version (44.20261019.0) as the OS version in Intune.
|
|
||||||
- ["himmelblau-os-release", "/usr/libexec/himmelblau-os-release"]
|
|
||||||
- ["himmelblau-os-release.service", "/usr/lib/systemd/system/himmelblau-os-release.service"]
|
|
||||||
- ["10-himmelblau-os-release.conf", "/usr/lib/systemd/system/himmelblaud.service.d/10-os-release.conf"]
|
|
||||||
- ["10-himmelblau-os-release.conf", "/usr/lib/systemd/system/himmelblaud-tasks.service.d/10-os-release.conf"]
|
|
||||||
|
|
||||||
postprocess:
|
|
||||||
# The package's %post selects its authselect vendor profile but drops
|
|
||||||
# with-altfiles; without it system users (gdm, pipewire, mdatp, ...) in
|
|
||||||
# /usr/lib/passwd do not resolve and the login screen never starts.
|
|
||||||
- |
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -xeuo pipefail
|
|
||||||
authselect select himmelblau with-altfiles with-pam-gnome-keyring --force --nobackup
|
|
||||||
for user in gdm mdatp; do
|
|
||||||
getent passwd "${user}" >/dev/null
|
|
||||||
done
|
|
||||||
grep -q pam_himmelblau.so /etc/pam.d/system-auth
|
|
||||||
chmod 0755 /usr/libexec/himmelblau-os-release
|
|
||||||
test -d /etc/cron.d
|
|
||||||
|
|
||||||
# himmelblau-selinux builds and loads its module in %posttrans, which does
|
|
||||||
# nothing in a compose; do it here.
|
|
||||||
- |
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -xeuo pipefail
|
|
||||||
tmp="$(mktemp -d)"
|
|
||||||
cp /usr/share/selinux/packages/himmelblaud/himmelblaud.{te,if,fc} "${tmp}/"
|
|
||||||
make -C "${tmp}" -f /usr/share/selinux/devel/Makefile himmelblaud.pp
|
|
||||||
semodule -n -i "${tmp}/himmelblaud.pp"
|
|
||||||
rm -rf "${tmp}"
|
|
||||||
|
|
||||||
# Show the QR greeter on the GDM login screen, not only in user sessions.
|
|
||||||
- |
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -xeuo pipefail
|
|
||||||
install -d /etc/dconf/db/gdm.d
|
|
||||||
cat > /etc/dconf/db/gdm.d/01-himmelblau-qr-greeter <<'CONF'
|
|
||||||
[org/gnome/shell]
|
|
||||||
enabled-extensions=['qr-greeter@himmelblau-idm.org']
|
|
||||||
CONF
|
|
||||||
dconf update
|
|
||||||
|
|
||||||
# Compliance check (user timer) and broker for every user.
|
|
||||||
- |
|
|
||||||
#!/usr/bin/env bash
|
|
||||||
set -xeuo pipefail
|
|
||||||
systemctl --user --global preset-all
|
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# Microsoft Intune app for Linux (intune-portal), the Microsoft identity broker
|
||||||
|
# and Microsoft Edge (needed for Conditional Access to Microsoft 365 web apps).
|
||||||
|
#
|
||||||
|
# Fedora is not a supported Intune distribution. The RHEL 10 builds are used
|
||||||
|
# because they link against the same library generation as Fedora 44. The
|
||||||
|
# repos are limited to these packages with includepkgs (repos/microsoft.repo),
|
||||||
|
# so nothing else from the RHEL repo can replace a Fedora package.
|
||||||
|
|
||||||
|
repos:
|
||||||
|
- microsoft-rhel10
|
||||||
|
- microsoft-edge
|
||||||
|
|
||||||
|
repo-packages:
|
||||||
|
- repo: microsoft-rhel10
|
||||||
|
packages:
|
||||||
|
- intune-portal
|
||||||
|
- microsoft-identity-broker
|
||||||
|
- repo: microsoft-edge
|
||||||
|
packages:
|
||||||
|
- microsoft-edge-stable
|
||||||
|
|
||||||
|
packages:
|
||||||
|
# intune-portal scriptlet dependencies (authselect profile + SELinux module)
|
||||||
|
# and the sssd base its authselect profile is derived from.
|
||||||
|
- authselect
|
||||||
|
- checkpolicy
|
||||||
|
- policycoreutils
|
||||||
|
- sssd-client
|
||||||
|
# Secret storage for the identity broker tokens.
|
||||||
|
- gnome-keyring
|
||||||
|
- libsecret
|
||||||
|
|
||||||
|
add-files:
|
||||||
|
- ["50-intune.preset", "/usr/lib/systemd/system-preset/50-intune.preset"]
|
||||||
|
|
||||||
|
postprocess:
|
||||||
|
# The intune-portal %post builds an authselect profile with pam_intune.so and
|
||||||
|
# an SELinux module, but neither reliably survives a compose (no running
|
||||||
|
# SELinux, authselect state under /etc). Redo both here so the image is
|
||||||
|
# deterministic either way.
|
||||||
|
- |
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -xeuo pipefail
|
||||||
|
profile=/etc/authselect/custom/intune-profile
|
||||||
|
if [[ ! -d "${profile}" ]]; then
|
||||||
|
authselect create-profile intune-profile -b sssd --symlink-meta --symlink-pam
|
||||||
|
fi
|
||||||
|
for f in system-auth password-auth; do
|
||||||
|
grep -q pam_intune.so "${profile}/${f}" && continue
|
||||||
|
sed -i \
|
||||||
|
-e 's/^\(auth[[:space:]]\+\)sufficient\([[:space:]]\+pam_unix.so\)/\1required \2/' \
|
||||||
|
-e 's/^\(password[[:space:]]\+\)sufficient\([[:space:]]\+pam_unix.so\)/\1required \2/' \
|
||||||
|
"${profile}/${f}"
|
||||||
|
sed -i \
|
||||||
|
-e '/^auth[[:space:]]\+required[[:space:]]\+pam_unix.so/a auth sufficient pam_intune.so' \
|
||||||
|
-e '/^password[[:space:]]\+required[[:space:]]\+pam_unix.so/a password sufficient pam_intune.so' \
|
||||||
|
-e '/^session[[:space:]]\+required[[:space:]]\+pam_unix.so/a session optional pam_intune.so' \
|
||||||
|
"${profile}/${f}"
|
||||||
|
done
|
||||||
|
authselect select custom/intune-profile --force --nobackup
|
||||||
|
|
||||||
|
- |
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -xeuo pipefail
|
||||||
|
cd /usr/share/selinux/packages/intune
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
checkmodule -M -m -o "${tmp}/intune.mod" intune.te
|
||||||
|
semodule_package -o "${tmp}/intune.pp" -m "${tmp}/intune.mod"
|
||||||
|
semodule -n -i "${tmp}/intune.pp"
|
||||||
|
rm -rf "${tmp}"
|
||||||
|
|
||||||
|
# intune-agent (user timer) reports compliance; enable it for every user.
|
||||||
|
- |
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -xeuo pipefail
|
||||||
|
systemctl --user --global preset-all
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
{
|
|
||||||
"Rules": [
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderInstalled",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender is not installed.",
|
|
||||||
"Description": "Microsoft Defender for Endpoint (mdatp) must be installed on this device."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderRunning",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender is not running.",
|
|
||||||
"Description": "The Microsoft Defender service (mdatp.service) is not responding. Restart the device or contact IT."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderOnboarded",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender is not onboarded.",
|
|
||||||
"Description": "This device is not onboarded to your organization's Microsoft Defender tenant. Contact IT."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderLicensed",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender is not licensed.",
|
|
||||||
"Description": "Microsoft Defender has no valid license on this device. Contact IT."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderHealthy",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender reports a problem.",
|
|
||||||
"Description": "Run 'mdatp health' to see the reported issues, or contact IT."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"SettingName": "DefenderRealTimeProtection",
|
|
||||||
"Operator": "IsEquals",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Real-time protection is off.",
|
|
||||||
"Description": "Microsoft Defender real-time protection must be enabled."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"DataType": "Boolean",
|
|
||||||
"Operand": true,
|
|
||||||
"MoreInfoUrl": "https://learn.microsoft.com/en-us/defender-endpoint/linux-install-manually",
|
|
||||||
"RemediationStrings": [
|
|
||||||
{
|
|
||||||
"Language": "en_US",
|
|
||||||
"Title": "Microsoft Defender definitions are out of date.",
|
|
||||||
"Description": "Connect the device to the network so Microsoft Defender can update its security intelligence."
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Intune custom compliance discovery script for Linux: is Microsoft Defender
|
|
||||||
# for Endpoint (mdatp) installed, onboarded, licensed and protecting?
|
|
||||||
#
|
|
||||||
# Upload in Intune > Endpoint security > Device compliance > Scripts (Linux),
|
|
||||||
# and pair it with defender-compliance.json in a Linux compliance policy
|
|
||||||
# (Custom compliance). Himmelblau runs it as root inside
|
|
||||||
# himmelblaud-tasks.service and sends the JSON on stdout to Intune, which
|
|
||||||
# evaluates it against the rules. Every setting is always emitted, so a missing
|
|
||||||
# or stopped Defender reads as non-compliant instead of as a script error.
|
|
||||||
|
|
||||||
MDATP=/usr/bin/mdatp
|
|
||||||
|
|
||||||
# Defender won't answer `mdatp health` started from inside himmelblaud-tasks
|
|
||||||
# (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() {
|
|
||||||
grep -m1 "^$1 *:" <<< "${health}" \
|
|
||||||
| sed -e 's/^[^:]*: *//' -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//'
|
|
||||||
}
|
|
||||||
|
|
||||||
bool() {
|
|
||||||
[[ "$1" == true ]] && echo true || echo false
|
|
||||||
}
|
|
||||||
|
|
||||||
installed=false
|
|
||||||
running=false
|
|
||||||
healthy=false
|
|
||||||
licensed=false
|
|
||||||
onboarded=false
|
|
||||||
rtp=false
|
|
||||||
behavior_monitoring=false
|
|
||||||
cloud_protection=false
|
|
||||||
definitions_ok=false
|
|
||||||
definitions_age=-1
|
|
||||||
version=""
|
|
||||||
|
|
||||||
if [[ -x "${MDATP}" ]]; then
|
|
||||||
installed=true
|
|
||||||
if [[ "$(field healthy)" =~ ^(true|false)$ ]]; then
|
|
||||||
# The daemon answered.
|
|
||||||
running=true
|
|
||||||
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)"
|
|
||||||
[[ "${age}" =~ ^[0-9]+$ ]] && definitions_age=$(( age / 1440 ))
|
|
||||||
version="$(field app_version)"
|
|
||||||
version="${version//[^0-9A-Za-z._-]/}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
cat <<EOF
|
|
||||||
{
|
|
||||||
"DefenderInstalled": ${installed},
|
|
||||||
"DefenderRunning": ${running},
|
|
||||||
"DefenderHealthy": ${healthy},
|
|
||||||
"DefenderLicensed": ${licensed},
|
|
||||||
"DefenderOnboarded": ${onboarded},
|
|
||||||
"DefenderRealTimeProtection": ${rtp},
|
|
||||||
"DefenderBehaviorMonitoring": ${behavior_monitoring},
|
|
||||||
"DefenderCloudProtection": ${cloud_protection},
|
|
||||||
"DefenderDefinitionsUpToDate": ${definitions_ok},
|
|
||||||
"DefenderDefinitionsAgeDays": ${definitions_age},
|
|
||||||
"DefenderVersion": "${version}"
|
|
||||||
}
|
|
||||||
EOF
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Intune custom configuration script for Linux: bring Microsoft Defender for
|
|
||||||
# Endpoint (mdatp) back to the state defender-compliance.sh checks for.
|
|
||||||
#
|
|
||||||
# Intune has no "run only when non-compliant" trigger for Linux, so this runs
|
|
||||||
# on the policy's schedule and only acts on what is wrong; on a healthy device
|
|
||||||
# it only reads `mdatp health`. Himmelblau installs it as an /etc/cron.d job.
|
|
||||||
#
|
|
||||||
# Intune > Devices > Configuration > Create > Linux > Settings catalog >
|
|
||||||
# Custom Configuration:
|
|
||||||
# Execution context: root
|
|
||||||
# Execution frequency: every 1 hour (or 15 minutes while rolling out)
|
|
||||||
# Execution retries: 1
|
|
||||||
# Script: this file, with ONBOARDING_JSON filled in
|
|
||||||
#
|
|
||||||
# Exit status is non-zero when Defender is still not healthy afterwards.
|
|
||||||
|
|
||||||
# Contents of mdatp_onboard.json from the Defender portal: Settings > Endpoints
|
|
||||||
# > Onboarding > Linux Server > "Local Script (Python)". The downloaded
|
|
||||||
# MicrosoftDefenderATPOnboardingLinuxServer.py writes this file to
|
|
||||||
# /etc/opt/microsoft/mdatp/ on the machine it runs on; paste that file's
|
|
||||||
# contents here. Leave the placeholder to skip onboarding.
|
|
||||||
ONBOARDING_JSON=$(cat <<'JSON'
|
|
||||||
REPLACE_WITH_MDATP_ONBOARD_JSON
|
|
||||||
JSON
|
|
||||||
)
|
|
||||||
|
|
||||||
MDATP=/usr/bin/mdatp
|
|
||||||
ONBOARD_FILE=/etc/opt/microsoft/mdatp/mdatp_onboard.json
|
|
||||||
|
|
||||||
log() {
|
|
||||||
logger -t defender-remediation -- "$*"
|
|
||||||
echo "$*"
|
|
||||||
}
|
|
||||||
|
|
||||||
# Print one `mdatp health` field without quotes, a trailing " [managed]" or
|
|
||||||
# the "ATTENTION: ..." lines mdatp prints while unlicensed.
|
|
||||||
field() {
|
|
||||||
"${MDATP}" health --field "$1" 2>/dev/null | grep -v '^ATTENTION' | tail -n1 \
|
|
||||||
| sed -e 's/ \[managed\]$//' -e 's/^"//' -e 's/"$//'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Wait up to 2 minutes for the daemon to answer.
|
|
||||||
wait_for_daemon() {
|
|
||||||
local i
|
|
||||||
for i in $(seq 1 24); do
|
|
||||||
[[ "$(field healthy)" =~ ^(true|false)$ ]] && return 0
|
|
||||||
sleep 5
|
|
||||||
done
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if [[ ! -x "${MDATP}" ]]; then
|
|
||||||
log "mdatp is not installed; it ships in the image, so update or rebase the device."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# The image finishes the Defender install on first boot (mdatp-firstboot).
|
|
||||||
if [[ ! -f /var/lib/mdatp-firstboot.done ]]; then
|
|
||||||
log "Defender first-boot setup has not completed; starting it."
|
|
||||||
systemctl start mdatp-firstboot.service
|
|
||||||
fi
|
|
||||||
|
|
||||||
if ! systemctl is-active --quiet mdatp.service; then
|
|
||||||
log "mdatp.service is not running; restarting it."
|
|
||||||
systemctl restart mdatp.service
|
|
||||||
fi
|
|
||||||
if ! wait_for_daemon; then
|
|
||||||
log "The Defender daemon does not respond."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ -z "$(field org_id)" ]]; 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}")"
|
|
||||||
printf '%s\n' "${ONBOARDING_JSON}" > "${ONBOARD_FILE}.tmp"
|
|
||||||
chown root:mdatp "${ONBOARD_FILE}.tmp"
|
|
||||||
chmod 0600 "${ONBOARD_FILE}.tmp"
|
|
||||||
mv -f "${ONBOARD_FILE}.tmp" "${ONBOARD_FILE}"
|
|
||||||
systemctl restart mdatp.service
|
|
||||||
wait_for_daemon || { log "The Defender daemon does not respond after onboarding."; exit 1; }
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Settings Defender's own policy manages show " [managed]" and can't be
|
|
||||||
# changed locally; this only fixes a locally disabled real-time protection.
|
|
||||||
if [[ "$(field real_time_protection_enabled)" != true ]]; then
|
|
||||||
log "Real-time protection is off; enabling it."
|
|
||||||
"${MDATP}" config real-time-protection --value enabled
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [[ "$(field definitions_status)" != up_to_date ]]; then
|
|
||||||
log "Definitions are not up to date; updating them."
|
|
||||||
"${MDATP}" definitions update
|
|
||||||
fi
|
|
||||||
|
|
||||||
healthy="$(field healthy)"
|
|
||||||
log "healthy=${healthy} licensed=$(field licensed) org_id=$(field org_id) rtp=$(field real_time_protection_enabled) definitions=$(field definitions_status)"
|
|
||||||
[[ "${healthy}" == true ]]
|
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Minimal GNOME workstation where users sign in with their Entra ID account
|
# Minimal GNOME workstation with the Microsoft Intune app, Microsoft Edge and
|
||||||
# (Himmelblau), with Microsoft Edge and Microsoft Defender for Endpoint.
|
# Microsoft Defender for Endpoint, for testing Intune enrollment and Linux
|
||||||
# Himmelblau joins the device to Entra ID at the first sign-in and, with
|
# compliance policies. Local accounts only: no Kanidm, because the Intune app
|
||||||
# apply_policy, enrolls it in Intune and reports compliance.
|
# needs its own authselect profile (pam_intune) for the password checks.
|
||||||
|
|
||||||
metadata:
|
metadata:
|
||||||
summary: Fedora minimal GNOME workstation with Entra ID sign-in and Intune
|
summary: Fedora minimal GNOME workstation with Microsoft Intune and Defender
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
# Opt out of the upstream fedora.yaml so we don't pull the Firefox RPM.
|
# Opt out of the upstream fedora.yaml so we don't pull the Firefox RPM.
|
||||||
@@ -18,22 +18,8 @@ default_target: graphical.target
|
|||||||
|
|
||||||
ref: fedora-intune/${releasever_ref}/${basearch}/intune
|
ref: fedora-intune/${releasever_ref}/${basearch}/intune
|
||||||
|
|
||||||
# Ship /opt as a plain read-only directory instead of a symlink to /var/opt.
|
|
||||||
# Defender installs to /opt/microsoft/mdatp and keeps its state in
|
|
||||||
# /var/opt/microsoft/mdatp; with the default symlink those are the same,
|
|
||||||
# read-only directory. Needs composefs, which upstream enables. The vendor
|
|
||||||
# directories are linked into /opt by finalize.d/60-opt-links.
|
|
||||||
opt-usrlocal: root
|
|
||||||
|
|
||||||
repos:
|
|
||||||
- himmelblau
|
|
||||||
- microsoft-fedora44
|
|
||||||
- microsoft-edge
|
|
||||||
|
|
||||||
include:
|
include:
|
||||||
- base-atomic.yaml
|
- base-atomic.yaml
|
||||||
- custom.yaml
|
- custom.yaml
|
||||||
# Entra ID sign-in and Intune.
|
- intune.yaml
|
||||||
- himmelblau.yaml
|
|
||||||
- microsoft-edge.yaml
|
|
||||||
- defender.yaml
|
- defender.yaml
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
# Microsoft Edge, from the microsoft-edge repo (repos/microsoft.repo).
|
|
||||||
# Conditional Access to Microsoft 365 web apps on Linux requires Edge.
|
|
||||||
#
|
|
||||||
# Entra SSO in Edge: himmelblau-sso only registers its native messaging host
|
|
||||||
# and force-installs the Linux Entra SSO extension for Chrome, Chromium and
|
|
||||||
# Firefox. Edge reads /etc/opt/edge, so add the same two files there.
|
|
||||||
|
|
||||||
repo-packages:
|
|
||||||
- repo: microsoft-edge
|
|
||||||
packages:
|
|
||||||
- microsoft-edge-stable
|
|
||||||
|
|
||||||
add-files:
|
|
||||||
- ["edge-linux_entra_sso.json", "/etc/opt/edge/native-messaging-hosts/linux_entra_sso.json"]
|
|
||||||
- ["edge-himmelblau-policy.json", "/etc/opt/edge/policies/managed/himmelblau.json"]
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Himmelblau Community Nightly packages (free development builds). The
|
|
||||||
# maintained Stable repositories need a subscription entitlement token:
|
|
||||||
# https://himmelblau-idm.org/stable-repositories/
|
|
||||||
[himmelblau]
|
|
||||||
name=Himmelblau Community Nightly (Fedora 44)
|
|
||||||
baseurl=https://packages.himmelblau-idm.org/nightly/latest/rpm/fedora44
|
|
||||||
enabled=1
|
|
||||||
gpgcheck=1
|
|
||||||
gpgkey=https://packages.himmelblau-idm.org/himmelblau.asc
|
|
||||||
@@ -1,18 +1,11 @@
|
|||||||
# packages.microsoft.com, limited with includepkgs to the packages the image
|
[microsoft-rhel10]
|
||||||
# uses, so nothing else from the Microsoft repo can replace a Fedora package.
|
name=Microsoft Production (RHEL 10)
|
||||||
#
|
baseurl=https://packages.microsoft.com/rhel/10/prod/
|
||||||
# Fedora 44 build of Defender (mdatp): Fedora is a supported Defender
|
|
||||||
# distribution, servers and desktops. Pinned to 44 rather than $releasever so a
|
|
||||||
# new Fedora release doesn't fail the build before Microsoft publishes its repo.
|
|
||||||
|
|
||||||
[microsoft-fedora44]
|
|
||||||
name=Microsoft Production (Fedora 44)
|
|
||||||
baseurl=https://packages.microsoft.com/fedora/44/prod/
|
|
||||||
enabled=1
|
enabled=1
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
repo_gpgcheck=1
|
repo_gpgcheck=1
|
||||||
gpgkey=https://packages.microsoft.com/keys/microsoft-2025.asc
|
gpgkey=https://packages.microsoft.com/keys/microsoft-2025.asc
|
||||||
includepkgs=mdatp
|
includepkgs=intune-portal microsoft-identity-broker mdatp
|
||||||
|
|
||||||
[microsoft-edge]
|
[microsoft-edge]
|
||||||
name=Microsoft Edge
|
name=Microsoft Edge
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
# Runs in the target root during `rpm-ostree compose image` (edition 2024
|
|
||||||
# finalize.d hook). rpm-ostree relocates RPM content under /opt to
|
|
||||||
# /usr/lib/opt. With `opt-usrlocal: root` /opt is a plain directory in the
|
|
||||||
# image and nothing links it back, so link each vendor directory. Images on the
|
|
||||||
# default (/opt -> /var/opt, linked by tmpfiles.d) are left alone.
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
root="${PWD}"
|
|
||||||
|
|
||||||
[[ -d "${root}/usr/lib/opt" ]] || exit 0
|
|
||||||
if [[ -L "${root}/opt" || ! -d "${root}/opt" ]]; then
|
|
||||||
exit 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
for dir in "${root}"/usr/lib/opt/*; do
|
|
||||||
name="$(basename "${dir}")"
|
|
||||||
ln -sfn "../usr/lib/opt/${name}" "${root}/opt/${name}"
|
|
||||||
echo "Linked /opt/${name} -> /usr/lib/opt/${name}"
|
|
||||||
done
|
|
||||||
Reference in new issue
Block a user