<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[The Offline Engineer]]></title><description><![CDATA[The Offline Engineer]]></description><link>https://blog.benmantzur.com</link><generator>RSS for Node</generator><lastBuildDate>Thu, 14 May 2026 22:25:57 GMT</lastBuildDate><atom:link href="https://blog.benmantzur.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[When Kerberos Authenticates Users but Can’t Tell You Who They Are]]></title><description><![CDATA[Kerberos authentication worked.
Apache accepted the ticket.
The backend received the request.
And yet, the application had no idea which user was calling it, or which groups they belonged to.
This post documents how I built a Kerberos-authenticated A...]]></description><link>https://blog.benmantzur.com/when-kerberos-authenticates-users-but-cant-tell-you-who-they-are</link><guid isPermaLink="true">https://blog.benmantzur.com/when-kerberos-authenticates-users-but-cant-tell-you-who-they-are</guid><category><![CDATA[Security]]></category><category><![CDATA[Kerberos]]></category><category><![CDATA[apache]]></category><category><![CDATA[SSO]]></category><category><![CDATA[SSO - Single Sign-On]]></category><category><![CDATA[dbus]]></category><category><![CDATA[NSS]]></category><category><![CDATA[#sssd]]></category><dc:creator><![CDATA[Ben Mantzur]]></dc:creator><pubDate>Sun, 28 Dec 2025 19:45:11 GMT</pubDate><content:encoded><![CDATA[<p>Kerberos authentication worked.</p>
<p>Apache accepted the ticket.</p>
<p>The backend received the request.</p>
<p>And yet, the application had <strong>no idea which user was calling it, or which groups they belonged to</strong>.</p>
<p>This post documents how I built a <strong>Kerberos-authenticated Apache reverse proxy</strong> that <strong>correctly propagates user identity and AD group membership</strong> to backend services — and why doing this requires <strong>SSSD, NSS, and DBus</strong>, not Kerberos alone.</p>
<p>This is not a tutorial.</p>
<p>It’s a systems explanation for people who already run real infrastructure.</p>
<h2 id="heading-the-problem-kerberos-auth-identity-context"><strong>The Problem: Kerberos Auth ≠ Identity Context</strong></h2>
<p>Kerberos answers <strong>one question only</strong>:</p>
<blockquote>
<p>“Is this client allowed to authenticate as this principal?”</p>
</blockquote>
<p>That’s it.</p>
<p>When Apache validates a Kerberos ticket, it gets:</p>
<ul>
<li><p>a <strong>principal name</strong> (e.g. user@REALM)</p>
</li>
<li><p>proof that the client is authenticated</p>
</li>
</ul>
<p>What it <strong>does NOT get</strong>:</p>
<ul>
<li><p>Unix username mapping</p>
</li>
<li><p>Group membership</p>
</li>
<li><p>LDAP attributes</p>
</li>
<li><p>Authorization context</p>
</li>
</ul>
<p>Kerberos is <strong>authentication</strong>, not <strong>identity resolution</strong>.</p>
<p>And backend services almost never want a Kerberos principal — they want:</p>
<ul>
<li><p>a username</p>
</li>
<li><p>group membership</p>
</li>
<li><p>roles</p>
</li>
</ul>
<p>That gap is where things usually break.</p>
<h2 id="heading-why-apache-alone-is-not-enough"><strong>Why Apache Alone Is Not Enough</strong></h2>
<p>Apache with mod_auth_gssapi can authenticate a request and set:</p>
<p>REMOTE_USER=user</p>
<pre><code class="lang-text">REMOTE_USER=user
into:
username + UID + GIDs + group names
</code></pre>
<p>And it does so in a way that:</p>
<ul>
<li><p>is cached</p>
</li>
<li><p>is secure</p>
</li>
<li><p>survives temporary directory outages</p>
</li>
</ul>
<p>Without SSSD, Apache has no reliable way to answer:</p>
<blockquote>
<p>“What groups is this user in?”</p>
</blockquote>
<h2 id="heading-why-dbus-is-involved"><strong>Why DBus Is Involved</strong></h2>
<p>Apache cannot just “ask SSSD” directly.</p>
<p>SSSD exposes identity data through <strong>IFP (InfoPipe)</strong>, which is accessed via <strong>DBus</strong>.</p>
<p>So the real call chain looks like this:</p>
<pre><code class="lang-text">Apache
  → mod_lookup_identity
    → DBus
      → SSSD IFP / NSS
        → Active Directory
</code></pre>
<p>This design matters because:</p>
<ul>
<li><p>SSSD runs as root</p>
</li>
<li><p>Apache does not</p>
</li>
<li><p>DBus enforces access control</p>
</li>
<li><p>identity lookups remain isolated and safe</p>
</li>
</ul>
<p>When DBus or IFP is misconfigured, group lookup silently fails — even though authentication succeeds.</p>
<p>This is one of the most misleading failure modes in the stack.</p>
<h2 id="heading-why-kerberos-cannot-provide-groups"><strong>Why Kerberos Cannot Provide Groups</strong></h2>
<p>This is important enough to state clearly</p>
<blockquote>
<p>Kerberos <strong>cannot</strong> provide group membership.</p>
</blockquote>
<p>Kerberos tickets do not carry:</p>
<ul>
<li><p>LDAP attributes</p>
</li>
<li><p>AD group lists</p>
</li>
<li><p>authorization data usable by applications</p>
</li>
</ul>
<p>Some environments use <strong>PAC data</strong> inside Kerberos tickets, but:</p>
<ul>
<li><p>Apache does not expose it cleanly</p>
</li>
<li><p>It is not portable</p>
</li>
<li><p>It still does not replace identity resolution</p>
</li>
</ul>
<p>So even with a valid Kerberos ticket, <strong>you still need LDAP/SSSD</strong> to answer authorization questions.</p>
<h2 id="heading-how-this-project-solves-the-problem"><strong>How This Project Solves the Problem</strong></h2>
<p>This setup intentionally separates responsibilities:</p>
<h3 id="heading-1-kerberos-authentication"><strong>1. Kerberos (Authentication)</strong></h3>
<ul>
<li><p>mod_auth_gssapi</p>
</li>
<li><p>validates the client ticket</p>
</li>
<li><p>establishes trust</p>
</li>
<li><p>sets REMOTE_USER</p>
</li>
</ul>
<h3 id="heading-2-sssd-nss-identity-resolution"><strong>2. SSSD + NSS (Identity Resolution)</strong></h3>
<ul>
<li><p>resolves username to UID/GIDs</p>
</li>
<li><p>fetches AD group membership</p>
</li>
<li><p>caches results</p>
</li>
</ul>
<h3 id="heading-3-dbus-modlookupidentity-bridging"><strong>3. DBus + mod_lookup_identity (Bridging)</strong></h3>
<ul>
<li><p>Apache queries SSSD safely</p>
</li>
<li><p>group information becomes available inside Apache</p>
</li>
</ul>
<h3 id="heading-4-apache-headers-propagation"><strong>4. Apache Headers (Propagation)</strong></h3>
<ul>
<li><p>identity is forwarded as headers:</p>
<ul>
<li><p>X-User</p>
</li>
<li><p>X-User-Groups</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-5-backend-authorization"><strong>5. Backend (Authorization)</strong></h3>
<ul>
<li><p>backend receives identity context</p>
</li>
<li><p>no Kerberos awareness required</p>
</li>
<li><p>no directory access required</p>
</li>
</ul>
<p>This keeps the backend simple and stateless.</p>
<h2 id="heading-why-headers-are-the-right-boundary"><strong>Why Headers Are the Right Boundary</strong></h2>
<p>Instead of:</p>
<ul>
<li><p>embedding Kerberos in every service</p>
</li>
<li><p>exposing LDAP credentials everywhere</p>
</li>
<li><p>duplicating identity logic</p>
</li>
</ul>
<p>The proxy becomes the <strong>identity boundary</strong>.</p>
<p>Backend services only need to trust:</p>
<ul>
<li><p>the proxy</p>
</li>
<li><p>the headers it injects</p>
</li>
</ul>
<p>This is intentional, auditable, and scalable.</p>
<h2 id="heading-see-the-working-configuration"><strong>See the Working Configuration</strong></h2>
<p>The full, <strong>sanitized, working configuration</strong> described in this post is available here:</p>
<p>👉 <strong>GitHub repository:</strong></p>
<p><a target="_blank" href="https://github.com/pash10/kerberos-apache-SSO-AD-reverse-proxy">https://github.com/pash10/kerberos-apache-SSO-AD-reverse-proxy</a></p>
<p>The repository includes:</p>
<ul>
<li><p>A complete Apache configuration for Kerberos-authenticated reverse proxying</p>
</li>
<li><p>SSSD + NSS integration examples</p>
</li>
<li><p>Dockerfile and Compose setup</p>
</li>
<li><p>Minimal documentation describing scope and assumptions</p>
</li>
</ul>
<p>This repository is intentionally <strong>artifact-first</strong>.</p>
<p>It is not a tutorial and does not attempt to abstract away the underlying systems.</p>
]]></content:encoded></item></channel></rss>