Microsoft Intune in an isolated, rootless Linux container — headless by default, with seamless Entra ID SSO in your host browser.
📖 Documentation · Quickstart · Architecture · Roadmap
Intune's Linux agent and the Microsoft identity broker are desktop apps that
make broad changes to a host. intune-container runs them in a dedicated,
rootless container — built from unprivileged user namespaces, with no host
root and no sudo — so your host stays clean. A tiny native-messaging bridge
then lets your everyday browser use the container's enrollment to sign in to
Teams, Outlook, and other M365 apps. Works on any Wayland compositor (niri,
Hyprland, Sway, GNOME, KDE) and X11.
Install the latest AppImage to ~/.local/bin:
curl -fsSL https://raw.githubusercontent.com/magicabdel/intune-container/master/install.sh | shPrefer a package or building yourself? Grab the .deb/.rpm from the
releases, or
build from source (needs Rust, Node.js + npm, and WebKitGTK/GTK):
just install # builds + installs `intune-container` (GUI + CLI in one binary)Run it with no subcommand to open the graphical interface (the default) and click Enroll this device. Set-up, the portal, Edge, browser SSO, live health and backups are all there; closing the window keeps it running in your tray:
intune-container # opens the GUIThe same binary is the command-line tool when given a subcommand:
intune-container enroll # set up + enroll your device (opens the portal)
intune-container start # (optional) run headless + seamless Teams/M365 SSODaily CLI use: edge · status · update · doctor · stop. Full walkthrough
in the Quickstart.
On a headless box — an EC2 instance, a build machine — there is no display to put
the sign-in window on, and the Primary Refresh Token still expires: Entra then
answers interaction_required and every token call fails until a human signs in
again. login does that over SSH, with no VNC and no compositor:
intune-container login # the sign-in window, drawn in your terminalIt starts a private invisible display, runs the portal on it, draws that display with half-block characters and sends your keys and clicks back. Type your address and password where you are; when the two-digit Authenticator number appears, press F2 to zoom in and read it. F1 lists the keys, F4 fits the whole window, Ctrl+arrows pan, Ctrl+Q leaves.
It needs Xvfb on the host (sudo apt install xvfb) and a terminal with 24-bit
colour. Everything it starts — the portal, the display forwarding, the X server —
it stops again, including when your SSH connection drops.
Half-block cells cost half the vertical resolution, and 13-pixel text survives that
only when you zoom. --web serves the same window to a browser instead, at its own
size and with a real pointer:
intune-container login --web # prints one link, with a token for this sessionIt listens on this host's tailnet address, so any device in your tailnet can
open it — your phone included, which is where the Authenticator prompt arrives. With
no Tailscale on the host it binds loopback instead and prints the ssh -L line for
it. --bind and --port override both. Every request must carry the token from the
link; nothing else is served.
The page is one file with no build step: a canvas, the tiles that changed since the last frame (gzipped, so the browser inflates them), and your keys and clicks posted back. Ctrl+V pastes — a password from your manager is typed into the display one key at a time. Finish closes the window and ends the session, as Ctrl+Q does in the terminal viewer.
Why not a plain text prompt? Because the identity broker allows none: it refuses a
device-code flow on Linux (AcquireTokenWithDeviceCodeFlow is not implemented on Linux platform) and renders the sign-in itself, in an embedded WebKitGTK view on
an X display. The window has to exist — this brings it to the terminal.
login signs in for you: it presses Sign in, types the address and the password,
and closes what it opened. When you would rather see Intune and do every step
yourself, share the screen instead:
intune-container screen # opens Intune, streams it, drives nothingIt starts the Intune portal so the first screen is there without a second command,
then touches nothing: every window the container opens — the portal, the identity
broker's Authentication dialog, an Edge window — appears as it is, and your keyboard
and mouse go straight to it. --bind, --port and --geometry work as they do for
login --web, and the link carries the same per-session token.
It streams the box around the windows rather than the whole screen, so a 478×628 portal fills the page instead of sitting in the corner of a black 1280×800 desktop. A screen with nothing on it yet says so on the picture.
Run it twice and the second one shares the screen the first already opened, rather
than making an empty one: intune-portal is a single instance, so a command that
insisted on its own display would stream black pixels while the portal drew
somewhere else. A share that finds a screen leaves it running when it closes — only
a share that opened one closes it.
The default container image is publicly hosted and ready to go (it already
includes everything for headless SSO) — there's nothing to build, and no
container engine is needed: the image is pulled with a built-in OCI client. Each
start compares the image's digest in the registry against the one the rootfs was
extracted from and re-pulls only when it changed (update --check shows both).
Requirements: a Linux host with unprivileged user namespaces enabled, a
/etc/subuid+/etc/subgidrange for your user,newuidmap/newgidmap(theuidmap/shadowpackage), and cgroup v2. Nosudo,systemd-nspawn,machinectl,nsenter, Docker, or Podman. Building from source needs Rust +just, and Node.js + npm — the interface is a TypeScript / React / Emotion app (infrontend/) that Tauri bundles into the binary at compile time. At runtime the GUI needs WebKitGTK 4.1, and — for the system tray —libayatana-appindicator(libappindicator-gtk3on some distros); without it the GUI still runs as a plain window (no tray).
A tray-resident Tauri desktop app. The Console shows the container's state,
the few actions you actually use (start/stop, open the portal, open Edge), the
signed-in identity, and live health checks — the same data doctor and
sso-test report. Other tabs give you an in-app Shell (a real terminal
inside the container), Backup/restore, Logs, and Destroy. From the
tray: single-click for a quick panel, double-click for the full window, and a
status-tinted icon that tracks the container (grey = stopped, teal = running,
amber = display attached).
The preview above is an illustrative render with placeholder data — the real interface shows your own status and account.
A single crate produces a single intune-container binary that is both the
graphical interface (default) and the command-line tool:
| Part | Role |
|---|---|
src/lib.rs (library) |
All logic — container lifecycle, enroll, SSO, backups, health checks — exposed as Rust functions in ops. |
src/runtime.rs |
The rootless runtime: user namespaces, setns, a delegated cgroup scope, pivot_root — no host root. |
src/main.rs (binary) |
clap dispatch: no subcommand → GUI; any subcommand → CLI. |
src/gui.rs |
The Tauri shell: window, tray, and typed commands that call ops. |
frontend/ |
The interface itself — TypeScript + React + Emotion (Vite), bundled into the binary. |
Both the GUI and CLI call the same ops functions in-process — neither
shells out, and neither needs elevated privilege.
- Rootless — boots the container's
systemdinside an unprivileged user namespace; no host root, nosudo, nosystemd-nspawn/machinectl/nsenter. - Headless by default — no window into your screen; the real display is forwarded only for the interactive portal and Edge flows.
- Seamless host-browser SSO — Teams/Outlook/M365 sign in automatically via the container's enrollment (no Python, no proxy daemon, no host bus).
- Compositor-agnostic — auto-detects Wayland, abstract X11, and Xauthority; no hardcoded socket names.
- One-command enroll — provision, boot, and open the portal in one step.
- Terminal sign-in (
login) — for a host with no screen: the portal runs on a private invisible display and is drawn in the terminal, keys and clicks included. No VNC server, no compositor, no X forwarding. - Browser sign-in over the tailnet (
login --web) — the same private display, served to any device in your tailnet at full resolution, behind a per-session token. One built-in HTTP server, no WebSocket and no VNC. - Screen share (
screen) — Intune opened for you and streamed to a browser, driving nothing: every window as it is, cropped to the windows, and a second run shares the screen the first one opened. - Enrollment backup/restore — survive container rebuilds without re-enrolling.
- Microsoft Edge in the container, with display/GPU passthrough during GUI sessions.
- Live health checks (
doctor) — registration, container, network, broker, keyring, and the compliance agent, surfaced right in the interface. - Graphical interface (default) — a tray-resident Tauri app: Console, in-app Shell, Backup/restore, Logs, and Destroy tabs, with a status-tinted tray icon and quick actions. Closing the window keeps it in the tray.
- One binary, two faces — a single
intune-containerexecutable is both the GUI (no subcommand) and the CLI (any subcommand), sharing one library.
- Private network namespace — the container currently shares the host network; a private netns with userspace egress (and LAN/localhost blocked) would close the main isolation gap (see the Roadmap).
- Live display attach — attach the host display to an already-running headless container without a restart.
| Compositor | Status | Notes |
|---|---|---|
| Niri | ✅ | Abstract X11 sockets auto-detected |
| Hyprland | ✅ | Standard XWayland |
| Sway | ✅ | Standard XWayland |
| GNOME | ✅ | Mutter Xauthority auto-detected |
| KDE | ✅ | Standard Xauthority |
This project stands on the shoulders of two excellent projects:
- frostyard/intuneme — the original
systemd-nspawn-based Intune manager that inspired this container approach (and the base OCI image). - siemens/linux-entra-sso — the browser extension and native-messaging protocol that make host SSO work; this project ships a compatible native-messaging host. Install the extension from its releases.
This is a personal, educational tool for running Microsoft Intune in an isolated container — for example, to keep corporate device management off your personal Linux machine. It is not intended to bypass, defeat, or misrepresent your organization's device-management or compliance controls, and it does not modify or weaken Intune or Entra ID themselves.
You are responsible for using it in line with your employer's acceptable-use and security policies and your Microsoft licensing terms. If you're unsure whether this is permitted in your environment, check with your IT/security team first. Provided as-is, with no warranty.
Source is MIT. It automates Microsoft proprietary software
(intune-portal, microsoft-edge, the identity broker) and integrates with
linux-entra-sso (MPL-2.0); those have their own terms and require a valid
Intune / Microsoft 365 subscription.
