top of page

Beyond the golden image, part 3. The Bourne Identity: Entra ID, Himmelblau and a laptop that doesn't know who you are

Foto van schrijver: Edwin de Bruin
Edwin de Bruin
11 minuten geleden
8 minuten om te lezen

Part 3: Logging in to NixOS with Entra ID


In part1 I said switching identity provider should be a change in one file, not a migration project. In part2 I promised to put that to the test. That turned out to be too much for one post, so it's a two-parter. This part: Himmelblau talking straight to Entra ID. Upcoming Part 4: Keycloak in between.

The direct route is the one that feels the most like Windows: number matching on the login screen, a Hello PIN, a device object in Entra. Exactly what users already know from their Windows laptop.


A freshly installed laptop is a bit like Jason Bourne waking up on that boat: plenty of skills, but no idea who it belongs to. The first login is where it finds out.


This part covers why I started with Entra ID at all, why Himmelblau is the piece that makes it work, what the login flow looks like, and how to control who gets in. Including a login screen that simply refused to show me a number.



Why Entra ID? Yes, really


Quick recap for those who read part 1 and part 2 (feel free to skip ahead): yes, I’m connecting a sovereignty project to Microsoft Entra ID. I get the irony and so forth.


But this is where design meets practice. Every organization I work with already has an Entra tenant. The users, the MFA methods, Conditional Access and the leaver process all live there. A second identity world just for the Linux laptops means two places to disable an account when someone leaves. Ask any auditor how they feel about that.


So the honest position: Entra ID is where the identities live today. A sovereign workplace doesn’t start by moving every identity on day one. It starts by making sure the laptop doesn’t care where the identity lives, so that moving it later is a change, not a project.


Why Himmelblau


Linux has had PAM and NSS for decades. They are great at asking a directory "who is this and is the password right". They were never designed for a cloud identity provider that wants MFA, number matching, device identity and tokens. Something has to translate. I looked at four options:

Option

Entra users on the laptop

Offline login

MFA at the login screen

Why it fell off (or didn't)

Local accounts

No

Yes

No

Two identities per user. The leaver process gets a second checklist

No

Yes

Own MFA

Can't federate with an external OIDC provider yet; the project calls it a very long time away

Yes, Entra or Keycloak

Not documented

Via device flow on a second device

Closest runner-up, but I couldn't find a proven offline path

Yes

Yes, Hello PIN

Yes, native

The winner

Himmelblau (German for sky blue, as in Azure blue, genuinely clever name!) is an open source project written in Rust that plugs into PAM and NSS and speaks Entra ID natively. What made it click for me is that it behaves like a Windows laptop, not like a Linux box with a password check bolted on:


  • MFA at the login prompt, using your existing Conditional Access policies.

  • Hello PIN,  protected by the TPM when the laptop has one. Without a TPM the keys are encrypted in software, and the encrypted disk does the heavy lifting. Either way, after the first login the user types a PIN, not a password.

  • Offline login with that PIN. Laptop on a train without WiFi? Still works.

  • Device join: the laptop becomes a device object in Entra ID.

  • Browser SSO for Microsoft 365 and Entra-protected apps, through Siemens' linux-entra-sso extension.

  • Intune enrollment and compliance, currently in beta.

  • Generic OIDC mode, so it can also talk to Keycloak or Okta. Hold that thought.


And for us it matters that there is a NixOS module. In my repo it's one flake input and a handful of settings per partner.


Himmelblau, straight to Entra ID


What to do in Entra ID

The Entra side is less work than you'd expect. One app registration, one group, and a few values to write down. Do this once per tenant, so once per partner.


  1. Create the app registration. Entra admin center, App registrations, New registration. Give it a name you'll recognize later, like Himmelblau workplace, and choose single tenant (accounts in this organizational directory only). Leave the redirect URI empty for now.

  2. Add the redirect URI. In the new app, go to Authentication, Add a platform, Mobile and desktop applications, and add himmelblau://Himmelblau.EntraId.BrokerPlugin.

  3. Grant consent. Under API permissions, the default Microsoft Graph User.Read (delegated) is enough. Click Grant admin consent.

  4. Don't create a client secret. Himmelblau doesn't need one, and a secret on a laptop is a secret you can't protect.

  5. Write down the Application (client) ID from the Overview page. That's your app_id. It's an identifier, not a secret, so it can live in a public repo.

  6. Create a security group for the people who may log in to the Linux laptops, for example GG-Workplace-NixOS, and write down its Object ID. We'll need it in a minute.

  7. Check your Conditional Access and MFA. 

    Himmelblau uses the same policies as every other sign-in. If your users get number matching in the Authenticator app today, they'll get it on the login screen too.


And one value you probably already know: the tenant's primary domain, the part after the @ in your users' UPN. Only the primary domain, by the way. Himmelblau expects one domain per tenant here.

In EUC terms: this is the same app registration dance you've done for every SaaS app in the last five years, just without the secret.


In the repo it lives where part 2 said it would, in the partner layer:


# Partners/Edwin/Config-system-identity.nix
{ ... }:
{
  services.himmelblau.settings = {
    domain = [ "debruinonline.net" ];
    app_id = "<application id of the app registration>";
    shell  = "/run/current-system/sw/bin/bash";
  };
}

That shell line deserves its own sentence. Himmelblau defaults to /bin/bash, which doesn't exist on NixOS. The login succeeds, the session starts, and one second later you're back at the login screen. GDM's only comment: Session never registered. That one took me a while to find.



Everything happens on the login screen. Entra only needs the internet, no VPN, and after day one the PIN does the work.


Step 1: no user list, just type your UPN. Exactly like a Windows login.
Step 1: no user list, just type your UPN. Exactly like a Windows login.


Step 2: the Entra ID password. Same one as for Microsoft 365, no separate Linux account.
Step 2: the Entra ID password. Same one as for Microsoft 365, no separate Linux account.

Step 3: the number shows up on GDM. Type it in the Authenticator app and you’re in. This is the screen SDDM never showed me.
Step 3: the number shows up on GDM. Type it in the Authenticator app and you’re in. This is the screen SDDM never showed me.

A little sidetrack: why Plasma?


DAWO gives you two choices for the desktop: GNOME or KDE Plasma. Both are excellent, and GNOME is arguably the more polished one. So why did I go with Plasma?


Because of the people who will use it. My users have spent their whole working lives on Windows. Plasma looks and behaves like what they know: a taskbar at the bottom, a start menu in the corner, windows with buttons where they expect them. Sit someone down at a Plasma desktop and they just start working. Sit them down at GNOME and the first question is “where is the start button?”


Could I make GNOME look like Windows? Sure, with a handful of extensions for a taskbar and a start menu. But every extension is one more thing to keep working after an update, and one more thing that breaks the day it doesn’t. In EUC terms: I’d rather pick a base image that’s close to what I need than layer customizations on top of one that isn’t.


Logged in: a Plasma desktop that any Windows user will find their way around.
Logged in: a Plasma desktop that any Windows user will find their way around.

The irony is that this choice caused the biggest headache in this post. Plasma comes with SDDM as its login screen, and SDDM is exactly what didn’t play nice with Entra. More on that below.


The number that didn't show up


In part 2 I swapped DAWO's login screen from SDDM to GDM and promised to explain why. Here it is. Plasma ships with SDDM, and SDDM doesn't fully support PAM conversations. Himmelblau needs that to show you the number for number matching, or to ask a second question after the password. On SDDM the number simply never appears (#1042), and SDDM answers every prompt with the password you typed (#1882). That last one is nasty: keep retrying and you're sending wrong answers to Entra, which can trigger smart lockout on the account.


The maintainers point to GDM as the login manager with full support. The desktop and the login screen are two separate things, so the fix in org/ was three lines: Plasma stays, SDDM forced off, GDM forced on. Plasma with GDM isn't an official DAWO combination, but it works. And it's an org-wide choice, which is why it's an mkForce.


Who gets to log in?


Here's the thing that's easy to miss. Out of the box, Himmelblau lets any user from the configured domain sign in, and creates their local account on the spot. That's convenient in a lab. In production, it means every account in your tenant that passes MFA can log in to every laptop.


In the Windows world you'd limit that with "Allow log on locally" or an Intune policy. Here it's one setting in the partner file:


services.himmelblau.settings.pam_allow_groups = [ "<object ID of an Entra group>" ];

Use the group's object ID, not its name.


The translation table, part 3

What we know from EUC

The DAWO / NixOS equivalent

Entra joined Windows device

Himmelblau with domain and app_id

Windows Hello for Business PIN

Himmelblau Hello PIN, protected by the TPM when there is one, encrypted in software when there isn’t

Primary Refresh Token and browser SSO

Himmelblau's PRT and linux-entra-sso

Credential provider on the logon screen

PAM, and a login manager that actually supports it (GDM)

"Allow log on locally"

pam_allow_groups with an Entra group

Gotchas I collected along the way


  • The shell. Set shell = "/run/current-system/sw/bin/bash". The default /bin/bash doesn't exist on NixOS, and the session dies one second after a successful login.

  • The lock screen. Add kde to Himmelblau's PAM services. Without it you can log in fine, but you can't unlock Plasma's lock screen with your Entra account.

  • The account lockout. DAWO's lockout covers the login screen it expects. Swap SDDM for GDM and you have to add gdm-password yourself, as shown in part 2.

  • SDDM retries. Don't keep hammering an SDDM login screen with an Entra account. Each attempt goes to Entra with the wrong answer, and smart lockout will notice before you do.


Where things stand


To keep myself honest again:


  • First login, number matching on GDM, the Hello PIN, offline login and pam_allow_groups all work.

  • Intune compliance is possible too, but I’m not going the Intune way.


My conclusion so far: if Entra ID is your identity provider, Himmelblau gives you Linux laptops that behave like Windows ones, and users barely notice the difference (I know, bold statement but it's close). The price is that every laptop talks to Entra directly. Whether that's a problem depends on how seriously you take the word "sovereignty".


What's next


Part 4 puts Keycloak between the laptop and Entra ID.


How do you handle Linux logins in your organization today: local accounts, a directory, or already something with Entra ID? Let me know in the comments.


Written with the help of AI, because I’m a busy guy. It uses a writing skill built entirely on me, following the second brain concept: my blogs, my notes, my frustrations, the occasional moment of me crying in despair, and my way of explaining things. The storylines are mine, and every issue and gotcha described here is real and happened on my own machines.

 
 
 

Opmerkingen


Post: Blog2_Post
bottom of page