fedora-intune: minimal GNOME workstation with Intune and Defender #1

Merged
Misthios merged 6 commits from misthios/fedora-intune AGit into main 2026-10-19 11:58:29 +00:00
25 changed files with 304 additions and 65 deletions
Showing only changes of commit 4f71d350fa - Show all commits

No files matched your search

+9 -4
View File
@@ -175,17 +175,22 @@ jobs:
steps: *build_steps
build-intune:
name: fedora-intune (44)
name: ${{ matrix.image }} (44)
needs: changes
if: needs.changes.outputs.intune == 'true'
runs-on: job-v2
strategy:
fail-fast: false
matrix:
image: [fedora-intune, fedora-intune-himmelblau]
container:
image: "quay.io/fedora-ostree-desktops/buildroot:44"
options: "--security-opt=label=disable --privileged --user 0:0 --device=/dev/fuse --volume /:/run/host:rw"
env:
IMAGE: fedora-intune
IMAGE: ${{ matrix.image }}
DISTRO: "44"
ARCH: x86_64
REGISTRY: git.plabble.org/misthios
@@ -215,7 +220,7 @@ jobs:
env:
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
run: |
for image in fedora-cosmic fedora-remote fedora-intune; do
for image in fedora-cosmic fedora-remote fedora-intune fedora-intune-himmelblau; do
./prune-releases.sh "${image}"
done
- name: Prune old registry tags
@@ -227,6 +232,6 @@ jobs:
authfile=/tmp/prune-auth.json
echo "${REGISTRY_TOKEN}" | skopeo login -u "${REGISTRY_USERNAME}" \
--password-stdin --authfile "${authfile}" git.plabble.org
for image in fedora-cosmic fedora-remote fedora-intune; do
for image in fedora-cosmic fedora-remote fedora-intune fedora-intune-himmelblau; do
AUTHFILE="${authfile}" ./prune-registry.sh "${image}" Misthios 44 rawhide
done
+4 -1
View File
@@ -201,7 +201,10 @@ case "${BUILD_MODE}" in
if [[ -d "${IMAGE_DIR}/files" ]]; then
cp -a "${IMAGE_DIR}/files/." "${BUILD_DIR}/upstream/"
fi
# Shared overlay (files/repos used by multiple images).
# Shared overlay (manifests/files/repos used by multiple images).
if compgen -G "${REPO_ROOT}/images/shared/*.yaml" >/dev/null; then
cp "${REPO_ROOT}"/images/shared/*.yaml "${BUILD_DIR}/upstream/"
fi
if [[ -d "${REPO_ROOT}/images/shared/files" ]]; then
cp -a "${REPO_ROOT}/images/shared/files/." "${BUILD_DIR}/upstream/"
fi
+2 -1
View File
@@ -22,7 +22,8 @@ if [[ "${all}" == 1 ]]; then
fi
grep -qE '^images/fedora-cosmic/' <<< "${changed}" && cosmic=true
grep -qE '^images/fedora-remote/' <<< "${changed}" && remote=true
grep -qE '^images/fedora-intune/' <<< "${changed}" && intune=true
# Both fedora-intune and fedora-intune-himmelblau.
grep -qE '^images/fedora-intune' <<< "${changed}" && intune=true
echo "cosmic=${cosmic}"
echo "remote=${remote}"
@@ -0,0 +1,10 @@
# Build configuration for images/fedora-intune-himmelblau. Sourced by build.sh.
BUILD_MODE=upstream
UPSTREAM_REPO="https://gitlab.com/fedora/ostree/ci-test.git"
# Stable only, like fedora-intune.
declare -A UPSTREAM_REFS=(
[44]=f44
)
@@ -0,0 +1,3 @@
# Intune compliance check at login and on a timer, for every user.
enable himmelblau-compliance-check.service
enable himmelblau-compliance-check.timer
@@ -0,0 +1,4 @@
# Himmelblau daemons; they pull in their sockets with Requires=.
enable himmelblaud.service
enable himmelblaud-tasks.service
enable himmelblau-hsm-pin-init.service
@@ -0,0 +1,17 @@
# 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
# Home directories named after the account (e.g. /home/jdoe), with a UPN alias.
home_attr = CN
home_alias = SPN
use_etc_skel = true
@@ -0,0 +1,68 @@
# 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
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"]
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
# 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,37 @@
# Minimal GNOME workstation where users sign in with their Entra ID account
# (Himmelblau), with Microsoft Edge and Microsoft Defender for Endpoint.
# Himmelblau joins the device to Entra ID at the first sign-in and, with
# apply_policy, enrolls it in Intune and reports compliance itself, so the
# Microsoft Intune app (fedora-intune) is not included.
metadata:
summary: Fedora minimal GNOME workstation with Entra ID sign-in (Himmelblau)
variables:
# Opt out of the upstream fedora.yaml so we don't pull the Firefox RPM.
distro: "fedora-intune-himmelblau"
variant: "intune-himmelblau"
# Keep it small: no weak/optional dependencies.
recommends: false
default_target: graphical.target
ref: fedora-intune-himmelblau/${releasever_ref}/${basearch}/intune-himmelblau
# /opt as a plain directory; see fedora-intune/manifest.yaml.
opt-usrlocal: root
repos:
- himmelblau
- microsoft-rhel10
- microsoft-edge
include:
- base-atomic.yaml
# Shared with fedora-intune (images/shared).
- workstation.yaml
- microsoft-edge.yaml
- defender.yaml
# Entra ID sign-in and Intune.
- himmelblau.yaml
@@ -0,0 +1,9 @@
# 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
-39
View File
@@ -1,39 +0,0 @@
# Microsoft Defender for Endpoint on Linux (mdatp), RHEL 10 build.
#
# mdatp's %post does the real install: it writes mdatp.service and the
# mde_netfilter_v2 units, loads its SELinux module and creates a machine GUID
# and state under /var/opt/microsoft/mdatp. In a compose the daemon cannot
# start and /var is not shipped, so the units and SELinux module are handled
# 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
# from the Defender portal on the device (see the Intune test doc).
repo-packages:
- repo: microsoft-rhel10
packages:
- mdatp
packages:
# Used by the Defender TVM/inventory scripts and the onboarding script.
- python3
add-files:
- ["50-defender.preset", "/usr/lib/systemd/system-preset/50-defender.preset"]
postprocess:
- |
#!/usr/bin/env bash
set -xeuo pipefail
conf=/opt/microsoft/mdatp/conf
# 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
if [[ ! -f "/usr/lib/systemd/system/${unit}" ]]; then
install -m 0644 "${conf}/${unit}" "/usr/lib/systemd/system/${unit}"
fi
done
semodule -n -i "${conf}/selinux_policies/out/mdatp_service.pp"
# Never ship a machine GUID or onboarding state baked in at build time.
ls -laR /etc/opt/microsoft /var/opt/microsoft 2>/dev/null || true
rm -rf /var/opt/microsoft/mdatp
rm -f /etc/opt/microsoft/mdatp/mdatp_onboard.json
@@ -1,3 +0,0 @@
# Defender daemon and its network filter (socket-activated).
enable mdatp.service
enable mde_netfilter_v2.socket
+8 -13
View File
@@ -1,23 +1,14 @@
# Microsoft Intune app for Linux (intune-portal), the Microsoft identity broker
# and Microsoft Edge (needed for Conditional Access to Microsoft 365 web apps).
# Microsoft Intune app for Linux (intune-portal) and the Microsoft identity
# broker, from the microsoft-rhel10 repo (shared/repos/microsoft.repo).
#
# 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
# because they link against the same library generation as Fedora 44.
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)
@@ -26,6 +17,7 @@ packages:
- checkpolicy
- policycoreutils
- sssd-client
- nss-altfiles
# Secret storage for the identity broker tokens.
- gnome-keyring
- libsecret
@@ -57,7 +49,10 @@ postprocess:
-e '/^session[[:space:]]\+required[[:space:]]\+pam_unix.so/a session optional pam_intune.so' \
"${profile}/${f}"
done
authselect select custom/intune-profile --force --nobackup
# with-altfiles: system users (gdm, pipewire, mdatp, ...) live in
# /usr/lib/passwd on rpm-ostree systems, not in /etc/passwd as on RHEL.
authselect select custom/intune-profile with-altfiles --force --nobackup
getent passwd gdm >/dev/null
- |
#!/usr/bin/env bash
+15 -2
View File
@@ -18,8 +18,21 @@ default_target: graphical.target
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.
opt-usrlocal: root
repos:
- microsoft-rhel10
- microsoft-edge
include:
- base-atomic.yaml
- custom.yaml
- intune.yaml
# Shared with fedora-intune-himmelblau (images/shared).
- workstation.yaml
- microsoft-edge.yaml
- defender.yaml
# Microsoft Intune app.
- intune.yaml
+46
View File
@@ -0,0 +1,46 @@
# Microsoft Defender for Endpoint on Linux (mdatp), RHEL 10 build, from the
# microsoft-rhel10 repo (shared/repos/microsoft.repo).
#
# mdatp's %post does the real install: units, SELinux module, and per-machine
# state under /var/opt/microsoft/mdatp (machine GUID, definitions database,
# logs). In a compose /var is read-only, and that state must not be shared
# between machines anyway, so the %post is saved into the image and run once on
# first boot by mdatp-firstboot.service. mdatp.service waits for it.
#
# Onboarding is per tenant and not part of the image: run the onboarding script
# from the Defender portal on the device.
repo-packages:
- repo: microsoft-rhel10
packages:
- mdatp
packages:
# The %post is a bash script; Defender's inventory scripts and the
# onboarding script are Python.
- bash
- python3
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"]
postprocess:
- |
#!/usr/bin/env bash
set -xeuo pipefail
# The %post wrote the units during the compose; fail the build if a newer
# mdatp stops doing that, rather than shipping a Defender that never starts.
for unit in mdatp.service mde_netfilter_v2.service mde_netfilter_v2.socket; do
test -f "/usr/lib/systemd/system/${unit}"
done
# rpm-ostree keeps /opt content in /usr/lib/opt until the commit.
for root in /usr/lib/opt /opt; do
pp="${root}/microsoft/mdatp/conf/selinux_policies/out/mdatp_service.pp"
[[ -f "${pp}" ]] && break
done
semodule -n -i "${pp}"
rpm -q --qf '%{POSTIN}' mdatp > /usr/libexec/mdatp-postinst
chmod 0755 /usr/libexec/mdatp-firstboot /usr/libexec/mdatp-postinst
+4
View File
@@ -0,0 +1,4 @@
# Defender: first-boot setup, the daemon and its network filter.
enable mdatp-firstboot.service
enable mdatp.service
enable mde_netfilter_v2.socket
+20
View File
@@ -0,0 +1,20 @@
#!/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
+17
View File
@@ -0,0 +1,17 @@
#!/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
+3
View File
@@ -0,0 +1,3 @@
# Don't start Defender before mdatp-firstboot.service has created its state.
[Unit]
ConditionPathExists=/var/lib/mdatp-firstboot.done
@@ -0,0 +1,15 @@
[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
+7
View File
@@ -0,0 +1,7 @@
# Microsoft Edge, from the microsoft-edge repo (shared/repos/microsoft.repo).
# Conditional Access to Microsoft 365 web apps on Linux requires Edge.
repo-packages:
- repo: microsoft-edge
packages:
- microsoft-edge-stable
@@ -1,3 +1,6 @@
# packages.microsoft.com, limited with includepkgs to the packages the images
# use, so nothing else from the RHEL repo can replace a Fedora package.
[microsoft-rhel10]
name=Microsoft Production (RHEL 10)
baseurl=https://packages.microsoft.com/rhel/10/prod/
@@ -1,4 +1,5 @@
# Minimal GNOME workstation.
# Minimal GNOME workstation, shared by the fedora-intune images. Include it
# from a manifest that sets `opt-usrlocal: root` (finalize.d/60-opt-links).
packages:
# Fedora integration normally provided by the upstream fedora.yaml (which we
@@ -45,7 +46,7 @@ exclude-packages:
add-files:
- ["50-workstation.preset", "/usr/lib/systemd/system-preset/50-workstation.preset"]
# Key-only SSH for local accounts.
- ["10-intune-sshd.conf", "/etc/ssh/sshd_config.d/10-intune-sshd.conf"]
- ["10-workstation-sshd.conf", "/etc/ssh/sshd_config.d/10-workstation-sshd.conf"]
postprocess:
# uresourced breaks the GDM greeter's user manager; see fedora-remote.