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) <noreply@anthropic.com>
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) <noreply@anthropic.com>
The changes job ran on the bare runner, which has no git (the build jobs
install it before checkout). actions/checkout then fell back to a tarball
without history, `git diff` failed, `|| true` turned that into an empty
change list, and an empty list means "rebuild everything" - on every push
and PR.
Run the job in the buildroot container with git and nodejs, warn when the
diff fails instead of hiding it, treat a diff with no changes as "build
nothing", and let a manual run pick one image. The weekly scheduled run
still rebuilds all images for package updates.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The RHEL 10 repo was there for the Intune app, which only ships for RHEL;
that app is gone and the repo now only provides mdatp. Use Microsoft's
Fedora 44 repo instead: Fedora is a supported Defender distribution
(servers and the Linux desktop preview), and it carries a newer build
(101.26091.0003 vs 101.26081.0010 in the RHEL 10 repo). Same signing key
and signed metadata.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Himmelblau installs Intune Linux custom configuration scripts as
/etc/cron.d jobs, and the image had no cron, so a script policy failed to
apply. Add cronie and enable crond.
Add a custom configuration script that repairs Microsoft Defender on its
schedule: finishes first-boot setup, restarts a stopped daemon, onboards with
the tenant's mdatp_onboard.json (pasted into the script in Intune), and
re-enables real-time protection and updates definitions when needed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Himmelblau reports VERSION_ID from /etc/os-release as the device OS version,
so Intune showed "44". Give himmelblaud and himmelblaud-tasks their own
os-release (bind-mounted in their mount namespace only) with VERSION_ID set to
OSTREE_VERSION, e.g. 44.20261019.0. The rest of the system keeps the stock
VERSION_ID that toolbox, flatpak and dnf expect.
Add an Intune custom compliance discovery script and rules for Microsoft
Defender for Endpoint (installed, running, onboarded, licensed, healthy,
real-time protection, definitions up to date). These are uploaded to Intune,
not shipped in the image.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
himmelblau-sso registers its native messaging host and force-installs
the Linux Entra SSO extension only for Chrome, Chromium and Firefox.
Edge reads /etc/opt/edge, so ship the same two files there.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Go with Entra ID sign-in through Himmelblau for fedora-intune and drop
the Microsoft Intune app (intune-portal, identity broker) variant. With
a single image, the workstation, Edge and Defender manifests move back
from images/shared into images/fedora-intune, and CI builds just that
image again. The Microsoft repo is now limited to mdatp and Edge.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Fixes found by building and inspecting the image:
- Select the Intune authselect profile with-altfiles. Without it system
users in /usr/lib/passwd (gdm, pipewire, mdatp) do not resolve and the
login screen never starts. The build now fails if gdm does not resolve.
- Ship /opt as a plain directory (opt-usrlocal: root). Defender installs
to /opt/microsoft/mdatp and keeps state in /var/opt/microsoft/mdatp;
with the default /opt -> /var/opt symlink those were the same
read-only directory. rpm-ostree still relocates /opt content to
/usr/lib/opt, so finalize.d/60-opt-links links it back (only for images
where /opt is a directory; fedora-cosmic/remote are unaffected).
- Run Defender's %post on first boot (mdatp-firstboot.service): in a
compose /var is read-only and the machine GUID/state must not be
shared. mdatp.service waits for it.
New image fedora-intune-himmelblau: the same workstation with Entra ID
sign-in through Himmelblau (Community Nightly packages), which joins the
device at the first sign-in and enrolls it in Intune (apply_policy)
instead of the Microsoft Intune app. Workstation, Edge and Defender
manifests move to images/shared so both images use them; build.sh copies
images/shared/*.yaml and CI builds both images in one matrix job.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR builds never push, and Gitea gives them no secrets, so the login with
an empty token failed every PR build before it started.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
New image for testing Microsoft Intune enrollment and Linux compliance
policies: a minimal GNOME workstation (gnome-initial-setup, Ptyxis,
Files) with the Intune app, the Microsoft identity broker, Edge and
Defender for Endpoint (mdatp) from packages.microsoft.com (RHEL 10
builds, limited to those packages with includepkgs).
No Kanidm: the Intune app installs its own authselect profile with
pam_intune for the password compliance checks. Its %post cannot select
the profile or load the SELinux module during a compose, so both are
redone in postprocess.
build.sh now copies every *.yaml of an image, so manifests can be split
into more files than manifest.yaml and custom.yaml.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The iGPU on the Proxmox host (UHD 630, 8086:3e91) is bound to vfio-pci and
will be passed through to this VM, so the guest's i915 needs the GuC/HuC
blobs (intel-gpu-firmware) and iHD VA-API decode (libva-intel-media-driver).
Both are upstream common.yaml entries that the trim list was dropping; iris
(mesa-dri-drivers) and i915 (kernel-modules) are already present.
base-atomic ships mesa-dri-drivers (the virtio_gpu Gallium driver) but not
mesa-libEGL, and with recommends off nothing pulls it in. mutter/gnome-shell
only require libglvnd's EGL dispatcher, which then has no Mesa backend, so the
session falls back to llvmpipe software rendering. Add mesa-libEGL explicitly
so the host iGPU is used under Proxmox's VirGL/virtio-gpu display.
Per-user Flatpaks are only visible to the account that logged in first (and
consumed space per user). Install them into the system installation instead, so
every user including Kanidm accounts sees Firefox.
The minimal base (recommends off) was missing session infrastructure and one
maintained service actively broke the greeter, so RDP clients saw only a white
screen:
- add dbus-daemon: gdm-wayland-session runs it for the greeter session bus
- add systemd-pam: pam_systemd starts the per-user systemd manager
- nsswitch: shadow must include systemd so pam_unix can resolve GDM 50's
dynamic gdm-greeter user (Fedora 44 ships 'files systemd')
- mask uresourced: its resource tuning makes user@<uid>.service fail with
'Failed to spawn executor', leaving no systemd user bus for gnome-session
grdctl's interactive set-credentials reads from a controlling terminal, which a
systemd service does not have, so piping the username/password in set nothing
and GRD denied every client with 'Credentials are not set'. Pass them as
arguments instead.
grd-firstboot aborts before enabling RDP because it shells out to openssl to
generate the TLS certificate, and the minimal image did not include the openssl
CLI. Add it. Also enable sshd so the headless host is reachable without RDP.
fedora-remote has no rawhide.* tags yet, so grep exited non-zero and, under
set -e/pipefail, killed prune-registry.sh before it reported anything. Tolerate
an empty match.
kernel-modules-extra is declared in the upstream top-level common.yaml, not
under packages/, so the headless fedora-remote image still requested it while
also listing it in exclude-packages, which made compose fail with
'Packages not found: kernel-modules-extra'. Scan top-level manifests as well,
and drop keys whose block is left empty (comments/blanks do not count).
Gitea does not expand a dynamic fromJson matrix (the job name stayed literal
and it queued with no matching runner). Instead, compute cosmic/remote booleans
in a changes job and gate static-matrix build jobs with job-level if.
Add a changes job that diffs the pushed range (or PR base) and emits a matrix
via ci-matrix.sh. Shared files or build tooling changes rebuild all images.
Also fix the upstream package trim to drop top-level keys left empty (e.g.
packages-x86_64 when every entry is excluded), which broke fedora-remote.
- Move Kanidm/flatpak/finalize.d/sshd files to images/shared/{files,repos}
and have build.sh overlay them for every image.
- fedora-remote: aggressive hardware trim (no firmware, no kernel-modules-extra,
no non-QEMU guest agents), keep + enable qemu-guest-agent (Proxmox).
- Fixes the missing package-list (release step) for fedora-remote.
New image built from upstream base-atomic (no desktop), adding only gnome-shell
+ gdm + gnome-remote-desktop + pipewire/portal + foot + Flatpak + Kanidm.
recommends=false to stay lean. Configures GNOME Remote Desktop headless remote
login on first boot (TLS + credentials + enable). Firefox preinstalled as a
per-user Flatpak. Add it to the CI matrix and prune both images.
Ship an sshd_config.d drop-in so sshd fetches authorized keys from Kanidm via
kanidm_ssh_authorizedkeys. Named 10-* to take precedence over systemd-userdbd.
Enforcing SELinux blocks Kanidm user resolution/authentication for sshd and
friends, but no AVC is emitted (even with dontaudit disabled), so the exact
allow could not be pinned. Keep the CIL allows and mark the login/nss domains
permissive so Kanidm logins work. Revisit when the denial can be isolated.
The registry path is lowercase, so the capitalized owner made skopeo inspect
fail and every cosign .sig tag was deleted. Lowercase the owner and, if no
digest resolves, keep all signature tags instead of deleting them.
Add prune-registry.sh: keeps the moving tag and newest versioned tag per
distro plus their cosign signature tags, deletes the rest via the Gitea
packages API. Wire it into the prune job (uses REGISTRY_TOKEN).
- uid_attr_map/gid_attr_map = name so NSS/PAM see 'misthios', not the SPN
- allow_local_account_override = [misthios] so Kanidm takes over an existing
local account of the same name (Kanidm otherwise ignores its own entry,
leaving logins to fall back to the local account and fail)
Schedule moves to Mondays 04:00 UTC. release.sh now deletes older releases
for the same image/distro (and their assets and tags) after publishing, so
only the newest is kept. Add prune-releases.sh for a one-off cleanup.
nss/pam consumers (sshd, the display manager, systemd-userdbd, polkit, ...)
were denied write access to /run/kanidm-unixd/* (var_run_t), so Kanidm users
could not be resolved or authenticated and logins failed with 'invalid user'.
Add the audit2allow-derived CIL and load it at build time (semodule noreload).
kanidm-unixd runs with DynamicUser=yes, so it cannot read the root-only
/etc/kanidm/unixd_token. With service_account_token_path set in
/etc/kanidm/unixd the daemon failed with PermissionDenied and no Kanidm user
resolved. Drop that setting and inject the token with LoadCredential +
KANIDM_SERVICE_ACCOUNT_TOKEN_PATH through a generated drop-in.
Verified in a QEMU VM: kanidm-unixd active, 'kanidm-unix status' online,
'getent passwd job' resolves.
build.sh writes KANIDM_UNIXD_TOKEN to /etc/kanidm/unixd_token (0600) when the
secret is set, via a generated add-files include. Without the secret the image
is built unchanged and the token must be provisioned on the host.
/var is read-only during compose so authselect could not create its backup
dir and fell back to writing PAM/nsswitch files directly. --nobackup keeps
authselect in charge of the symlinks.
Date-only build IDs collided on same-day rebuilds, so release tags already
existed and the changelog/release step was skipped. Use the CI run number
(timestamp fallback locally).
Kanidm is not in the Fedora repos; it ships from the openSUSE OBS
network:idm project. Add per-distro repo files (Fedora_44 / Fedora_Rawhide)
and teach build.sh to overlay repos/<distro>/*.repo.
Install kanidm-unixd-clients/kanidm-clients, point /etc/kanidm/config at
auth.plabble.org, configure unixd (TPM-backed cache, pam_allowed_login_groups),
and set up PAM/nsswitch via an authselect custom profile (with a direct-file
fallback). Enable kanidm-unixd{,-tasks} and make unconfined_service_t
permissive until Kanidm ships an SELinux policy.
Avoid mounting the built image with buildah (ran out of disk); the
finalize.d hook records the rpm list next to the treefile and release.sh
consumes it directly.
Trim NVIDIA, Intel GPU/audio/platform, other wireless vendors and VM guest
packages from the cloned upstream manifests, and block linux-firmware's
recommends of them via a generated exclude list. Keeps amd-gpu-firmware,
amd-ucode-firmware, iwlwifi-mvm-firmware (AX200) and alsa-sof-firmware.