<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Identity &amp; Authentication Archives - Linuxcent</title>
	<atom:link href="https://linuxcent.com/category/identity-authentication/feed/" rel="self" type="application/rss+xml" />
	<link>https://linuxcent.com/category/identity-authentication/</link>
	<description>Infrastructure security, from the kernel up.</description>
	<lastBuildDate>Wed, 13 May 2026 05:35:44 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0.4</generator>

<image>
	<url>https://linuxcent.com/wp-content/uploads/2026/04/favicon-512x512-1-150x150.png</url>
	<title>Identity &amp; Authentication Archives - Linuxcent</title>
	<link>https://linuxcent.com/category/identity-authentication/</link>
	<width>32</width>
	<height>32</height>
</image> 
<site xmlns="com-wordpress:feed-additions:1">211632295</site>	<item>
		<title>Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification</title>
		<link>https://linuxcent.com/zero-trust-identity-spiffe-spire/</link>
					<comments>https://linuxcent.com/zero-trust-identity-spiffe-spire/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 09 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Kubernetes]]></category>
		<category><![CDATA[mTLS]]></category>
		<category><![CDATA[SPIFFE]]></category>
		<category><![CDATA[SPIRE]]></category>
		<category><![CDATA[Zero Trust]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1808</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span>Zero Trust identity: SPIFFE workload identity, SPIRE attestation, mTLS service mesh, PAM tools, and continuous verification — from /etc/passwd to cryptographic trust.</p>
<p>The post <a href="https://linuxcent.com/zero-trust-identity-spiffe-spire/">Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 13</em><br />
<a href="/entra-id-linux-login/">EP12: Entra ID + Linux</a> → <strong>EP13</strong></p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Zero Trust means &#8220;never trust, always verify&#8221; — identity is verified continuously, not just at login time; network location provides no implicit trust</li>
<li>Human identity (users) and workload identity (services, pods, jobs) are separate problems — LDAP/Kerberos/OIDC solve the human side; SPIFFE/SPIRE solve the workload side</li>
<li>SPIFFE (Secure Production Identity Framework For Everyone) defines a standard for workload identity — a SPIFFE ID is a URI like <code class="" data-line="">spiffe://corp.com/ns/prod/sa/payments-svc</code></li>
<li>SPIRE (SPIFFE Runtime Environment) issues short-lived X.509 SVIDs (SPIFFE Verifiable Identity Documents) to workloads — certificates that rotate automatically, every hour</li>
<li>mTLS (mutual TLS) is how workloads prove identity to each other — both sides present certificates; no passwords, no API keys</li>
<li>The evolution: <code class="" data-line="">/etc/passwd</code> (1970) → NIS → LDAP → Kerberos → SAML → OIDC → SPIFFE/SPIRE — the problem has always been the same; the trust boundary keeps moving outward</li>
</ul>
<hr />
<h2 id="the-big-picture-from-etcpasswd-to-zero-trust">The Big Picture: From /etc/passwd to Zero Trust</h2>
<pre><code class="" data-line="">1970s  /etc/passwd              ← trust: the local machine
       One machine, one user list

1984   NIS / Yellow Pages       ← trust: the local network
       Centralized, but cleartext, flat

1993   LDAP                     ← trust: the directory server
       Hierarchical, scalable, encrypted (eventually)

1988   Kerberos                 ← trust: the KDC
       Tickets instead of passwords, network-wide

2002   SAML                     ← trust: the IdP assertion
       Identity crosses the internet

2014   OIDC / OAuth2            ← trust: the JWT signature
       API-native, mobile-native, developer-native

2017   SPIFFE / SPIRE           ← trust: the workload certificate
       Automated identity for services, not humans

2026   Zero Trust               ← trust: nothing, verify everything
       Continuous verification, short-lived credentials,
       device posture, behavioral signals
</code></pre>
<p>EP01 of this series started with the chaos of per-machine <code class="" data-line="">/etc/passwd</code>. This episode — EP13 — closes the loop: from that chaos to a model where identity is verified continuously, credentials expire in hours not years, and the network provides no implicit trust.</p>
<hr />
<h2 id="the-assumption-that-zero-trust-rejects">The Assumption That Zero Trust Rejects</h2>
<p>Traditional security assumed: if you&#8217;re on the internal network, you&#8217;re trusted. A VPN user was treated as equivalent to someone at a desk in the office. A service running on the same Kubernetes node as another service was implicitly trusted.</p>
<p>That assumption broke in practice:</p>
<ul>
<li>Compromised VPN credentials gave attackers full internal access</li>
<li>Lateral movement after initial compromise was easy — once inside, everything trusted you</li>
<li>Perimeter-based security had no visibility into east-west traffic (service-to-service)</li>
</ul>
<p>Zero Trust inverts the model: <strong>the network provides no trust.</strong> Every access request is verified — user or service, internal or external, first request or hundredth. Trust is dynamic, contextual, and short-lived.</p>
<hr />
<h2 id="human-zero-trust-continuous-verification">Human Zero Trust: Continuous Verification</h2>
<p>For human users, Zero Trust extends OIDC and Conditional Access:</p>
<p><strong>Short-lived tokens.</strong> Access tokens expire in 1 hour (OIDC standard). Refresh tokens are revocable. A user who is terminated can have their refresh tokens revoked in Entra ID — the next time their app tries to use the refresh token, it fails. The maximum blast radius of a stolen token is bounded by its lifetime.</p>
<p><strong>Device posture.</strong> The device the user authenticates from is part of the identity assertion. Conditional Access can require: device is managed (Intune-enrolled), device is compliant (no malware, full-disk encryption enabled, OS patched). A valid user credential from an unmanaged device is denied.</p>
<p><strong>Behavioral signals.</strong> Entra ID Identity Protection and similar systems analyze login patterns — unusual location, impossible travel (login from Mumbai, then New York 5 minutes later), unfamiliar device. High-risk sign-ins trigger step-up authentication or are blocked automatically.</p>
<p><strong>Privileged Access Management (PAM).</strong> For privileged operations (production shell access, AD admin), Zero Trust adds time-bounded just-in-time access:</p>
<pre><code class="" data-line="">Request:  &quot;I need admin access to db01.corp.com for 2 hours to investigate an incident&quot;
Approval: Manager approves via Slack/email/ticketing system
Grant:    Temporary role assignment or password checkout from the PAM vault
Access:   User SSHes with a one-time or time-limited credential
Expire:   Credential automatically revoked after 2 hours
Audit:    Full session recording available for review
</code></pre>
<p>CyberArk, BeyondTrust, and HashiCorp Vault implement this model. Vault&#8217;s SSH Secrets Engine issues short-lived SSH certificates:</p>
<pre><code class="" data-line=""># Request a signed SSH certificate (valid 30 minutes)
vault ssh \
  -role=prod-admin \
  -mode=ca \
  -mount-point=ssh-client-signer \
  vamshi@db01.corp.com

# Vault issues a certificate signed by the server&#039;s trusted CA
# sshd on db01 trusts that CA — no authorized_keys needed
# Certificate expires in 30 minutes — no cleanup required
</code></pre>
<hr />
<h2 id="workload-identity-the-non-human-problem">Workload Identity: The Non-Human Problem</h2>
<p>Services don&#8217;t have passwords they can type. A microservice calling another microservice needs to prove its identity — but you can&#8217;t give a Kubernetes pod a static API key (it&#8217;ll be in a config file, in a git repo, or in a crash dump within 6 months).</p>
<p>Workload identity solves this with <strong>short-lived, automatically rotated certificates</strong> — the service&#8217;s identity is its certificate, issued by a trusted CA, expiring in minutes to hours.</p>
<pre><code class="" data-line="">Traditional:                     Zero Trust:
  payments-svc → orders-svc        payments-svc → orders-svc
  Authentication: API key           Authentication: mTLS (X.509 cert)
  &quot;Bearer sk_live_abc123&quot;           cert: spiffe://corp.com/ns/prod/sa/payments-svc
  Rotation: manual (rarely done)    Rotation: automatic, every hour
  Revocation: change the key        Revocation: cert expires; new cert issued
  Audit: &quot;API key was used&quot;         Audit: &quot;spiffe://payments-svc → spiffe://orders-svc&quot;
</code></pre>
<hr />
<h2 id="spiffe-the-standard">SPIFFE: The Standard</h2>
<p>SPIFFE (Secure Production Identity Framework For Everyone) defines what a workload identity looks like. The core concept is the SPIFFE ID — a URI in the format:</p>
<pre><code class="" data-line="">spiffe://&lt;trust-domain&gt;/&lt;workload-path&gt;

Examples:
  spiffe://corp.com/ns/prod/sa/payments-svc
  spiffe://corp.com/region/us-east/service/auth-api
  spiffe://corp.com/k8s/cluster-prod/namespace/payments/pod/payments-svc-abc123
</code></pre>
<p>The trust domain (<code class="" data-line="">corp.com</code>) is the organizational boundary. The path is the workload identifier — typically encoding namespace, service account, or cluster information.</p>
<p>A SPIFFE ID is embedded in an SVID (SPIFFE Verifiable Identity Document) — either an X.509 certificate (X.509-SVID) or a JWT (JWT-SVID). The X.509-SVID is the standard form: the SPIFFE ID appears in the certificate&#8217;s Subject Alternative Name (SAN) field.</p>
<pre><code class="" data-line="">X.509 Certificate (SVID):
  Subject: CN=payments-svc
  SAN: URI=spiffe://corp.com/ns/prod/sa/payments-svc
  Validity: 1 hour
  Issuer: SPIRE Intermediate CA
  Signed by: corp.com trust bundle
</code></pre>
<p>Any service that has the <code class="" data-line="">corp.com</code> trust bundle (the CA certificate chain) can verify that a certificate with <code class="" data-line="">spiffe://corp.com/...</code> in the SAN was issued by the authorized CA for that trust domain.</p>
<hr />
<h2 id="spire-the-runtime">SPIRE: The Runtime</h2>
<p>SPIRE (SPIFFE Runtime Environment) is the reference implementation that issues SVIDs to workloads.</p>
<pre><code class="" data-line="">SPIRE Server
  ├── Node attestation: verifies the identity of the node/VM
  │   (AWS instance identity document, GCP service account, k8s node SA)
  └── Workload attestation: verifies the identity of the process
      (Kubernetes SA, Unix UID/GID, Docker container labels)
         │
         │ issues X.509 SVIDs (short-lived, auto-rotated)
         ▼
SPIRE Agent (runs on every node)
         │
         │ SPIFFE Workload API (Unix socket)
         ▼
Workload (your service)
  → gets its own certificate
  → gets the trust bundle (CA certs of trusted domains)
  → uses cert for mTLS with other services
</code></pre>
<p>The workload fetches its identity via the Workload API socket — no environment variables, no file mounts. The SPIRE Agent pushes new certificates before the old ones expire. Rotation is transparent to the workload.</p>
<pre><code class="" data-line=""># On a node with SPIRE Agent running:
# Fetch the SVID for the current workload
spire-agent api fetch x509 \
  -socketPath /run/spire/sockets/agent.sock

# Output shows:
# SPIFFE ID: spiffe://corp.com/ns/prod/sa/payments-svc
# Certificate: (PEM)
# Trust bundle: (PEM of issuing CA chain)
# Expires: 2026-04-27T02:00:00Z (1 hour from now)
</code></pre>
<hr />
<h2 id="mtls-both-sides-show-id">mTLS: Both Sides Show ID</h2>
<p>Mutual TLS (mTLS) is what makes SPIFFE useful operationally. In standard TLS, only the server presents a certificate — the client just verifies it. In mTLS, both sides present certificates. Both sides verify the other&#8217;s certificate against the trust bundle.</p>
<pre><code class="" data-line="">payments-svc → orders-svc connection:

TLS handshake:
  payments-svc presents: spiffe://corp.com/ns/prod/sa/payments-svc cert
  orders-svc presents:   spiffe://corp.com/ns/prod/sa/orders-svc cert

  Both verify:
    • cert signed by trusted CA (the corp.com SPIRE CA)
    • cert not expired
    • SPIFFE ID in SAN matches what&#039;s expected

  After handshake: encrypted channel, both sides verified
  Authorization: orders-svc checks its policy:
    &quot;is spiffe://corp.com/ns/prod/sa/payments-svc allowed to call /api/orders?&quot;
</code></pre>
<p>Service meshes (Istio, Linkerd, Consul Connect) implement mTLS transparently — the application doesn&#8217;t handle certificates; the sidecar proxy does. In Istio&#8217;s case, Citadel (now istiod) acts as the SPIFFE-compatible CA, issuing certificates to envoy sidecars. The application code doesn&#8217;t change.</p>
<hr />
<h2 id="open-policy-agent-authorization-after-identity">Open Policy Agent: Authorization After Identity</h2>
<p>Zero Trust separates identity from authorization. Once you know who the caller is (SPIFFE ID, OIDC token, user cert), a policy engine decides what they can do.</p>
<p>OPA (Open Policy Agent) is the standard for this:</p>
<pre><code class="" data-line=""># opa-policy.rego
package authz

# payments-svc can read orders; nothing else can write orders
allow {
  input.caller == &quot;spiffe://corp.com/ns/prod/sa/payments-svc&quot;
  input.method == &quot;GET&quot;
  startswith(input.path, &quot;/api/orders&quot;)
}

default allow = false
</code></pre>
<p>The service checks OPA on each request: &#8220;caller=X wants to do Y to Z — allowed?&#8221; OPA evaluates the policy and returns a decision. The policy is version-controlled, tested, and deployed independently of the service.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;Zero Trust means no trust.&#8221;</strong> Zero Trust means trust is earned dynamically through verification, not granted by network location. A verified user with a valid, compliant device and MFA is trusted — for the scope and duration of the verified session. The &#8220;zero&#8221; refers to implicit trust, not trust itself.</p>
<p><strong>&#8220;SPIFFE replaces OIDC.&#8221;</strong> SPIFFE is for workload (service) identity. OIDC is for human (user) identity. They complement each other — a service has a SPIFFE identity; a user has an OIDC identity; the authorization layer accepts both.</p>
<p><strong>&#8220;mTLS is complex to implement.&#8221;</strong> With a service mesh (Istio, Linkerd), mTLS is transparent — the sidecar handles it. Without a service mesh, the application needs to use the SPIFFE Workload API. The complexity is real but manageable, especially compared to the alternative of static API keys.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>Zero Trust extends IAM to workloads (SPIFFE) and continuous verification (short-lived tokens, device posture) — it&#8217;s the current frontier of identity architecture</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The separation of identity (SPIFFE ID), authentication (mTLS), and authorization (OPA) is a clean architectural decomposition that scales to complex multi-service environments</td>
</tr>
<tr>
<td>CISSP Domain 4: Communications and Network Security</td>
<td>mTLS encrypts and authenticates every service-to-service connection — it eliminates the assumption that east-west traffic on the internal network is safe</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>Zero Trust is a risk management posture — it accepts that perimeter breach is inevitable and limits blast radius through continuous verification and least-privilege</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Zero Trust rejects network-based implicit trust — every request is verified regardless of source</li>
<li>Human identity: short-lived OIDC tokens, device posture checks, Conditional Access, JIT privileged access (Vault, CyberArk)</li>
<li>Workload identity: SPIFFE IDs in X.509 certificates, issued by SPIRE, rotated automatically every hour — no static API keys</li>
<li>mTLS lets services verify each other&#8217;s identity at the TLS layer — service meshes (Istio, Linkerd) implement it transparently</li>
<li>OPA handles authorization after identity is established — who you are ≠ what you can do</li>
<li>The series arc: <code class="" data-line="">/etc/passwd</code> → NIS → LDAP → Kerberos → SAML → OIDC → SPIFFE/SPIRE — the problem has always been &#8220;how do you know who someone is, at scale, without trusting the network?&#8221; The answer keeps getting better.</li>
</ul>
<hr />
<p>What does identity look like at your organization — still static API keys and shared service accounts, or moving toward SPIFFE and short-lived credentials? <img src="https://s.w.org/images/core/emoji/17.0.2/72x72/1f447.png" alt="👇" class="wp-smiley" style="height: 1em; max-height: 1em;" /></p>
<hr />
<p><em>The Identity Stack: From LDAP to Zero Trust — 13 episodes complete.</em></p>
<p><a href="/what-is-ldap/">Start from EP01: What Is LDAP →</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&amp;linkname=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fzero-trust-identity-spiffe-spire%2F&#038;title=Zero%20Trust%20Identity%3A%20SPIFFE%2C%20SPIRE%2C%20mTLS%2C%20and%20Continuous%20Verification" data-a2a-url="https://linuxcent.com/zero-trust-identity-spiffe-spire/" data-a2a-title="Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification"></a></p><p>The post <a href="https://linuxcent.com/zero-trust-identity-spiffe-spire/">Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/zero-trust-identity-spiffe-spire/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1808</post-id>	</item>
		<item>
		<title>Entra ID Linux Login: SSH Authentication with Azure AD Credentials</title>
		<link>https://linuxcent.com/entra-id-linux-login/</link>
					<comments>https://linuxcent.com/entra-id-linux-login/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Sat, 09 May 2026 02:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Azure AD]]></category>
		<category><![CDATA[Cloud Security]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[SSH]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1805</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Enable Entra ID SSH login on Linux: aad-auth package, Conditional Access Policies, pam_aad stack, and Entra ID Connect for hybrid on-prem sync.</p>
<p>The post <a href="https://linuxcent.com/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 12</em><br />
<a href="/identity-providers-explained/">EP11: Identity Providers</a> → <strong>EP12</strong> → <a href="/zero-trust-identity-spiffe-spire/">EP13: Zero Trust Identity</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Entra ID (Azure AD) Linux login lets you SSH into a VM using your Azure AD credentials — no local Linux accounts, no SSH keys to distribute</li>
<li>The stack: <code class="" data-line="">aad-auth</code> package + <code class="" data-line="">pam_aad.so</code> + SSSD — Azure authenticates via OIDC device code flow or password, then maps the identity to a local Linux UID</li>
<li>Entra ID is not AD — it&#8217;s OIDC/OAuth2 native, with no LDAP and no Kerberos (unless you add Azure AD DS, a separate managed service)</li>
<li>Conditional Access Policies can gate Linux logins — MFA, device compliance, location restrictions — the same policies as for web apps</li>
<li>Two login modes: interactive (browser-based device code, for non-Azure VMs) and integrated (Azure IMDS-based, for Azure VMs)</li>
<li>Required roles: <code class="" data-line="">Virtual Machine Administrator Login</code> or <code class="" data-line="">Virtual Machine User Login</code> on the VM — IAM, not local sudoers</li>
</ul>
<hr />
<h2 id="the-big-picture-how-entra-id-linux-login-works">The Big Picture: How Entra ID Linux Login Works</h2>
<pre><code class="" data-line="">User: ssh vamshi@vm.corp.azure.com

  sshd on Linux VM
      │
      ▼
  PAM (/etc/pam.d/sshd)
      │
      ├── pam_aad.so (auth)
      │     │
      │     │  OIDC device code flow:
      │     │  &quot;Go to microsoft.com/devicelogin and enter code ABCD-1234&quot;
      │     │  User authenticates in browser with MFA
      │     │  Entra ID issues id_token + access_token
      │     ▼
      │   pam_aad validates token:
      │     • signature (JWKS from Entra ID)
      │     • tenant ID (iss claim)
      │     • VM resource audience (aud claim)
      │     • group membership (groups claim)
      │
      └── pam_mkhomedir (session)
            Creates /home/vamshi@corp.com on first login

  Shell session created
  whoami → vamshi_corp_com (sanitized UPN for Linux username)
</code></pre>
<p>EP11 mapped the IdP landscape. This episode gets specific: Entra ID and Linux. Understanding this matters because Entra ID is increasingly where enterprise identities live, and cloud VMs that SSH into with local accounts are an operational and security liability.</p>
<hr />
<h2 id="entra-id-vs-active-directory-whats-different">Entra ID vs Active Directory: What&#8217;s Different</h2>
<p>This distinction matters before configuring anything.</p>
<table>
<thead>
<tr>
<th></th>
<th>Active Directory (on-prem)</th>
<th>Entra ID (cloud)</th>
</tr>
</thead>
<tbody>
<tr>
<td>Protocol</td>
<td>LDAP + Kerberos</td>
<td>OIDC + OAuth2</td>
</tr>
<tr>
<td>Directory queries</td>
<td><code class="" data-line="">ldapsearch</code></td>
<td>Microsoft Graph API</td>
</tr>
<tr>
<td>Linux join</td>
<td><code class="" data-line="">realm join</code> (adcli + SSSD)</td>
<td><code class="" data-line="">aad-auth</code> package</td>
</tr>
<tr>
<td>Authentication</td>
<td>Kerberos tickets</td>
<td>JWT tokens</td>
</tr>
<tr>
<td>Group policy</td>
<td>GPO via Sysvol</td>
<td>Conditional Access + Intune</td>
</tr>
<tr>
<td>Network requirement</td>
<td>DC reachable on LAN/VPN</td>
<td>HTTPS to login.microsoftonline.com</td>
</tr>
</tbody>
</table>
<p>Entra ID has no LDAP interface and no Kerberos realm. You cannot run <code class="" data-line="">ldapsearch</code> against it. You cannot <code class="" data-line="">kinit</code> to it. The authentication protocol is entirely OIDC/OAuth2 — the same protocol your browser uses to &#8220;Login with Microsoft.&#8221;</p>
<p>If you need LDAP and Kerberos from Azure, that&#8217;s <strong>Azure AD Domain Services</strong> — a separate managed service that Microsoft runs, which does speak LDAP and Kerberos. It&#8217;s not Entra ID; it&#8217;s a managed AD replica in Azure. EP12 covers the Entra ID path — the modern, protocol-native approach.</p>
<hr />
<h2 id="prerequisites">Prerequisites</h2>
<pre><code class="" data-line=""># Azure side:
# 1. The VM&#039;s managed identity must be enabled (System-assigned)
# 2. Two Entra ID roles assigned on the VM resource:
#    - &quot;Virtual Machine Administrator Login&quot; (for sudo access)
#    - &quot;Virtual Machine User Login&quot; (for regular access)
# 3. Conditional Access policies that apply to the VM login scope

# VM side (Ubuntu 20.04+ / RHEL 8+):
# Install the aad-auth package (Microsoft-maintained)
curl -sSL https://packages.microsoft.com/keys/microsoft.asc \
  | gpg --dearmor -o /usr/share/keyrings/microsoft.gpg
echo &quot;deb [signed-by=/usr/share/keyrings/microsoft.gpg] \
  https://packages.microsoft.com/ubuntu/22.04/prod jammy main&quot; \
  &gt; /etc/apt/sources.list.d/microsoft.list
apt-get update &amp;&amp; apt-get install -y aad-auth
</code></pre>
<hr />
<h2 id="configuration">Configuration</h2>
<pre><code class="" data-line=""># Configure the aad-auth package
aad-auth configure \
  --tenant-id 12345678-1234-1234-1234-123456789abc \
  --app-id 87654321-4321-4321-4321-cba987654321

# This writes /etc/aad.conf:
# [aad]
# tenant_id = 12345678-...
# app_id = 87654321-...
# version = 1

# Verify the PAM configuration was updated
grep pam_aad /etc/pam.d/common-auth
# auth [success=1 default=ignore] pam_aad.so
</code></pre>
<p>The <code class="" data-line="">aad-auth</code> package installs <code class="" data-line="">pam_aad.so</code> and configures PAM automatically. It also modifies <code class="" data-line="">/etc/nsswitch.conf</code> to add <code class="" data-line="">aad</code> as a source for <code class="" data-line="">passwd</code> lookups — so <code class="" data-line="">getent passwd vamshi@corp.com</code> works after the first login.</p>
<hr />
<h2 id="the-login-flow">The Login Flow</h2>
<h3 id="on-an-azure-vm-integrated-mode">On an Azure VM (Integrated mode)</h3>
<p>Azure VMs have access to the Instance Metadata Service (IMDS) at <code class="" data-line="">169.254.169.254</code>. <code class="" data-line="">pam_aad</code> uses the VM&#8217;s managed identity to get a token from IMDS, which proves the VM is trusted, then validates the user&#8217;s token against the tenant.</p>
<pre><code class="" data-line=""># User SSHes with username as UPN (user@tenant.onmicrosoft.com or user@corp.com)
ssh vamshi@corp.com@vm.eastus.cloudapp.azure.com

# Or use the short form if the tenant is configured:
ssh vamshi@corp.com@vm.eastus.cloudapp.azure.com
</code></pre>
<p>On first connection, <code class="" data-line="">pam_aad</code> initiates the device code flow:</p>
<pre><code class="" data-line="">To sign in, use a web browser to open https://microsoft.com/devicelogin
and enter the code ABCD-1234 to authenticate.
</code></pre>
<p>The user opens the URL in any browser (on any device), enters the code, and authenticates with their Entra ID credentials + MFA. The SSH session gets a token. Subsequent logins within the token cache TTL skip the device code step.</p>
<h3 id="username-format-on-the-linux-system">Username format on the Linux system</h3>
<p>Entra ID usernames (UPNs) contain <code class="" data-line="">@</code> — not valid in Linux usernames. <code class="" data-line="">aad-auth</code> sanitizes the UPN:</p>
<pre><code class="" data-line="">vamshi@corp.com → vamshi_corp_com    (default)
# or, with shorter_username enabled in /etc/aad.conf:
vamshi@corp.com → vamshi
</code></pre>
<p>The UID is derived from the Azure AD Object ID (a deterministic hash) — stable across logins, same UID on every VM in the tenant.</p>
<hr />
<h2 id="conditional-access-for-linux-logins">Conditional Access for Linux Logins</h2>
<p>Conditional Access Policies in Entra ID apply to Linux VM logins the same way they apply to web app logins.</p>
<pre><code class="" data-line="">Policy: Require MFA for Linux VM Login
  Conditions:
    Cloud apps: &quot;Azure Linux Virtual Machine Sign-In&quot;
    Users: All users (or specific groups)
  Grant:
    Require multi-factor authentication
    Require compliant device (optional)
</code></pre>
<p>With this policy, every SSH login triggers MFA — regardless of whether the client machine supports it. The MFA challenge appears in the device code flow (the browser window the user opens).</p>
<p>You can also enforce:<br />
&#8211; <strong>Location restrictions</strong> — only from corporate IP ranges<br />
&#8211; <strong>Device compliance</strong> — device must be Intune-managed<br />
&#8211; <strong>Sign-in risk</strong> — block logins flagged as risky by Entra ID Identity Protection</p>
<p>This is the operational shift: Linux login security is now managed in the same Conditional Access policy engine as every other Entra ID-protected resource. No more per-machine PAM configuration for MFA.</p>
<hr />
<h2 id="role-based-access-who-can-log-in">Role-Based Access: Who Can Log In</h2>
<p>Access to the VM is controlled by Azure RBAC — not by local Linux groups or sudoers.</p>
<pre><code class="" data-line=""># Grant a user SSH access to the VM
az role assignment create \
  --assignee vamshi@corp.com \
  --role &quot;Virtual Machine User Login&quot; \
  --scope /subscriptions/SUB_ID/resourceGroups/RG/providers/Microsoft.Compute/virtualMachines/VM_NAME

# Grant admin (sudo) access
az role assignment create \
  --assignee vamshi@corp.com \
  --role &quot;Virtual Machine Administrator Login&quot; \
  --scope /subscriptions/SUB_ID/...
</code></pre>
<p><code class="" data-line="">Virtual Machine Administrator Login</code> maps to the <code class="" data-line="">sudo</code> group on the Linux VM. Users with this role get passwordless sudo. Users with <code class="" data-line="">Virtual Machine User Login</code> get a regular shell.</p>
<p>The mapping is enforced by <code class="" data-line="">pam_aad</code> checking the <code class="" data-line="">groups</code> claim in the token against the configured admin group. No <code class="" data-line="">/etc/sudoers.d/</code> files needed.</p>
<hr />
<h2 id="debugging-entra-id-linux-logins">Debugging Entra ID Linux Logins</h2>
<pre><code class="" data-line=""># Check aad-auth service status
systemctl status aad-auth

# View aad-auth logs
journalctl -u aad-auth -f

# Attempt a manual token validation (requires aad-auth debug mode)
aad-auth login --username vamshi@corp.com

# Check the local user cache
getent passwd vamshi_corp_com
# Returns if the user has logged in before

# Clear the local cache (forces re-authentication)
aad-auth clean-cache

# Verify Conditional Access isn&#039;t blocking (check Entra ID Sign-in logs)
# Azure Portal → Entra ID → Sign-in logs → filter by user + app &quot;Azure Linux VM Sign-In&quot;
</code></pre>
<p>The Entra ID Sign-in logs in the Azure Portal show every authentication attempt, the Conditional Access policies that evaluated, which ones passed/failed, and the exact failure reason. This is far more diagnostic than reading PAM logs.</p>
<hr />
<h2 id="entra-id-connect-bringing-on-prem-users-to-entra-id">Entra ID Connect: Bringing On-Prem Users to Entra ID</h2>
<p>For organizations with existing on-prem AD who want to enable Entra ID Linux login:</p>
<pre><code class="" data-line="">On-prem AD users → Entra ID Connect sync → Entra ID
                                                │
                                    Linux VM login (aad-auth)
</code></pre>
<p>Entra ID Connect is a Windows Server application that syncs users from on-prem AD to Entra ID every 30 minutes. Users authenticate against Entra ID (which validates against AD via Password Hash Sync, Pass-Through Authentication, or Federation). The Linux VM doesn&#8217;t know or care — it sees an Entra ID token.</p>
<p>With Password Hash Sync: password hashes (not plaintext) are synced to Entra ID — users authenticate directly in the cloud.<br />
With Pass-Through Authentication: Entra ID forwards authentication requests to an on-prem agent that validates against AD — no password hashes leave the datacenter.<br />
With Federation (AD FS / Entra ID as a relying party): Entra ID delegates authentication to AD FS — the most complex, the most on-prem control.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;Entra ID = Azure Active Directory = Active Directory.&#8221;</strong> Three different things. Active Directory: on-prem, LDAP+Kerberos. Azure AD (now Entra ID): cloud, OIDC+OAuth2. Azure AD Domain Services: managed AD replica in Azure, LDAP+Kerberos, not Entra ID.</p>
<p><strong>&#8220;You need Azure AD DS to join Linux to Azure.&#8221;</strong> Azure AD DS is the managed AD service. Entra ID Linux login (via aad-auth) is entirely separate and doesn&#8217;t require AD DS. You can authenticate Linux to Entra ID directly via OIDC.</p>
<p><strong>&#8220;The Linux username matches the Entra ID username.&#8221;</strong> The UPN is sanitized (<code class="" data-line="">@</code> → <code class="" data-line="">_</code>) to produce a valid Linux username. The canonical identity is the UPN or the Entra Object ID. Don&#8217;t hardcode the sanitized username in scripts.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>Entra ID Linux login centralizes Linux VM access in the same IAM system as all other enterprise resources — one policy engine, one audit log</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>Eliminating per-VM local accounts removes a class of credential management risk — no SSH keys to rotate, no local accounts to audit</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>Conditional Access Policies enforcing MFA on Linux logins reduce the risk of credential-based compromise of cloud VMs</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Entra ID Linux login uses OIDC device code flow — no LDAP, no Kerberos, no local Linux accounts</li>
<li><code class="" data-line="">aad-auth</code> package installs <code class="" data-line="">pam_aad.so</code> and handles the full authentication stack: token issuance, validation, user cache, UID mapping</li>
<li>VM access is controlled by Azure RBAC roles (<code class="" data-line="">Virtual Machine Administrator Login</code> / <code class="" data-line="">Virtual Machine User Login</code>) — not by sudoers files</li>
<li>Conditional Access Policies apply to Linux VM logins — MFA, device compliance, and location restrictions use the same engine as every other Entra ID app</li>
<li>Debugging starts in Entra ID Sign-in logs (Azure Portal), not in <code class="" data-line="">/var/log/auth.log</code></li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP12 showed how Entra ID enables Linux logins in the cloud. EP13 is the series closer: Zero Trust identity — what it means to verify identity continuously, how SPIFFE and SPIRE handle workload (non-human) identity, and where the stack goes from <code class="" data-line="">/etc/passwd</code> in 1970 to a Zero Trust policy engine in 2026.</p>
<p><em>Next: <a href="/zero-trust-identity-spiffe-spire/">Zero Trust Identity: SPIFFE, SPIRE, mTLS, and Continuous Verification</a></em></p>
<p>Get EP13 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&amp;linkname=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fentra-id-linux-login%2F&#038;title=Entra%20ID%20Linux%20Login%3A%20SSH%20Authentication%20with%20Azure%20AD%20Credentials" data-a2a-url="https://linuxcent.com/entra-id-linux-login/" data-a2a-title="Entra ID Linux Login: SSH Authentication with Azure AD Credentials"></a></p><p>The post <a href="https://linuxcent.com/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/entra-id-linux-login/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1805</post-id>	</item>
		<item>
		<title>Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</title>
		<link>https://linuxcent.com/identity-providers-explained/</link>
					<comments>https://linuxcent.com/identity-providers-explained/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Fri, 08 May 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Entra ID]]></category>
		<category><![CDATA[IAM]]></category>
		<category><![CDATA[Identity Provider]]></category>
		<category><![CDATA[Okta]]></category>
		<category><![CDATA[SAML]]></category>
		<category><![CDATA[SCIM]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1802</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>Identity providers explained: on-prem IdPs (AD FS, Keycloak), cloud IdPs (Okta, Entra ID), SCIM provisioning, SAML federation, and directory sync vs identity federation.</p>
<p>The post <a href="https://linuxcent.com/identity-providers-explained/">Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 11</em><br />
<a href="/saml-vs-oidc-vs-oauth2/">EP10: SAML/OIDC</a> → <strong>EP11</strong> → <a href="/entra-id-linux-login/">EP12: Entra ID + Linux</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>An Identity Provider (IdP) is the system that authenticates users and issues identity assertions (SAML assertions, OIDC tokens) to applications</li>
<li>On-prem IdPs: AD FS (Microsoft), Shibboleth (universities), Keycloak (open source), Ping Identity — they sit in front of AD and speak SAML/OIDC to cloud apps</li>
<li>Cloud IdPs: Okta, Entra ID (Azure AD), Google Workspace, Ping Identity Cloud — they are the directory and the authentication layer in one</li>
<li>Federation: IdPs can trust each other — a corporate IdP can delegate to a cloud IdP, or federate with a partner org&#8217;s IdP</li>
<li>SCIM (System for Cross-domain Identity Management) is provisioning, not authentication — it creates/updates/deactivates user accounts in target systems when the source directory changes</li>
<li>The key distinction: federation (authentication flow) vs directory sync (data copy) — they solve different problems and are often deployed together</li>
</ul>
<hr />
<h2 id="the-big-picture-where-idps-sit">The Big Picture: Where IdPs Sit</h2>
<pre><code class="" data-line="">                        On-prem Directory
                        (Active Directory / OpenLDAP / FreeIPA)
                               │
                               │ LDAP / Kerberos
                               ▼
                         Identity Provider
                         ┌──────────────────────────────────┐
                         │  AD FS / Keycloak / Okta /       │
                         │  Entra ID Connect / Shibboleth   │
                         │                                  │
                         │  Speaks: SAML 2.0 + OIDC + OAuth2│
                         └────────────────┬─────────────────┘
                                          │ assertions / tokens
                      ┌───────────────────┼───────────────────┐
                      ▼                   ▼                   ▼
               Salesforce          GitHub Enterprise      AWS IAM
               (SAML SP)           (OIDC RP)              (OIDC)
</code></pre>
<p>EP10 covered the protocols. This episode covers the systems — what an IdP actually does, how the major ones differ, and how they connect to each other through federation and SCIM.</p>
<hr />
<h2 id="on-premises-identity-providers">On-Premises Identity Providers</h2>
<h3 id="ad-fs-active-directory-federation-services">AD FS (Active Directory Federation Services)</h3>
<p>AD FS is Microsoft&#8217;s on-prem federation server — a Windows Server role that sits in front of Active Directory and speaks SAML 2.0 and OIDC to external applications.</p>
<p>What it does:<br />
&#8211; Authenticates users against AD (Kerberos/LDAP behind the scenes)<br />
&#8211; Issues SAML assertions and OIDC tokens to external SPs<br />
&#8211; Handles claims transformation: maps AD attributes to what the SP expects</p>
<p>What it doesn&#8217;t do well:<br />
&#8211; It&#8217;s Windows Server only<br />
&#8211; Configuration is complex (XML, certificates, claim rule language)<br />
&#8211; No built-in MFA (requires Azure MFA or a third-party provider)<br />
&#8211; Being deprecated in favor of Entra ID for most use cases</p>
<p>AD FS made sense when everything was on-prem. As workloads move to cloud, Entra ID Connect (a lighter sync agent) combined with Entra ID as the IdP replaces AD FS for most enterprises.</p>
<h3 id="keycloak">Keycloak</h3>
<p>Keycloak is the open-source IdP from Red Hat. It&#8217;s what FreeIPA uses for web-based OIDC/SAML SSO, and it&#8217;s widely deployed independently for organizations that want full control over their identity infrastructure.</p>
<pre><code class="" data-line=""># Run Keycloak in development mode (Docker)
docker run -p 8080:8080 \
  -e KEYCLOAK_ADMIN=admin \
  -e KEYCLOAK_ADMIN_PASSWORD=admin \
  quay.io/keycloak/keycloak:latest \
  start-dev

# Keycloak concepts:
# Realm     — an isolated namespace (like a tenant)
# Client    — an application that uses Keycloak for auth (SP/RP)
# User federation — connect Keycloak to an existing LDAP/AD directory
# Identity brokering — federate with external IdPs (Google, GitHub, another SAML IdP)
</code></pre>
<p>Keycloak reads users from AD/LDAP via its User Federation feature — it doesn&#8217;t replace the directory, it federates it. Users still live in AD; Keycloak issues SAML/OIDC tokens based on those users.</p>
<h3 id="shibboleth">Shibboleth</h3>
<p>Shibboleth is the dominant IdP in academia. Most universities run it. It&#8217;s SAML-native, designed for federation between institutions — a student can authenticate at their home university&#8217;s IdP and access resources at a partner institution.</p>
<hr />
<h2 id="cloud-identity-providers">Cloud Identity Providers</h2>
<h3 id="okta">Okta</h3>
<p>Okta is a cloud IdP + directory. It can:<br />
&#8211; Act as the primary user directory (storing users, credentials)<br />
&#8211; Connect to on-prem AD via the Okta Active Directory Agent (a lightweight sync service)<br />
&#8211; Federate with other IdPs (act as IdP or SP in a SAML/OIDC chain)<br />
&#8211; Enforce MFA, Adaptive Authentication, Device Trust</p>
<p>Okta&#8217;s Lifecycle Management handles provisioning: when a user is created/disabled in Okta (or synced from AD), Okta can automatically create/deactivate accounts in downstream SaaS apps — via SCIM or app-specific APIs.</p>
<h3 id="entra-id-azure-active-directory">Entra ID (Azure Active Directory)</h3>
<p>Entra ID is Microsoft&#8217;s cloud IdP. It&#8217;s both a directory (stores users, groups) and an IdP (issues tokens). For organizations running on-prem AD, Entra ID Connect syncs users from AD to Entra ID.</p>
<p>Entra ID is OIDC and OAuth2 native — it speaks SAML for legacy apps but JWT/OIDC for everything modern. Its OIDC implementation follows the standard closely; its token validation happens via <code class="" data-line="">/.well-known/openid-configuration</code> and the JWKS endpoint.</p>
<pre><code class="" data-line="">On-prem AD  →  Entra ID Connect (sync agent)  →  Entra ID (cloud)
                                                      │
                                              SAML / OIDC
                                                      │
                                            SaaS apps, Azure resources
</code></pre>
<h3 id="google-workspace">Google Workspace</h3>
<p>Google Workspace is Google&#8217;s combined directory + IdP. Google accounts are the users. Apps integrate via SAML or OIDC. Google&#8217;s OIDC implementation is one of the most widely used reference implementations — most OIDC libraries are tested against it.</p>
<hr />
<h2 id="federation-idps-trusting-each-other">Federation: IdPs Trusting Each Other</h2>
<p>Federation is the mechanism that lets IdPs delegate to each other. Two patterns:</p>
<h3 id="saml-federation-idp-to-idp">SAML Federation (IdP-to-IdP)</h3>
<p>Common in academia and partner integrations:</p>
<pre><code class="" data-line="">User at University A → requests resource at University B
                              │
                              │ doesn&#039;t know user
                              ▼
                    University B SP redirects to...
                    Discovery Service: &quot;which IdP are you from?&quot;
                              │
                              ▼
                    University A IdP authenticates user
                              │
                    Sends SAML assertion to University B SP
</code></pre>
<p>University B&#8217;s SP trusts University A&#8217;s IdP because both are members of a SAML federation (e.g., InCommon in the US, eduGAIN globally). The federation metadata aggregates all members&#8217; SAML metadata — certificates, endpoints — so members don&#8217;t have to manually configure each bilateral trust.</p>
<h3 id="oidc-identity-brokering">OIDC Identity Brokering</h3>
<p>Keycloak, Okta, and Entra ID can all act as identity brokers — they sit between the application and the actual authenticating IdP:</p>
<pre><code class="" data-line="">App (OIDC RP) → Keycloak (broker IdP) → Google / GitHub / SAML IdP
                                               │ authenticate
                                               ▼
                                      Keycloak receives assertion
                                      Maps external claims to local claims
                                      Issues OIDC token to app
</code></pre>
<p>The app only knows Keycloak. Keycloak handles the upstream IdP complexity.</p>
<hr />
<h2 id="scim-provisioning-authentication">SCIM: Provisioning ≠ Authentication</h2>
<p>SCIM (RFC 7644) is a REST API standard for user lifecycle management — creating, updating, and deactivating user accounts in a target system when changes happen in the source directory.</p>
<pre><code class="" data-line="">Source (Okta / Entra ID)           Target (Slack / GitHub / Jira)
         │                                    │
         │  SCIM 2.0 (REST + JSON)            │
         ├─ POST /Users  ─────────────────────► create user
         ├─ PATCH /Users/id ──────────────────► update attributes
         └─ DELETE /Users/id ─────────────────► deactivate account
</code></pre>
<p>SCIM is not SSO. A SCIM-provisioned user in Slack can log in to Slack — but the authentication still goes through the IdP (SAML/OIDC). SCIM ensures the account exists. The IdP proves the user&#8217;s identity.</p>
<p>Why both? Because SSO alone doesn&#8217;t create accounts in target systems — it just authenticates to them. If a user tries to log in to Slack for the first time via SSO, Slack needs an account to map them to. SCIM creates that account before the first login (Just-in-Time provisioning handles it at first login, but SCIM handles it in bulk and handles deprovisioning reliably).</p>
<p>Deprovisioning is where SCIM matters most. When an employee leaves, you disable them in Okta — SCIM deactivates their account in every connected app within minutes. Without SCIM, IT runs a manual checklist. Someone misses Jira. The ex-employee has access for three weeks.</p>
<hr />
<h2 id="directory-sync-vs-federation">Directory Sync vs Federation</h2>
<p>These are commonly confused:</p>
<p><strong>Directory sync</strong> — copy user data from source to target. Entra ID Connect copies users from on-prem AD to Entra ID. This is not authentication; it&#8217;s data replication. After sync, Entra ID has its own copy of the user record.</p>
<p><strong>Federation</strong> — delegate authentication to an external IdP. The target system doesn&#8217;t store credentials; it redirects to the IdP for authentication and trusts the assertion that comes back.</p>
<p>You often need both:<br />
&#8211; Sync: so the target system has the user record and can enforce policies (group membership, license assignment)<br />
&#8211; Federation: so the user authenticates against the source of truth (your IdP) rather than maintaining a separate password in every system</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;SCIM is an authentication protocol.&#8221;</strong> SCIM is a provisioning protocol. It creates and manages accounts. Authentication is SAML/OIDC. Both solve different parts of the identity lifecycle problem.</p>
<p><strong>&#8220;SSO means you only have one password.&#8221;</strong> SSO means you only authenticate once per session. The password still exists (at the IdP). SSO reduces the number of authentication events, not the number of credentials.</p>
<p><strong>&#8220;On-prem IdP + cloud sync is the same as a cloud IdP.&#8221;</strong> With on-prem IdP + cloud sync (e.g., AD + Entra ID Connect), authentication happens via the on-prem IdP — if it goes down, cloud SSO breaks. A pure cloud IdP (Okta standalone, Entra ID without on-prem AD) authenticates entirely in the cloud.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>IdPs are the central control plane for federated identity — their architecture, trust relationships, and provisioning workflows define the enterprise IAM posture</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>SCIM-based deprovisioning is an access control risk management practice — without it, terminated employee access persists across connected systems</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The choice of on-prem vs cloud IdP, federation vs sync, and SCIM vs JIT provisioning are architectural decisions with long-term operational and security implications</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>An IdP authenticates users and issues assertions (SAML) or tokens (OIDC/OAuth2) — applications trust the IdP, not the user directly</li>
<li>On-prem: AD FS (Windows/legacy), Keycloak (open source, flexible), Shibboleth (academia)</li>
<li>Cloud: Okta (cloud-native, strong lifecycle management), Entra ID (Microsoft-integrated), Google Workspace</li>
<li>Federation = authentication delegation between IdPs; Directory sync = data replication; SCIM = account lifecycle (provisioning/deprovisioning)</li>
<li>SCIM deprovisioning is the critical control — it ensures ex-employees lose access automatically across all connected systems</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP11 covered the IdP landscape. EP12 gets specific: Entra ID and Linux — how you configure a Linux VM to accept SSH logins authenticated against Azure AD credentials, and how the <code class="" data-line="">aad-auth</code> / <code class="" data-line="">pam_aad</code> stack works end to end.</p>
<p><em>Next: <a href="/entra-id-linux-login/">Entra ID Linux Login: SSH Authentication with Azure AD Credentials</a></em></p>
<p>Get EP12 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&amp;linkname=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fidentity-providers-explained%2F&#038;title=Identity%20Providers%20Explained%3A%20On-Prem%2C%20Cloud%2C%20SCIM%2C%20and%20Federation" data-a2a-url="https://linuxcent.com/identity-providers-explained/" data-a2a-title="Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation"></a></p><p>The post <a href="https://linuxcent.com/identity-providers-explained/">Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/identity-providers-explained/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1802</post-id>	</item>
		<item>
		<title>SAML vs OIDC vs OAuth2: Which Protocol Handles Which Identity Problem</title>
		<link>https://linuxcent.com/saml-vs-oidc-vs-oauth2/</link>
					<comments>https://linuxcent.com/saml-vs-oidc-vs-oauth2/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Fri, 08 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Identity Federation]]></category>
		<category><![CDATA[OAuth2]]></category>
		<category><![CDATA[OIDC]]></category>
		<category><![CDATA[SAML]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SSO]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1799</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>SAML vs OIDC vs OAuth2: understand what problem each solves, when to use which, and how a browser SSO flow works end-to-end — from IdP redirect to JWT claim.</p>
<p>The post <a href="https://linuxcent.com/saml-vs-oidc-vs-oauth2/">SAML vs OIDC vs OAuth2: Which Protocol Handles Which Identity Problem</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 10</em><br />
<a href="/active-directory-ldap-kerberos/">EP09: Active Directory</a> → <strong>EP10</strong> → <a href="/identity-providers-explained/">EP11: Identity Providers</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>SAML 2.0 is a federation protocol for browser-based SSO — an IdP issues a signed XML assertion that a Service Provider trusts; designed for enterprise applications</li>
<li>OAuth2 is an <em>authorization</em> delegation protocol, not authentication — it lets an application act on your behalf without knowing your password; the access token says what, not who</li>
<li>OIDC (OpenID Connect) = OAuth2 + an identity layer — adds the <code class="" data-line="">id_token</code> (a JWT containing who you are) on top of OAuth2&#8217;s <code class="" data-line="">access_token</code> (what you can do)</li>
<li>SAML vs OIDC: SAML is XML, enterprise-native, stateful; OIDC is JSON/JWT, API-native, stateless — new applications almost always use OIDC</li>
<li>The <code class="" data-line="">id_token</code> is a JWT — decode it at jwt.io and read every claim — it tells you exactly what the IdP asserts about the user</li>
<li>The browser SSO flow is three redirects: user → SP → IdP (authenticate) → SP (consume assertion)</li>
</ul>
<hr />
<h2 id="the-problem-ldap-and-kerberos-dont-cross-the-internet">The Problem: LDAP and Kerberos Don&#8217;t Cross the Internet</h2>
<p>EP09 showed how authentication works inside a corporate network. LDAP and Kerberos both assume network proximity to the directory server — firewall-friendly ports don&#8217;t help when the authentication protocol requires a direct connection to the KDC or directory.</p>
<pre><code class="" data-line="">Internal network: works
  Browser → intranet app → LDAP/Kerberos → AD DC (all on 10.0.0.0/8)

Internet: breaks
  Browser → SaaS app (AWS) → LDAP/Kerberos → AD DC (on-prem behind firewall)
  ✗ KDC not reachable across NAT
  ✗ LDAP not exposed to internet (shouldn&#039;t be)
  ✗ Every SaaS app can&#039;t have its own LDAP connection to your DC
</code></pre>
<p>SAML was invented in 2002 to solve this. OIDC in 2014. Both let identity assertions travel over HTTPS — the one protocol that crosses every firewall.</p>
<hr />
<h2 id="saml-20-enterprise-browser-sso">SAML 2.0: Enterprise Browser SSO</h2>
<p>SAML 2.0 has three actors: the User, the Identity Provider (IdP), and the Service Provider (SP).</p>
<pre><code class="" data-line="">1. User visits SP (e.g., Salesforce)
   SP: &quot;I don&#039;t know this user — send them to the IdP&quot;
   ↓  HTTP redirect with SAMLRequest (base64-encoded AuthnRequest)

2. User arrives at IdP (e.g., Okta, AD FS, Entra ID)
   IdP: &quot;Authenticate me&quot; → user enters credentials
   IdP: generates a signed SAML Assertion (XML)
   ↓  HTTP POST to SP&#039;s Assertion Consumer Service (ACS) URL

3. SP receives the SAMLResponse
   SP: verifies the signature using IdP&#039;s public key
   SP: extracts user attributes from the Assertion
   SP: creates a session — user is logged in
</code></pre>
<p>The SAML Assertion is an XML document signed by the IdP. It contains:</p>
<pre><code class="" data-line="">&lt;saml:Assertion&gt;
  &lt;saml:Issuer&gt;https://idp.corp.com&lt;/saml:Issuer&gt;
  &lt;saml:Subject&gt;
    &lt;saml:NameID Format=&quot;urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress&quot;&gt;
      vamshi@corp.com
    &lt;/saml:NameID&gt;
  &lt;/saml:Subject&gt;
  &lt;saml:Conditions
    NotBefore=&quot;2026-04-27T01:00:00Z&quot;
    NotOnOrAfter=&quot;2026-04-27T01:05:00Z&quot;&gt;  ← short-lived: replay protection
  &lt;/saml:Conditions&gt;
  &lt;saml:AttributeStatement&gt;
    &lt;saml:Attribute Name=&quot;email&quot;&gt;
      &lt;saml:AttributeValue&gt;vamshi@corp.com&lt;/saml:AttributeValue&gt;
    &lt;/saml:Attribute&gt;
    &lt;saml:Attribute Name=&quot;groups&quot;&gt;
      &lt;saml:AttributeValue&gt;engineers&lt;/saml:AttributeValue&gt;
      &lt;saml:AttributeValue&gt;sre-team&lt;/saml:AttributeValue&gt;
    &lt;/saml:Attribute&gt;
  &lt;/saml:AttributeStatement&gt;
&lt;/saml:Assertion&gt;
</code></pre>
<p>The SP trusts the assertion because it&#8217;s signed with the IdP&#8217;s private key, and the SP has the IdP&#8217;s public certificate configured. No direct connection between SP and IdP needed during authentication — only the browser carries the assertion.</p>
<p><strong>SP-initiated vs IdP-initiated:</strong><br />
&#8211; SP-initiated: user visits the SP, gets redirected to IdP, authenticates, redirected back — the common flow<br />
&#8211; IdP-initiated: user starts at the IdP (e.g., company portal), clicks an app, IdP sends assertion directly — simpler but no SP-generated RequestID, so the SP can&#8217;t verify the request was expected (a security concern)</p>
<hr />
<h2 id="oauth2-authorization-delegation-not-authentication">OAuth2: Authorization Delegation (Not Authentication)</h2>
<p>This distinction is important and consistently confused: <strong>OAuth2 is for authorization, not authentication.</strong></p>
<p>OAuth2 solves: &#8220;I want to let GitHub Actions post to my Slack without giving GitHub my Slack password.&#8221;</p>
<pre><code class="" data-line="">Resource Owner (you)  → grants permission to →  Client (GitHub Actions)
                                                        │
                                                        │ access_token
                                                        ▼
                                               Resource Server (Slack API)
                                               &quot;this token can post messages&quot;
</code></pre>
<p>The <code class="" data-line="">access_token</code> answers &#8220;what can this client do?&#8221; not &#8220;who is this user?&#8221; A resource server receiving an access token knows the token is valid and what scopes it carries — it does not necessarily know which human authorized it.</p>
<p>The four OAuth2 grant types:</p>
<table>
<thead>
<tr>
<th>Grant</th>
<th>Use case</th>
</tr>
</thead>
<tbody>
<tr>
<td>Authorization Code</td>
<td>Web apps (server-side) — most secure, recommended</td>
</tr>
<tr>
<td>PKCE (+ Auth Code)</td>
<td>Native/SPA apps — Auth Code without client secret</td>
</tr>
<tr>
<td>Client Credentials</td>
<td>Machine-to-machine (no user) — service accounts</td>
</tr>
<tr>
<td>Device Code</td>
<td>Devices without browsers (smart TVs, CLIs)</td>
</tr>
</tbody>
</table>
<p>The Implicit grant (tokens in URL fragment) is deprecated. Don&#8217;t use it.</p>
<hr />
<h2 id="oidc-oauth2-who-you-are">OIDC: OAuth2 + Who You Are</h2>
<p>OpenID Connect adds identity to OAuth2 by adding the <code class="" data-line="">id_token</code> — a JWT that the IdP signs and that contains claims about the authenticated user.</p>
<pre><code class="" data-line="">Authorization Code flow with OIDC:

1. Client redirects user to IdP:
   GET /authorize?
     response_type=code
     &amp;client_id=myapp
     &amp;scope=openid email profile    ← &quot;openid&quot; scope triggers OIDC
     &amp;redirect_uri=https://app.com/callback
     &amp;state=random-nonce

2. IdP authenticates user, returns:
   GET /callback?code=AUTH_CODE&amp;state=random-nonce

3. Client exchanges code for tokens:
   POST /token
   grant_type=authorization_code&amp;code=AUTH_CODE...

4. IdP returns:
   {
     &quot;access_token&quot;: &quot;eyJ...&quot;,    ← what the user authorized
     &quot;id_token&quot;: &quot;eyJ...&quot;,        ← who the user is (JWT)
     &quot;token_type&quot;: &quot;Bearer&quot;,
     &quot;expires_in&quot;: 3600
   }
</code></pre>
<p>The <code class="" data-line="">id_token</code> decoded:</p>
<pre><code class="" data-line="">{
  &quot;iss&quot;: &quot;https://idp.corp.com&quot;,          ← issuer (the IdP)
  &quot;sub&quot;: &quot;user-guid-12345&quot;,               ← subject (stable user identifier)
  &quot;aud&quot;: &quot;myapp&quot;,                          ← audience (your client_id)
  &quot;exp&quot;: 1745730000,                       ← expiry (Unix timestamp)
  &quot;iat&quot;: 1745726400,                       ← issued at
  &quot;email&quot;: &quot;vamshi@corp.com&quot;,
  &quot;name&quot;: &quot;Vamshi Krishna&quot;,
  &quot;groups&quot;: [&quot;engineers&quot;, &quot;sre-team&quot;]     ← custom claims from IdP
}
</code></pre>
<pre><code class="" data-line=""># Decode any JWT at the command line (no verification — for debugging only)
echo &quot;eyJ...&quot; | cut -d. -f2 | base64 -d 2&gt;/dev/null | python3 -m json.tool

# Or: jwt.io — paste the token, read every claim
</code></pre>
<p><strong><code class="" data-line="">sub</code> is the stable user identifier.</strong> Email addresses change. Names change. The <code class="" data-line="">sub</code> claim is the IdP&#8217;s internal identifier for the user — use it as the primary key when storing user data. Never store email as the primary key.</p>
<hr />
<h2 id="saml-vs-oidc-when-to-use-which">SAML vs OIDC: When to Use Which</h2>
<table>
<thead>
<tr>
<th></th>
<th>SAML 2.0</th>
<th>OIDC</th>
</tr>
</thead>
<tbody>
<tr>
<td>Format</td>
<td>XML</td>
<td>JSON / JWT</td>
</tr>
<tr>
<td>Transport</td>
<td>HTTP POST (browser only)</td>
<td>HTTP redirect + JSON API</td>
</tr>
<tr>
<td>Age</td>
<td>2002</td>
<td>2014</td>
</tr>
<tr>
<td>Enterprise adoption</td>
<td>Very high (AD FS, Okta, Entra ID)</td>
<td>Very high (newer apps)</td>
</tr>
<tr>
<td>API-friendly</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Mobile apps</td>
<td>No</td>
<td>Yes</td>
</tr>
<tr>
<td>Complexity</td>
<td>High (XML, schemas, signatures)</td>
<td>Medium (JWT, JSON)</td>
</tr>
<tr>
<td>Single Logout</td>
<td>Specified (rarely works well)</td>
<td>Optional, inconsistent</td>
</tr>
</tbody>
</table>
<p><strong>Use SAML when:</strong> You&#8217;re integrating with an enterprise SaaS that only supports SAML (Salesforce classic, legacy HR systems), or your IdP team mandates it.</p>
<p><strong>Use OIDC when:</strong> You&#8217;re building a new application, integrating with a modern IdP, or need API-based token validation. OIDC is the default for everything new.</p>
<p><strong>Use OAuth2 (Client Credentials) when:</strong> Service-to-service authentication with no user — your CI/CD pipeline authenticating to an API, your microservice calling another microservice.</p>
<hr />
<h2 id="a-complete-browser-sso-flow-oidc">A Complete Browser SSO Flow (OIDC)</h2>
<pre><code class="" data-line="">1. User visits https://app.corp.com (not logged in)
   App: no session → redirect to IdP

2. GET https://idp.corp.com/authorize?
        response_type=code
        &amp;client_id=app-corp
        &amp;scope=openid email
        &amp;redirect_uri=https://app.corp.com/callback
        &amp;state=abc123
        &amp;nonce=xyz789

3. IdP: user is not authenticated → show login form
   User: enters vamshi@corp.com + password
   (or: IdP sees existing session cookie → skip login)

4. IdP: authentication success
   Redirect: GET https://app.corp.com/callback?code=AUTH_CODE&amp;state=abc123

5. App (server-side): validate state=abc123 (CSRF protection)
   POST https://idp.corp.com/token
     grant_type=authorization_code
     &amp;code=AUTH_CODE
     &amp;client_id=app-corp
     &amp;client_secret=SECRET
     &amp;redirect_uri=https://app.corp.com/callback

6. IdP responds:
   { &quot;id_token&quot;: &quot;JWT...&quot;, &quot;access_token&quot;: &quot;JWT...&quot;, &quot;expires_in&quot;: 3600 }

7. App: validate id_token signature (using IdP&#039;s JWKS endpoint)
   App: extract sub, email, groups from id_token
   App: create session for vamshi@corp.com
   App: redirect user to original destination
</code></pre>
<p>Step 7 is where most bugs live. The app must validate: signature (using IdP&#8217;s public keys from <code class="" data-line="">/.well-known/jwks.json</code>), <code class="" data-line="">iss</code> (matches the expected IdP), <code class="" data-line="">aud</code> (matches the client_id), <code class="" data-line="">exp</code> (not expired), and <code class="" data-line="">nonce</code> (matches what was sent in step 2). Skip any of these and you have an authentication bypass.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;OAuth2 is for login.&#8221;</strong> OAuth2 is for authorization delegation. It can be used as a login mechanism only when OIDC (the <code class="" data-line="">openid</code> scope + <code class="" data-line="">id_token</code>) is added on top. &#8220;Login with Google&#8221; uses OIDC, not bare OAuth2.</p>
<p><strong>&#8220;JWTs are encrypted.&#8221;</strong> By default, JWTs are signed (JWS), not encrypted. The header and payload are base64url-encoded — anyone can decode them. Encryption (JWE) is a separate, less commonly used spec. Never put secrets in a JWT payload assuming it&#8217;s private.</p>
<p><strong>&#8220;SAML Single Logout works reliably.&#8221;</strong> SAML SLO is specified but inconsistently implemented. Many SPs ignore SLO requests or don&#8217;t propagate them correctly. Don&#8217;t depend on SLO for security — session revocation requires additional mechanisms (short-lived tokens, token introspection, session registries).</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>SAML, OAuth2, and OIDC are the three protocols that enable federated identity and SSO — understanding which does what is foundational to modern IAM design</td>
</tr>
<tr>
<td>CISSP Domain 4: Communications and Network Security</td>
<td>JWT validation (signature, claims, expiry) is a network security control — failing to validate any claim is an authentication bypass vulnerability</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The choice of SAML vs OIDC is an architectural decision that affects every application integration, mobile support, and API design</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>SAML 2.0: XML-based browser SSO — three redirects, signed assertion, enterprise legacy apps</li>
<li>OAuth2: authorization delegation — access tokens grant scopes, not identity</li>
<li>OIDC: OAuth2 + <code class="" data-line="">id_token</code> — adds who the user is on top of what they can do</li>
<li><code class="" data-line="">sub</code> is the stable user identifier in OIDC — never use email as a primary key</li>
<li>JWT validation must check: signature, <code class="" data-line="">iss</code>, <code class="" data-line="">aud</code>, <code class="" data-line="">exp</code>, <code class="" data-line="">nonce</code> — missing any is a security bypass</li>
<li>New applications: OIDC. Legacy enterprise SaaS: SAML. Service-to-service: OAuth2 Client Credentials</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP10 covered the protocols. EP11 covers the systems that implement them — the identity providers: what Okta, Entra ID, Keycloak, and AD FS actually do, how they federate with each other, and how SCIM handles user provisioning separately from authentication.</p>
<p><em>Next: <a href="/identity-providers-explained/">Identity Providers Explained: On-Prem, Cloud, SCIM, and Federation</a></em></p>
<p>Get EP11 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&amp;linkname=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fsaml-vs-oidc-vs-oauth2%2F&#038;title=SAML%20vs%20OIDC%20vs%20OAuth2%3A%20Which%20Protocol%20Handles%20Which%20Identity%20Problem" data-a2a-url="https://linuxcent.com/saml-vs-oidc-vs-oauth2/" data-a2a-title="SAML vs OIDC vs OAuth2: Which Protocol Handles Which Identity Problem"></a></p><p>The post <a href="https://linuxcent.com/saml-vs-oidc-vs-oauth2/">SAML vs OIDC vs OAuth2: Which Protocol Handles Which Identity Problem</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/saml-vs-oidc-vs-oauth2/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1799</post-id>	</item>
		<item>
		<title>How Active Directory Works: LDAP, Kerberos, and Group Policy Under the Hood</title>
		<link>https://linuxcent.com/active-directory-ldap-kerberos/</link>
					<comments>https://linuxcent.com/active-directory-ldap-kerberos/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 07 May 2026 11:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[Windows]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1796</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>How Active Directory works under the hood: LDAP storage, Kerberos authentication, USN replication, KCC site topology, GPO delivery — and how Linux joins it.</p>
<p>The post <a href="https://linuxcent.com/active-directory-ldap-kerberos/">How Active Directory Works: LDAP, Kerberos, and Group Policy Under the Hood</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 9</em><br />
<a href="/freeipa-linux-identity-management/">EP08: FreeIPA</a> → <strong>EP09</strong> → <a href="/saml-vs-oidc-vs-oauth2/">EP10: SAML/OIDC</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Active Directory is not a product that happens to use LDAP — it <em>is</em> an LDAP directory with a Microsoft-extended schema, a built-in Kerberos KDC, and DNS tightly integrated</li>
<li>Replication uses USNs (Update Sequence Numbers) and GUIDs — the Knowledge Consistency Checker (KCC) automatically builds the replication topology</li>
<li>Sites and site links tell AD which DCs are physically close — AD prefers to authenticate users against a DC in the same site to minimize WAN latency</li>
<li>Group Policy Objects (GPOs) are stored as LDAP entries (in the <code class="" data-line="">CN=Policies</code> container) and Sysvol files — LDAP tells clients which GPOs apply; Sysvol delivers the policy files</li>
<li>Linux joins AD via <code class="" data-line="">realm join</code> (uses adcli + SSSD) or <code class="" data-line="">net ads join</code> (Samba + winbind) — both register a machine account in AD and get a Kerberos keytab</li>
<li>The difference between Linux in AD and Linux in FreeIPA: AD is optimized for Windows; FreeIPA is optimized for Linux — both interoperate</li>
</ul>
<hr />
<h2 id="the-big-picture-what-ad-actually-is">The Big Picture: What AD Actually Is</h2>
<pre><code class="" data-line="">Active Directory Domain: corp.com
┌────────────────────────────────────────────────────────────┐
│                                                            │
│  LDAP directory          Kerberos KDC                      │
│  ─────────────           ──────────                        │
│  Schema: 1000+ classes   Realm: CORP.COM                   │
│  Objects: users, groups, Issues TGTs + service tickets     │
│  computers, GPOs, OUs    Uses LDAP as the account DB       │
│                                                            │
│  DNS                     Sysvol (DFS share)                │
│  ────                    ────────────────                  │
│  SRV records for KDC     GPO templates                     │
│  and LDAP discovery      Login scripts                     │
│                          Replicated via DFSR               │
│                                                            │
│  Replication engine: USN + GUID + KCC                      │
└────────────────────────────────────────────────────────────┘
          │ replicates to          │ replicates to
          ▼                        ▼
   DC: dc02.corp.com        DC: dc03.corp.com
</code></pre>
<p>EP08 showed FreeIPA as the Linux-native answer to enterprise identity. AD is the Microsoft answer — and because most enterprises run Windows clients, understanding AD is unavoidable for Linux infrastructure engineers. This episode goes behind the LDAP and Kerberos protocols to explain what makes AD specifically work.</p>
<hr />
<h2 id="the-ad-schema-ldap-with-1000-object-classes">The AD Schema: LDAP With 1000+ Object Classes</h2>
<p>AD&#8217;s schema extends the base LDAP schema with Microsoft-specific classes and attributes. Every user object is a <code class="" data-line="">user</code> class (which extends <code class="" data-line="">organizationalPerson</code> which extends <code class="" data-line="">person</code> which extends <code class="" data-line="">top</code>) with additional attributes like:</p>
<pre><code class="" data-line="">sAMAccountName   ← the pre-Windows 2000 login name (vamshi)
userPrincipalName ← the modern UPN (vamshi@corp.com)
objectGUID       ← a globally unique 128-bit identifier (never changes, even if DN changes)
objectSid        ← Windows Security Identifier (used for ACL enforcement on Windows)
whenCreated      ← creation timestamp
pwdLastSet       ← password change timestamp
userAccountControl ← bitmask: disabled, locked, password never expires, etc.
memberOf         ← back-link: groups this user belongs to
</code></pre>
<p><code class="" data-line="">objectGUID</code> is the authoritative identifier in AD — not the DN. When a user is renamed or moved to a different OU, the GUID stays the same. Applications that store a user&#8217;s DN will break on rename; applications that store the GUID won&#8217;t.</p>
<p><code class="" data-line="">userAccountControl</code> is the bitmask that controls account state:</p>
<pre><code class="" data-line="">Flag          Value   Meaning
ACCOUNTDISABLE  2     Account disabled
LOCKOUT         16    Account locked out
PASSWD_NOTREQD  32    Password not required
NORMAL_ACCOUNT  512   Normal user account (set on almost all accounts)
DONT_EXPIRE_PASSWD 65536  Password never expires
</code></pre>
<pre><code class="" data-line=""># Query AD from a Linux machine
ldapsearch -x -H ldap://dc.corp.com \
  -D &quot;vamshi@corp.com&quot; -w password \
  -b &quot;dc=corp,dc=com&quot; \
  &quot;(sAMAccountName=vamshi)&quot; \
  sAMAccountName userPrincipalName objectGUID memberOf userAccountControl
</code></pre>
<hr />
<h2 id="replication-usn-guid-kcc">Replication: USN + GUID + KCC</h2>
<p>AD replication is multi-master — every DC accepts writes. The replication engine uses:</p>
<p><strong>USN (Update Sequence Number)</strong> — a per-DC counter that increments on every local write. Each attribute in the directory stores the USN at which it was last modified (<code class="" data-line="">uSNChanged</code>, <code class="" data-line="">uSNCreated</code>). When DC-A replicates to DC-B, DC-B asks: &#8220;give me everything you&#8217;ve changed since the last USN I saw from you.&#8221;</p>
<p><strong>GUID</strong> — each object has a globally unique identifier. If the same attribute is modified on two DCs before replication (a conflict), the conflict is resolved: last-writer-wins at the attribute level, based on the modification timestamp. If timestamps are equal, the attribute value from the DC with the lexicographically higher GUID wins.</p>
<p><strong>KCC (Knowledge Consistency Checker)</strong> — a component that runs on every DC and automatically constructs the replication topology. You don&#8217;t configure which DCs replicate to which — the KCC builds a minimum spanning tree that ensures every DC is connected to every other within a set number of hops. You configure Sites and site links; the KCC does the rest.</p>
<pre><code class="" data-line=""># Check replication status from a Linux machine (requires rpcclient or adcli)
# Or on the DC: repadmin /showrepl (Windows tool)

# Simulate: query the highestCommittedUSN from a DC
ldapsearch -x -H ldap://dc.corp.com \
  -D &quot;vamshi@corp.com&quot; -w password \
  -b &quot;&quot; -s base highestCommittedUSN
</code></pre>
<hr />
<h2 id="sites-and-site-links">Sites and Site Links</h2>
<p>Sites are AD&#8217;s concept of physical network topology. A site is a set of IP subnets with high-bandwidth connectivity between them. Site links represent the WAN connections between sites.</p>
<pre><code class="" data-line="">Site: Mumbai              Site: Hyderabad
┌────────────────┐        ┌────────────────┐
│ DC: dc-mum-01  │        │ DC: dc-hyd-01  │
│ DC: dc-mum-02  │        │ DC: dc-hyd-02  │
│ subnet: 10.1/16│        │ subnet: 10.2/16│
└───────┬────────┘        └────────┬───────┘
        │                          │
        └──── Site Link ───────────┘
              Cost: 100
              Replication interval: 15 min
</code></pre>
<p>When a user in Mumbai authenticates, AD&#8217;s KDC locates a DC in the same site using DNS SRV records. The SRV records include the site name in the service name: <code class="" data-line="">_ldap._tcp.Mumbai._sites.dc._msdcs.corp.com</code>. SSSD and Windows clients query site-local SRV records first.</p>
<p>If no DC is available in the local site, authentication falls back to a DC in another site across the WAN link. Configuring sites correctly prevents remote authentication failures from killing local operations.</p>
<hr />
<h2 id="group-policy-ldap-sysvol">Group Policy: LDAP + Sysvol</h2>
<p>GPOs are stored in two places:</p>
<p><strong>LDAP</strong> — the <code class="" data-line="">CN=Policies,CN=System,DC=corp,DC=com</code> container holds GPO metadata objects. Each GPO has a GUID, a display name, and version numbers. The <code class="" data-line="">gPLink</code> attribute on OUs and the domain root links GPOs to where they apply.</p>
<p><strong>Sysvol</strong> — the actual policy templates and scripts live in <code class="" data-line="">\\corp.com\SYSVOL\corp.com\Policies\{GPO-GUID}\</code>. Sysvol is a DFS-R (Distributed File System Replication) share replicated to every DC.</p>
<p>When a Windows client applies Group Policy:<br />
1. LDAP query: what GPOs are linked to my OU chain?<br />
2. Sysvol fetch: download the policy templates from the GPO&#8217;s Sysvol path<br />
3. Apply: process Registry settings, Security settings, Scripts</p>
<p>Linux clients don&#8217;t process GPOs natively. The <code class="" data-line="">adcli</code> and <code class="" data-line="">sssd</code> tools interpret a small subset of AD policy (password policy, account lockout) via LDAP. Full GPO processing on Linux requires Samba&#8217;s <code class="" data-line="">samba-gpupdate</code> or third-party tools.</p>
<hr />
<h2 id="joining-linux-to-ad">Joining Linux to AD</h2>
<h3 id="realm-join-recommended">realm join (recommended)</h3>
<pre><code class="" data-line=""># Install required packages
dnf install -y realmd sssd adcli samba-common

# Discover the domain
realm discover corp.com
# corp.com
#   type: kerberos
#   realm-name: CORP.COM
#   domain-name: corp.com
#   configured: no
#   server-software: active-directory
#   client-software: sssd

# Join
realm join corp.com -U Administrator
# Prompts for Administrator password
# Creates machine account in AD
# Configures sssd.conf, krb5.conf, nsswitch.conf, pam.d automatically

# Verify
realm list
id vamshi@corp.com
</code></pre>
<h3 id="what-the-join-does">What the join does:</h3>
<ol>
<li>Creates a machine account <code class="" data-line="">HOSTNAME$</code> in <code class="" data-line="">CN=Computers,DC=corp,DC=com</code></li>
<li>Sets a machine password (rotated automatically by SSSD)</li>
<li>Retrieves a Kerberos keytab to <code class="" data-line="">/etc/krb5.keytab</code></li>
<li>Configures SSSD with <code class="" data-line="">id_provider = ad</code>, <code class="" data-line="">auth_provider = ad</code></li>
<li>Updates <code class="" data-line="">/etc/nsswitch.conf</code> to include <code class="" data-line="">sss</code></li>
<li>Updates <code class="" data-line="">/etc/pam.d/</code> to include <code class="" data-line="">pam_sss</code></li>
</ol>
<p>After joining, SSSD uses the machine&#8217;s Kerberos keytab to authenticate to the DC and query LDAP — no hardcoded service account credentials required.</p>
<hr />
<h2 id="ldap-queries-against-ad-from-linux">LDAP Queries Against AD from Linux</h2>
<pre><code class="" data-line=""># Find a user (after kinit or with -w password)
ldapsearch -Y GSSAPI -H ldap://dc.corp.com \
  -b &quot;dc=corp,dc=com&quot; \
  &quot;(sAMAccountName=vamshi)&quot; \
  sAMAccountName mail memberOf

# Find all members of a group
ldapsearch -Y GSSAPI -H ldap://dc.corp.com \
  -b &quot;dc=corp,dc=com&quot; \
  &quot;(cn=engineers)&quot; \
  member

# Find all AD-joined Linux machines
ldapsearch -Y GSSAPI -H ldap://dc.corp.com \
  -b &quot;dc=corp,dc=com&quot; \
  &quot;(&amp;(objectClass=computer)(operatingSystem=*Linux*))&quot; \
  cn operatingSystem lastLogonTimestamp

# Find disabled accounts
ldapsearch -Y GSSAPI -H ldap://dc.corp.com \
  -b &quot;dc=corp,dc=com&quot; \
  &quot;(userAccountControl:1.2.840.113556.1.4.803:=2)&quot; \
  sAMAccountName
</code></pre>
<p>The last filter uses an LDAP extensible match (<code class="" data-line="">1.2.840.113556.1.4.803</code> is the OID for bitwise AND). <code class="" data-line="">userAccountControl:1.2.840.113556.1.4.803:=2</code> means &#8220;entries where userAccountControl AND 2 equals 2&#8221; — i.e., the ACCOUNTDISABLE bit is set. This is a Microsoft AD extension not in standard LDAP.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;AD is just Microsoft&#8217;s LDAP.&#8221;</strong> AD is LDAP + Kerberos + DNS + DFS-R + GPO, all tightly integrated and with a schema that the Microsoft ecosystem depends on. You can query AD with standard <code class="" data-line="">ldapsearch</code>. You cannot replace it with OpenLDAP without breaking every Windows client.</p>
<p><strong>&#8220;Linux machines in AD get GPO.&#8221;</strong> Linux machines appear in AD and can be organized into OUs. Standard GPOs don&#8217;t apply to them. Samba&#8217;s <code class="" data-line="">samba-gpupdate</code> can process a subset of AD policy for Linux — mostly Registry and Security settings mapped to Linux equivalents.</p>
<p><strong>&#8220;realm leave removes the machine cleanly.&#8221;</strong> <code class="" data-line="">realm leave</code> removes local configuration but does not delete the machine account from AD. The stale computer object stays in <code class="" data-line="">CN=Computers</code> until an AD admin deletes it. Always run <code class="" data-line="">realm leave &amp;&amp; adcli delete-computer -U Administrator</code> for a clean removal.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>AD is the dominant enterprise identity store — understanding its LDAP structure, Kerberos realm, and GPO model is essential for IAM in mixed environments</td>
</tr>
<tr>
<td>CISSP Domain 4: Communications and Network Security</td>
<td>AD replication traffic (RPC, LDAP, Kerberos) is a significant portion of enterprise WAN traffic — Sites and site links are a network security and performance design decision</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>AD forest/domain/OU hierarchy is an architectural decision with long-term security consequences — getting OU structure wrong constrains GPO delegation for years</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>AD is LDAP + Kerberos + DNS + GPO + DFS-R — not a product that &#8220;uses&#8221; these; they&#8217;re the implementation</li>
<li>Replication is multi-master via USN + GUID; the KCC builds the topology automatically from Sites configuration</li>
<li><code class="" data-line="">objectGUID</code> is the stable identifier — not the DN, which changes on rename/move</li>
<li><code class="" data-line="">realm join</code> is the correct way to join Linux to AD — it configures SSSD, Kerberos, PAM, and NSS correctly in one command</li>
<li><code class="" data-line="">userAccountControl</code> is the bitmask that controls account state — <code class="" data-line="">(userAccountControl:1.2.840.113556.1.4.803:=2)</code> finds disabled accounts</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP09 covered AD — LDAP and Kerberos inside the corporate network. EP10 covers what happens when identity needs to work across the internet, where Kerberos doesn&#8217;t reach: SAML, OAuth2, and OIDC — the protocols that let identity leave the building.</p>
<p><em>Next: <a href="/saml-vs-oidc-vs-oauth2/">SAML vs OIDC vs OAuth2: Which Protocol Handles Which Identity Problem</a></em></p>
<p>Get EP10 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&amp;linkname=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Factive-directory-ldap-kerberos%2F&#038;title=How%20Active%20Directory%20Works%3A%20LDAP%2C%20Kerberos%2C%20and%20Group%20Policy%20Under%20the%20Hood" data-a2a-url="https://linuxcent.com/active-directory-ldap-kerberos/" data-a2a-title="How Active Directory Works: LDAP, Kerberos, and Group Policy Under the Hood"></a></p><p>The post <a href="https://linuxcent.com/active-directory-ldap-kerberos/">How Active Directory Works: LDAP, Kerberos, and Group Policy Under the Hood</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/active-directory-ldap-kerberos/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1796</post-id>	</item>
		<item>
		<title>FreeIPA: LDAP + Kerberos + PKI in a Single Linux Identity Stack</title>
		<link>https://linuxcent.com/freeipa-linux-identity-management/</link>
					<comments>https://linuxcent.com/freeipa-linux-identity-management/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Thu, 07 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[FreeIPA]]></category>
		<category><![CDATA[HBAC]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[linux-security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1793</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutes</span></span>FreeIPA combines 389-DS, MIT Kerberos, Dogtag PKI, and Bind DNS into one Linux identity stack. Set up HBAC rules, centralized sudo, and AD trust.</p>
<p>The post <a href="https://linuxcent.com/freeipa-linux-identity-management/">FreeIPA: LDAP + Kerberos + PKI in a Single Linux Identity Stack</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 8</em><br />
<a href="/ldap-high-availability/">EP07: LDAP HA</a> → <strong>EP08</strong> → <a href="/active-directory-ldap-kerberos/">EP09: Active Directory</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>FreeIPA is 389-DS (LDAP) + MIT Kerberos + Dogtag PKI + Bind DNS + SSSD — one <code class="" data-line="">ipa-server-install</code> command gets you an enterprise identity platform</li>
<li>Host-Based Access Control (HBAC) lets you define centrally: which users can SSH to which hosts — no more managing <code class="" data-line="">/etc/security/access.conf</code> per machine</li>
<li>Sudo rules from the directory: define <code class="" data-line="">sudo</code> policy centrally, have every machine pull it — no <code class="" data-line="">/etc/sudoers.d/</code> files scattered across the fleet</li>
<li><code class="" data-line="">ipa</code> CLI is the management interface — <code class="" data-line="">ipa user-add</code>, <code class="" data-line="">ipa group-add</code>, <code class="" data-line="">ipa hbacrule-add</code> — everything that took five LDAP commands takes one <code class="" data-line="">ipa</code> command</li>
<li>FreeIPA trusts with Active Directory let Linux machines authenticate AD users without joining the AD domain</li>
<li>The right choice for Linux-centric environments; AD is the right choice when Windows clients dominate</li>
</ul>
<hr />
<h2 id="the-big-picture-what-freeipa-integrates">The Big Picture: What FreeIPA Integrates</h2>
<pre><code class="" data-line="">┌─────────────────────────────────────────────────────────┐
│                    FreeIPA Server                        │
│                                                         │
│  389-DS (LDAP)    MIT Kerberos    Dogtag PKI            │
│  ─────────────    ───────────     ─────────             │
│  User/group       TGT + service   Machine certs         │
│  storage          ticket issuing  User certs             │
│                                   OCSP / CRL            │
│  Bind DNS         SSSD (client)   Apache (WebUI)        │
│  ──────────       ────────────    ──────────────        │
│  SRV records      Enrollment      Management UI         │
│  for KDC/LDAP     automation      REST API              │
└─────────────────────────────────────────────────────────┘
              ▲                  ▲
              │ enrollment       │ SSH + sudo rules
   ┌──────────┴──────────┐  ┌───┴──────────────────┐
   │  Linux client        │  │  Linux client         │
   │  (ipa-client-install)│  │  (ipa-client-install) │
   └─────────────────────┘  └──────────────────────┘
</code></pre>
<p>EP06 and EP07 built OpenLDAP from components. FreeIPA gives you all of that plus Kerberos, PKI, DNS, and HBAC — opinionated, integrated, and managed through a single CLI and WebUI. This episode shows what you actually get from it.</p>
<hr />
<h2 id="why-freeipa-instead-of-bare-openldap">Why FreeIPA Instead of Bare OpenLDAP</h2>
<p>Running bare OpenLDAP requires you to:<br />
&#8211; Configure schema for POSIX accounts, SSH keys, sudo rules, HBAC manually<br />
&#8211; Set up MIT Kerberos separately and integrate it with LDAP<br />
&#8211; Build your own PKI for machine certificates<br />
&#8211; Maintain DNS SRV records for Kerberos discovery<br />
&#8211; Write client enrollment scripts<br />
&#8211; Build a management interface (or live in LDIF)</p>
<p>FreeIPA does all of this in one installer, with a consistent data model across all components. The trade-off is opacity — FreeIPA makes decisions for you (schema, replication topology, Kerberos realm name) that bare OpenLDAP leaves to you.</p>
<hr />
<h2 id="installing-freeipa-server">Installing FreeIPA Server</h2>
<pre><code class="" data-line=""># RHEL / Rocky / AlmaLinux
dnf install -y freeipa-server freeipa-server-dns

# Run the installer (interactive)
ipa-server-install

# Or non-interactive:
ipa-server-install \
  --realm=CORP.COM \
  --domain=corp.com \
  --ds-password=DM_password \
  --admin-password=Admin_password \
  --setup-dns \
  --forwarder=8.8.8.8 \
  --unattended

# After install: get an admin Kerberos ticket
kinit admin
</code></pre>
<p>The installer creates:<br />
&#8211; 389-DS instance with the FreeIPA schema<br />
&#8211; MIT KDC with realm <code class="" data-line="">CORP.COM</code><br />
&#8211; Dogtag CA and all certificate infrastructure<br />
&#8211; Bind DNS with SRV records for the KDC and LDAP server<br />
&#8211; Apache WebUI at <code class="" data-line="">https://ipa.corp.com/ipa/ui/</code><br />
&#8211; SSSD configured on the server itself</p>
<p>Time: 5–10 minutes. What used to take a week of manual configuration.</p>
<hr />
<h2 id="the-ipa-cli">The ipa CLI</h2>
<p>Every management action goes through <code class="" data-line="">ipa</code>. It talks to the IPA server&#8217;s REST API and handles Kerberos authentication transparently (it uses your <code class="" data-line="">kinit</code> session).</p>
<pre><code class="" data-line=""># Users
ipa user-add vamshi \
  --first=Vamshi --last=Krishna \
  --email=vamshi@corp.com \
  --password

ipa user-show vamshi
ipa user-find --all              # search all users
ipa user-disable vamshi          # lock account without deleting
ipa user-mod vamshi --shell=/bin/zsh

# Groups
ipa group-add engineers --desc &quot;Engineering team&quot;
ipa group-add-member engineers --users=vamshi,alice

# Password policy
ipa pwpolicy-mod --minlength=12 --maxlife=90 --history=10

# SSH public keys — stored centrally, pushed to every host
ipa user-mod vamshi --sshpubkey=&quot;ssh-ed25519 AAAA...&quot;
# SSSD on enrolled hosts will use this key for SSH login — no authorized_keys file needed
</code></pre>
<hr />
<h2 id="host-based-access-control-hbac">Host-Based Access Control (HBAC)</h2>
<p>HBAC is the feature that justifies FreeIPA for most Linux shops. It lets you define centrally: which users (or groups) can log in to which hosts (or host groups), using which services (SSH, sudo, FTP).</p>
<p>Without HBAC, access control is per-machine: <code class="" data-line="">/etc/security/access.conf</code> or PAM <code class="" data-line="">pam_access</code> rules, replicated across every server, managed inconsistently.</p>
<p>With HBAC: one rule, enforced everywhere.</p>
<pre><code class="" data-line=""># Create host groups
ipa hostgroup-add production-servers --desc &quot;Production Linux hosts&quot;
ipa hostgroup-add-member production-servers --hosts=web01.corp.com,db01.corp.com

# Create user groups
ipa group-add sre-team
ipa group-add-member sre-team --users=vamshi,alice

# Create an HBAC rule
ipa hbacrule-add allow-sre-to-prod \
  --desc &quot;SRE team can SSH to production&quot;
ipa hbacrule-add-user allow-sre-to-prod --groups=sre-team
ipa hbacrule-add-host allow-sre-to-prod --hostgroups=production-servers
ipa hbacrule-add-service allow-sre-to-prod --hbacsvcs=sshd

# Test the rule before applying
ipa hbactest \
  --user=vamshi \
  --host=web01.corp.com \
  --service=sshd
# Access granted: True
# Matched rules: allow-sre-to-prod
</code></pre>
<p>SSSD on each enrolled host enforces the HBAC rules at login time by querying the IPA server. No per-machine configuration. Add a new server to the <code class="" data-line="">production-servers</code> host group and the HBAC rules apply immediately.</p>
<hr />
<h2 id="sudo-rules-from-the-directory">Sudo Rules from the Directory</h2>
<pre><code class="" data-line=""># Create a sudo rule
ipa sudorule-add allow-sre-sudo \
  --cmdcat=all \
  --desc &quot;SRE team gets full sudo on production&quot;
ipa sudorule-add-user allow-sre-sudo --groups=sre-team
ipa sudorule-add-host allow-sre-sudo --hostgroups=production-servers

# Or a scoped rule — only specific commands
ipa sudorule-add allow-service-restart
ipa sudocmdgroup-add service-commands
ipa sudocmd-add /usr/bin/systemctl
ipa sudocmdgroup-add-member service-commands --sudocmds=&quot;/usr/bin/systemctl&quot;
ipa sudorule-add-allow-command allow-service-restart --sudocmdgroups=service-commands
</code></pre>
<p>On enrolled hosts, SSSD&#8217;s <code class="" data-line="">sssd_sudo</code> responder pulls these rules and the <code class="" data-line="">sudo</code> command evaluates them locally. No <code class="" data-line="">/etc/sudoers.d/</code> files. Central policy, local enforcement.</p>
<hr />
<h2 id="enrolling-a-client">Enrolling a Client</h2>
<pre><code class="" data-line=""># On the client machine
dnf install -y freeipa-client

ipa-client-install \
  --domain=corp.com \
  --server=ipa.corp.com \
  --realm=CORP.COM \
  --principal=admin \
  --password=Admin_password \
  --unattended

# What this does:
# 1. Registers the host in IPA as a machine principal
# 2. Retrieves a host Kerberos keytab (/etc/krb5.keytab)
# 3. Configures SSSD (sssd.conf, nsswitch.conf, pam.d)
# 4. Configures Kerberos (/etc/krb5.conf)
# 5. Optionally configures NTP and DNS
</code></pre>
<p>After enrollment: <code class="" data-line="">getent passwd vamshi</code> returns the IPA user. SSH with an IPA password works. HBAC rules are enforced. Sudo rules from the directory apply. SSH public keys from the user&#8217;s IPA profile work without <code class="" data-line="">authorized_keys</code> files.</p>
<hr />
<h2 id="freeipa-trust-with-active-directory">FreeIPA Trust with Active Directory</h2>
<p>In mixed environments (Linux servers + Windows clients), you can establish a trust between FreeIPA and AD without joining the Linux servers to the AD domain directly.</p>
<pre><code class="" data-line=""># On the IPA server (after installing ipa-server-trust-ad)
ipa-adtrust-install --netbios-name=CORP

# Establish the trust
ipa trust-add ad.corp.com \
  --admin=Administrator \
  --password \
  --type=ad

# AD users can now log in to IPA-enrolled Linux hosts
# They appear as: CORP.COM\username or username@corp.com
</code></pre>
<p>Under the hood: FreeIPA acts as an SSSD-enabled Samba DC for the trust relationship. AD users&#8217; Kerberos tickets from the AD KDC are accepted by the FreeIPA KDC, which maps them to POSIX attributes stored in IPA (or automatically generated via ID mapping).</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;FreeIPA is just OpenLDAP with a UI.&#8221;</strong> FreeIPA uses 389-DS (not OpenLDAP), adds a full Kerberos KDC, a certificate authority, DNS, HBAC enforcement, and sudo management — all with a consistent schema designed for these use cases. It&#8217;s an integrated identity platform, not a wrapper.</p>
<p><strong>&#8220;HBAC rules replace firewall rules.&#8221;</strong> HBAC controls who can log in to a host at the authentication layer — not network access. A blocked HBAC rule means the SSH session is rejected after TCP connection. You still need firewall rules to block TCP access.</p>
<p><strong>&#8220;FreeIPA replicas are identical.&#8221;</strong> FreeIPA uses 389-DS Multi-Supplier replication. All replicas accept reads and writes. But the CA is separate — only the initial server (and explicitly designated CA replicas) run the CA. If the CA goes down, certificate operations stop; authentication does not.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>FreeIPA is an enterprise IAM platform — HBAC, sudo policy, SSH key management, and certificate-based authentication are all IAM controls</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>FreeIPA&#8217;s integrated CA enables certificate-based authentication for machines and users — a stronger authentication factor than passwords</td>
</tr>
<tr>
<td>CISSP Domain 1: Security and Risk Management</td>
<td>Centralized HBAC and sudo policy reduces the attack surface of privilege escalation — no more inconsistent sudoers files that drift across the fleet</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>FreeIPA = 389-DS + MIT Kerberos + Dogtag PKI + Bind DNS — one installer, one management interface</li>
<li>HBAC rules define centrally who can SSH to which host groups — enforced by SSSD on every enrolled client, no per-machine config</li>
<li>Sudo rules from the directory replace scattered <code class="" data-line="">/etc/sudoers.d/</code> files — central policy, SSSD-enforced locally</li>
<li><code class="" data-line="">ipa hbactest</code> lets you verify access rules before a user hits a blocked login — use it before every policy change</li>
<li>For Linux-centric environments: FreeIPA. For Windows-dominant environments: AD. For mixed: FreeIPA trust with AD.</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>FreeIPA is the Linux answer to enterprise identity. EP09 covers the Microsoft answer — Active Directory — which extended LDAP and Kerberos into a complete enterprise platform with Group Policy, Sites, and a replication model built for global scale.</p>
<p><em>Next: <a href="/active-directory-ldap-kerberos/">How Active Directory Works: LDAP, Kerberos, and Group Policy Under the Hood</a></em></p>
<p>Get EP09 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&amp;linkname=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Ffreeipa-linux-identity-management%2F&#038;title=FreeIPA%3A%20LDAP%20%2B%20Kerberos%20%2B%20PKI%20in%20a%20Single%20Linux%20Identity%20Stack" data-a2a-url="https://linuxcent.com/freeipa-linux-identity-management/" data-a2a-title="FreeIPA: LDAP + Kerberos + PKI in a Single Linux Identity Stack"></a></p><p>The post <a href="https://linuxcent.com/freeipa-linux-identity-management/">FreeIPA: LDAP + Kerberos + PKI in a Single Linux Identity Stack</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/freeipa-linux-identity-management/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1793</post-id>	</item>
		<item>
		<title>LDAP High Availability: Load Balancing and Production Architecture</title>
		<link>https://linuxcent.com/ldap-high-availability/</link>
					<comments>https://linuxcent.com/ldap-high-availability/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Wed, 06 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Directory Services]]></category>
		<category><![CDATA[HAProxy]]></category>
		<category><![CDATA[High Availability]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OpenLDAP]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1790</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span>LDAP high availability architecture: HAProxy load balancing, read/write split, connection pooling, cn=monitor metrics, and what to watch for at 10M+ entries.</p>
<p>The post <a href="https://linuxcent.com/ldap-high-availability/">LDAP High Availability: Load Balancing and Production Architecture</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 6</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 7</em><br />
<a href="/openldap-setup-replication/">EP06: OpenLDAP</a> → <strong>EP07</strong> → <a href="/freeipa-linux-identity-management/">EP08: FreeIPA</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>LDAP HA means multiple directory servers behind a load balancer — clients connect to a VIP, not to individual servers</li>
<li>Read/write split: all writes go to the provider, reads are distributed across consumers — the load balancer enforces this by routing on port or backend check</li>
<li>SSSD handles multi-server failover natively (<code class="" data-line="">ldap_uri</code> accepts a comma-separated list) — for apps without built-in failover, HAProxy with health checks does the work</li>
<li>Connection pooling is critical at scale — <code class="" data-line="">nss_ldap</code> and <code class="" data-line="">pam_ldap</code> opened a new connection per login; SSSD maintains a pool; apps that use libldap directly must implement their own</li>
<li><code class="" data-line="">cn=monitor</code> is the built-in monitoring endpoint — exposes connection counts, operation rates, and backend stats readable via <code class="" data-line="">ldapsearch</code></li>
<li>389-DS (Red Hat Directory Server) is the production choice for &gt;1M entries — purpose-built for large directories with a dedicated replication engine</li>
</ul>
<hr />
<h2 id="the-big-picture-production-ldap-topology">The Big Picture: Production LDAP Topology</h2>
<pre><code class="" data-line="">         Clients (SSSD, apps, VPN concentrators)
                      │
              ┌───────▼───────┐
              │   HAProxy VIP  │   ← single endpoint, port 389/636
              │  10.0.0.10     │
              └───────┬───────┘
                      │
          ┌───────────┼───────────┐
          ▼           ▼           ▼
   ldap1.corp.com  ldap2.corp.com  ldap3.corp.com
   (Provider)      (Consumer)      (Consumer)
   Reads + Writes  Reads only      Reads only
          │           ▲               ▲
          └───────────┴───────────────┘
               SyncRepl replication
</code></pre>
<p>EP06 built a two-node replicated directory. This episode covers what happens when the directory becomes infrastructure — when it needs to survive a node failure, handle thousands of connections, and be monitored like any other critical service.</p>
<hr />
<h2 id="haproxy-for-ldap">HAProxy for LDAP</h2>
<p>HAProxy is the standard choice for LDAP load balancing. Unlike HTTP, LDAP is a stateful protocol — once a client binds, subsequent operations on that connection share the authenticated session. The load balancer must use connection persistence, not per-request routing.</p>
<pre><code class="" data-line=""># /etc/haproxy/haproxy.cfg

global
    log /dev/log local0
    maxconn 50000

defaults
    mode tcp                  # LDAP is TCP, not HTTP
    timeout connect 5s
    timeout client  30s
    timeout server  30s
    option tcplog

# ── LDAP read/write split ─────────────────────────────────────────────

# Writes → provider only
frontend ldap-write
    bind *:389
    default_backend ldap-provider

backend ldap-provider
    balance first                   # always use first available (provider)
    option tcp-check
    tcp-check connect
    server ldap1 ldap1.corp.com:389 check inter 5s rise 2 fall 3
    server ldap2 ldap2.corp.com:389 check inter 5s rise 2 fall 3 backup

# Reads → all nodes round-robin
frontend ldap-read
    bind *:3389                     # internal read port
    default_backend ldap-consumers

backend ldap-consumers
    balance roundrobin
    option tcp-check
    tcp-check connect
    server ldap1 ldap1.corp.com:389 check inter 5s
    server ldap2 ldap2.corp.com:389 check inter 5s
    server ldap3 ldap3.corp.com:389 check inter 5s

# LDAPS (TLS)
frontend ldaps
    bind *:636
    default_backend ldap-consumers-tls

backend ldap-consumers-tls
    balance roundrobin
    server ldap1 ldap1.corp.com:636 check inter 5s ssl verify required ca-file /etc/ssl/certs/ca.pem
    server ldap2 ldap2.corp.com:636 check inter 5s ssl verify required ca-file /etc/ssl/certs/ca.pem
</code></pre>
<p>The health check (<code class="" data-line="">tcp-check connect</code>) just verifies TCP connectivity. For a more precise check — verifying that <code class="" data-line="">slapd</code> is actually responding to LDAP requests — use a custom script that runs <code class="" data-line="">ldapsearch</code> and checks the result code.</p>
<hr />
<h2 id="sssd-multi-server-failover">SSSD Multi-Server Failover</h2>
<p>SSSD has native failover — no load balancer required for SSSD-based clients:</p>
<pre><code class="" data-line=""># /etc/sssd/sssd.conf
[domain/corp.com]
ldap_uri = ldap://ldap1.corp.com, ldap://ldap2.corp.com, ldap://ldap3.corp.com
# SSSD tries them in order; switches to next on failure
# Switches back to primary after ldap_recovery_interval (default: 30s)

# For AD, discovery via DNS SRV records is even better:
ad_server = _srv_
# SSSD queries _ldap._tcp.corp.com SRV records and gets all DCs automatically
</code></pre>
<p>SSSD monitors the connection health. If the current server becomes unreachable, it switches to the next in the list within seconds. Existing cached data keeps serving during the switchover. Clients using SSSD don&#8217;t need a load balancer for basic HA.</p>
<hr />
<h2 id="connection-pooling">Connection Pooling</h2>
<p>Every LDAP bind creates an authenticated session on the server. A server with connection limits (<code class="" data-line="">olcConnMaxPending</code>, <code class="" data-line="">olcConnMaxPendingAuth</code> in OLC) will reject new connections when those limits are hit.</p>
<p>The problem: applications that use <code class="" data-line="">libldap</code> directly tend to open a new connection per operation. At 500 requests/second, that&#8217;s 500 new TCP connections, 500 binds, 500 TLS handshakes per second — a directory that can handle 5000 concurrent connections starts refusing new ones.</p>
<p>The solutions:</p>
<p><strong>SSSD</strong> — handles this automatically. SSSD maintains one or a small number of persistent connections per domain and multiplexes all PAM/NSS queries through them.</p>
<p><strong>Application-level pooling</strong> — frameworks like <code class="" data-line="">python-ldap</code> with connection pooling, <code class="" data-line="">ldap3</code> with connection strategies, or dedicated middleware like <code class="" data-line="">389-DS</code>&#8216;s Directory Proxy Server.</p>
<p><strong><code class="" data-line="">ldap_maxconnections</code></strong> in OpenLDAP — sets a hard limit. When hit, new connections block until existing ones close. Set this to something reasonable (<code class="" data-line="">olcConnMaxPending: 100</code> in OLC) so you get a controlled failure mode instead of unbounded queuing.</p>
<hr />
<h2 id="monitoring-with-cnmonitor">Monitoring with cn=monitor</h2>
<p>OpenLDAP exposes live operational statistics via the <code class="" data-line="">cn=monitor</code> database — a virtual LDAP subtree that reflects the server&#8217;s current state. Enable it:</p>
<pre><code class="" data-line=""># enable-monitor.ldif
dn: cn=module,cn=config
objectClass: olcModuleList
cn: module
olcModulePath: /usr/lib/ldap
olcModuleLoad: back_monitor

dn: olcDatabase=monitor,cn=config
objectClass: olcDatabaseConfig
olcDatabase: monitor
olcAccess: to *
  by dn=&quot;cn=admin,dc=corp,dc=com&quot; read
  by * none
</code></pre>
<p>Query it:</p>
<pre><code class="" data-line=""># Overall statistics
ldapsearch -x -H ldap://localhost \
  -D &quot;cn=admin,dc=corp,dc=com&quot; -w password \
  -b &quot;cn=monitor&quot; -s sub &quot;(objectClass=*)&quot; \
  monitorOpInitiated monitorOpCompleted

# Connection counts
ldapsearch -x -H ldap://localhost \
  -D &quot;cn=admin,dc=corp,dc=com&quot; -w password \
  -b &quot;cn=Connections,cn=monitor&quot; -s one \
  monitorConnectionNumber

# Operations by type
ldapsearch -x -H ldap://localhost \
  -D &quot;cn=admin,dc=corp,dc=com&quot; -w password \
  -b &quot;cn=Operations,cn=monitor&quot; -s one \
  monitorOpInitiated monitorOpCompleted
</code></pre>
<p>Useful metrics to export to Prometheus (via <code class="" data-line="">prometheus-openldap-exporter</code> or similar):<br />
&#8211; <code class="" data-line="">monitorOpCompleted</code> per operation type (bind, search, modify)<br />
&#8211; <code class="" data-line="">monitorConnectionNumber</code> — current connection count<br />
&#8211; Backend-specific: <code class="" data-line="">olmMDBEntries</code>, <code class="" data-line="">olmMDBPagesMax</code>, <code class="" data-line="">olmMDBPagesUsed</code></p>
<hr />
<h2 id="389-ds-ldap-at-scale">389-DS: LDAP at Scale</h2>
<p>OpenLDAP is excellent for directories up to a few million entries. When you need:<br />
&#8211; 10M+ entries<br />
&#8211; High write throughput (more than a few hundred writes/second)<br />
&#8211; Fine-grained replication filtering<br />
&#8211; A dedicated web-based admin UI</p>
<p>…389-DS (Red Hat Directory Server, community edition) is the production answer. It&#8217;s what FreeIPA uses under the hood.</p>
<p>Key architectural differences from OpenLDAP:</p>
<p><strong>Multi-supplier replication</strong> — 389-DS&#8217;s replication engine uses a dedicated changelog (stored in LMDB) and Change Sequence Numbers (CSNs) for conflict resolution. Multi-supplier (multi-master) replication is first-class, not a bolted-on feature.</p>
<p><strong>Changelog</strong> — every change is written to a persistent changelog before being applied. This enables precise replication: a consumer can reconnect after a network partition and get exactly the changes it missed, rather than doing a full resync.</p>
<p><strong>Plugin architecture</strong> — 389-DS functionality (replication, managed entries, DNA for automatic UID allocation, memberOf, password policy) is all implemented as plugins that can be enabled/disabled per directory instance.</p>
<pre><code class="" data-line=""># Install 389-DS
dnf install -y 389-ds-base

# Create a new instance
dscreate interactive
# — or use a template:
dscreate from-file /path/to/instance.inf

# Manage with dsctl
dsctl slapd-corp status
dsctl slapd-corp start
dsctl slapd-corp stop

# Admin with dsconf
dsconf slapd-corp backend suffix list
dsconf slapd-corp replication status -suffix &quot;dc=corp,dc=com&quot;
</code></pre>
<p>The <code class="" data-line="">dsconf replication status</code> command gives a live view of replication lag across all suppliers and consumers — something OpenLDAP requires you to compute manually from contextCSN comparisons.</p>
<hr />
<h2 id="global-catalog-cross-domain-search-in-ad">Global Catalog: Cross-Domain Search in AD</h2>
<p>When your directory spans multiple AD domains in a forest, the Global Catalog solves a specific problem: a user in <code class="" data-line="">emea.corp.com</code> needs to be found by an app that only knows <code class="" data-line="">corp.com</code>.</p>
<pre><code class="" data-line="">Forest: corp.com
  ├── corp.com       → DC port 389    full directory: 500K entries
  ├── emea.corp.com  → DC port 389    full directory: 200K entries
  └── Global Catalog → GC port 3268  partial replica: 700K entries
                                       (not all attributes — just the most queried ones)
</code></pre>
<p>The GC replicates a subset of attributes from every domain in the forest. By default: <code class="" data-line="">cn</code>, <code class="" data-line="">mail</code>, <code class="" data-line="">sAMAccountName</code>, <code class="" data-line="">userPrincipalName</code>, <code class="" data-line="">memberOf</code>, and about 150 others. Attributes marked with <code class="" data-line="">isMemberOfPartialAttributeSet</code> in the schema are replicated to the GC.</p>
<p>If an application is configured to use port 3268 instead of 389, it&#8217;s using the GC — and it won&#8217;t see attributes not included in the partial attribute set. This surprises teams that add a custom attribute to AD and then wonder why their application can&#8217;t see it on 3268 but can on 389.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>HAProxy TCP health checks don&#8217;t verify LDAP is responsive.</strong> A server can accept TCP connections but have <code class="" data-line="">slapd</code> in a degraded state (database corruption, out-of-memory). Build a proper LDAP health check: a script that binds and searches a known entry and checks the result.</p>
<p><strong>replication lag under write load.</strong> SyncRepl consumers can fall behind under sustained write load. Monitor the contextCSN difference between provider and consumers. If consumers are more than a few seconds behind, investigate the provider&#8217;s write throughput and the consumer&#8217;s processing speed.</p>
<p><strong>Directory size and the MDB mapsize.</strong> LMDB requires a pre-configured maximum database size (<code class="" data-line="">olcDbMaxSize</code>). If the database grows beyond this, <code class="" data-line="">slapd</code> starts failing writes. Set it to 2–4x your expected data size and monitor <code class="" data-line="">olmMDBPagesUsed / olmMDBPagesMax</code>.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>HAProxy in TCP mode provides LDAP load balancing — use <code class="" data-line="">balance first</code> for write routing (provider only), <code class="" data-line="">balance roundrobin</code> for reads</li>
<li>SSSD has native failover via <code class="" data-line="">ldap_uri</code> — for SSSD clients, a load balancer adds HA but isn&#8217;t strictly required</li>
<li><code class="" data-line="">cn=monitor</code> is the built-in OpenLDAP monitoring endpoint — export its counters to Prometheus for operational visibility</li>
<li>389-DS is the right choice for &gt;1M entries, high write throughput, or multi-supplier replication as a first-class feature</li>
<li>Global Catalog (port 3268/3269) is a partial replica of all AD domains — useful for forest-wide searches, but missing non-replicated attributes</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP07 covers the infrastructure layer. EP08 zooms out to FreeIPA — what you get when LDAP, Kerberos, DNS, PKI, and HBAC are integrated into a single Linux-native identity stack, and why most Linux shops running their own directory should be running FreeIPA instead of bare OpenLDAP.</p>
<p><em>Next: <a href="/freeipa-linux-identity-management/">FreeIPA: LDAP + Kerberos + PKI in a Single Linux Identity Stack</a></em></p>
<p>Get EP08 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&amp;linkname=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fldap-high-availability%2F&#038;title=LDAP%20High%20Availability%3A%20Load%20Balancing%20and%20Production%20Architecture" data-a2a-url="https://linuxcent.com/ldap-high-availability/" data-a2a-title="LDAP High Availability: Load Balancing and Production Architecture"></a></p><p>The post <a href="https://linuxcent.com/ldap-high-availability/">LDAP High Availability: Load Balancing and Production Architecture</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/ldap-high-availability/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1790</post-id>	</item>
		<item>
		<title>OpenLDAP Setup and Replication: Running Your Own Directory</title>
		<link>https://linuxcent.com/openldap-setup-replication/</link>
					<comments>https://linuxcent.com/openldap-setup-replication/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Tue, 05 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Directory Services]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[OpenLDAP]]></category>
		<category><![CDATA[Replication]]></category>
		<category><![CDATA[Security]]></category>
		<category><![CDATA[SyncRepl]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1787</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutes</span></span>Set up OpenLDAP with SyncRepl replication, OLC (cn=config), and MDB backend. Hands-on guide for a production-ready two-node replicated directory.</p>
<p>The post <a href="https://linuxcent.com/openldap-setup-replication/">OpenLDAP Setup and Replication: Running Your Own Directory</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 5</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 6</em><br />
<a href="/what-is-ldap/">EP01</a> → &#8230; → <a href="/how-kerberos-works-tickets-kdc/">EP05: Kerberos</a> → <strong>EP06</strong> → <a href="/ldap-high-availability-scale/">EP07: LDAP HA</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>OpenLDAP&#8217;s server process is <code class="" data-line="">slapd</code> — the backend that stores data is MDB (LMDB), a memory-mapped B-tree that replaced the old Berkeley DB backend</li>
<li>Configuration lives in the directory itself: <code class="" data-line="">cn=config</code> (OLC — Online Configuration) lets you modify <code class="" data-line="">slapd</code> at runtime without restarting</li>
<li>SyncRepl is the replication protocol: a consumer subscribes to a provider and stays in sync via either polling (<code class="" data-line="">refreshOnly</code>) or a persistent connection (<code class="" data-line="">refreshAndPersist</code>)</li>
<li>Multi-Provider (formerly Multi-Master) lets multiple nodes accept writes — conflict resolution uses CSN (Change Sequence Number), last-writer-wins</li>
<li>The essential tools: <code class="" data-line="">slapd</code>, <code class="" data-line="">ldapadd</code>, <code class="" data-line="">ldapmodify</code>, <code class="" data-line="">ldapsearch</code>, <code class="" data-line="">slapcat</code>, <code class="" data-line="">slaptest</code></li>
<li>Always build indexes on the attributes you search most — <code class="" data-line="">uid</code>, <code class="" data-line="">cn</code>, <code class="" data-line="">memberOf</code> — or every search is a full scan</li>
</ul>
<hr />
<h2 id="the-big-picture-slapd-architecture">The Big Picture: slapd Architecture</h2>
<pre><code class="" data-line="">ldapsearch / ldapadd / SSSD / any LDAP client
              │ TCP 389 / 636
              ▼
         ┌─────────────────────────────────┐
         │  slapd (OpenLDAP server)         │
         │                                 │
         │  Frontend (protocol layer)       │
         │    • parse BER requests          │
         │    • ACL enforcement             │
         │    • schema validation           │
         │                                 │
         │  Backend (storage layer)         │
         │    • MDB (LMDB) — default       │
         │    • memory-mapped file I/O      │
         │    • ACID transactions           │
         └────────────┬────────────────────┘
                      │
              /var/lib/ldap/
              data.mdb   (the directory data)
              lock.mdb   (LMDB lock file)
</code></pre>
<p>EP05 showed Kerberos in isolation. OpenLDAP is where you run the identity store that Kerberos references — and where SSSD looks up user and group attributes. This episode builds a working two-node replicated directory from scratch.</p>
<hr />
<h2 id="installation">Installation</h2>
<pre><code class="" data-line=""># Ubuntu / Debian
apt-get install -y slapd ldap-utils

# RHEL / Rocky / AlmaLinux
dnf install -y openldap-servers openldap-clients

# After install — Ubuntu runs a configuration wizard
# Skip it: dpkg-reconfigure slapd
# Or answer it and then switch to OLC management
</code></pre>
<p>On RHEL-family systems, <code class="" data-line="">slapd</code> is not configured after install — you work entirely through OLC from the start.</p>
<hr />
<h2 id="olc-the-directory-configures-itself">OLC: The Directory Configures Itself</h2>
<p>The old way was <code class="" data-line="">slapd.conf</code> — a static file that required a full restart on every change. OLC (Online Configuration) replaced it: <code class="" data-line="">slapd</code>&#8216;s own configuration is stored as LDAP entries under <code class="" data-line="">cn=config</code>. You modify configuration the same way you modify data — with <code class="" data-line="">ldapmodify</code>. Changes take effect immediately.</p>
<pre><code class="" data-line="">cn=config                        ← root config entry
├── cn=schema,cn=config          ← schema definitions
│     ├── cn={0}core             ← core schema
│     ├── cn={1}cosine           ← RFC 1274 attributes
│     └── cn={2}inetorgperson    ← inetOrgPerson object class
├── olcDatabase={-1}frontend     ← default settings for all databases
├── olcDatabase={0}config        ← the config database itself
└── olcDatabase={1}mdb           ← your actual directory data
      ├── olcAccess              ← ACLs
      ├── olcSuffix              ← base DN (e.g., dc=corp,dc=com)
      └── olcDbIndex             ← search indexes
</code></pre>
<p>Everything under <code class="" data-line="">cn=config</code> has attributes prefixed with <code class="" data-line="">olc</code> (OpenLDAP Configuration). You query and modify it just like any other LDAP subtree — with one restriction: only the <code class="" data-line="">cn=config</code> admin (usually <code class="" data-line="">gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth</code> — the local root via SASL EXTERNAL) can write to it.</p>
<hr />
<h2 id="bootstrapping-a-directory">Bootstrapping a Directory</h2>
<p>The quickest way to get a working directory is a set of LDIF files applied in order.</p>
<h3 id="1-load-schemas">1. Load schemas</h3>
<pre><code class="" data-line=""># Apply the schemas OpenLDAP ships with
ldapadd -Y EXTERNAL -H ldapi:/// \
  -f /etc/ldap/schema/cosine.ldif
ldapadd -Y EXTERNAL -H ldapi:/// \
  -f /etc/ldap/schema/inetorgperson.ldif
ldapadd -Y EXTERNAL -H ldapi:/// \
  -f /etc/ldap/schema/nis.ldif       # adds posixAccount, posixGroup
</code></pre>
<h3 id="2-configure-the-mdb-database">2. Configure the MDB database</h3>
<pre><code class="" data-line=""># mdb-config.ldif
dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcSuffix
olcSuffix: dc=corp,dc=com
-
replace: olcRootDN
olcRootDN: cn=admin,dc=corp,dc=com
-
replace: olcRootPW
olcRootPW: {SSHA}hashed_password_here
</code></pre>
<p>Generate the hash: <code class="" data-line="">slappasswd -s yourpassword</code></p>
<pre><code class="" data-line="">ldapmodify -Y EXTERNAL -H ldapi:/// -f mdb-config.ldif
</code></pre>
<h3 id="3-add-indexes">3. Add indexes</h3>
<pre><code class="" data-line=""># indexes.ldif
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcDbIndex
olcDbIndex: uid eq,pres
olcDbIndex: cn eq,sub
olcDbIndex: sn eq,sub
olcDbIndex: mail eq
olcDbIndex: memberOf eq
olcDbIndex: entryCSN eq
olcDbIndex: entryUUID eq
</code></pre>
<p>The last two (<code class="" data-line="">entryCSN</code>, <code class="" data-line="">entryUUID</code>) are required for SyncRepl replication to work efficiently.</p>
<h3 id="4-load-initial-data">4. Load initial data</h3>
<pre><code class="" data-line=""># base.ldif
dn: dc=corp,dc=com
objectClass: top
objectClass: dcObject
objectClass: organization
o: Corp
dc: corp

dn: ou=people,dc=corp,dc=com
objectClass: organizationalUnit
ou: people

dn: ou=groups,dc=corp,dc=com
objectClass: organizationalUnit
ou: groups

dn: uid=vamshi,ou=people,dc=corp,dc=com
objectClass: inetOrgPerson
objectClass: posixAccount
objectClass: shadowAccount
cn: Vamshi Krishna
sn: Krishna
uid: vamshi
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/vamshi
loginShell: /bin/bash
mail: vamshi@corp.com
userPassword: {SSHA}hashed_password_here
</code></pre>
<pre><code class="" data-line="">ldapadd -x -H ldap://localhost \
  -D &quot;cn=admin,dc=corp,dc=com&quot; \
  -w adminpassword \
  -f base.ldif
</code></pre>
<hr />
<h2 id="acls-who-can-read-what">ACLs: Who Can Read What</h2>
<p>OpenLDAP ACLs are evaluated top-to-bottom; first match wins.</p>
<pre><code class="" data-line=""># acls.ldif — set via OLC
dn: olcDatabase={1}mdb,cn=config
changetype: modify
replace: olcAccess
# Users can change their own passwords
olcAccess: to attrs=userPassword
  by self write
  by anonymous auth
  by * none
# Users can read their own entry
olcAccess: to dn.base=&quot;ou=people,dc=corp,dc=com&quot;
  by self read
  by users read
  by * none
# Service accounts can read everything (for SSSD)
olcAccess: to *
  by dn=&quot;cn=svc-ldap,ou=services,dc=corp,dc=com&quot; read
  by self read
  by * none
</code></pre>
<p>A service account (<code class="" data-line="">cn=svc-ldap</code>) that SSSD uses to search the directory needs read access to <code class="" data-line="">ou=people</code> and <code class="" data-line="">ou=groups</code>. Never give SSSD admin (write) access.</p>
<hr />
<h2 id="syncrepl-replication">SyncRepl Replication</h2>
<p>SyncRepl is a pull-based replication protocol built on the LDAP Sync operation (RFC 4533). A consumer connects to a provider and requests changes. The provider sends them. The consumer stays in sync.</p>
<h3 id="on-the-provider-enable-the-syncprov-overlay">On the Provider: Enable the syncprov overlay</h3>
<pre><code class="" data-line=""># syncprov.ldif
dn: olcOverlay=syncprov,olcDatabase={1}mdb,cn=config
objectClass: olcOverlayConfig
objectClass: olcSyncProvConfig
olcOverlay: syncprov
olcSpCheckpoint: 100 10     # checkpoint every 100 ops or 10 minutes
olcSpSessionLog: 100        # keep last 100 changes for delta-sync
</code></pre>
<pre><code class="" data-line="">ldapadd -Y EXTERNAL -H ldapi:/// -f syncprov.ldif
</code></pre>
<h3 id="on-the-consumer-configure-syncrepl">On the Consumer: Configure syncrepl</h3>
<pre><code class="" data-line=""># consumer-config.ldif
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcSyncrepl
olcSyncrepl: rid=001
  provider=ldap://ldap1.corp.com:389
  bindmethod=simple
  binddn=&quot;cn=repl-svc,dc=corp,dc=com&quot;
  credentials=replication-password
  searchbase=&quot;dc=corp,dc=com&quot;
  scope=sub
  schemachecking=on
  type=refreshAndPersist    # persistent connection (vs refreshOnly = polling)
  retry=&quot;5 5 60 +&quot;          # retry: 5 times every 5s, then every 60s forever
  interval=00:00:05:00      # (for refreshOnly) sync every 5 minutes
-
add: olcUpdateRef
olcUpdateRef: ldap://ldap1.corp.com   # redirect writes to provider
</code></pre>
<p><code class="" data-line="">refreshAndPersist</code> keeps a persistent connection open. Changes replicate within milliseconds. <code class="" data-line="">refreshOnly</code> polls on an interval — simpler, but adds latency.</p>
<h3 id="verify-replication">Verify Replication</h3>
<pre><code class="" data-line=""># On provider: check the contextCSN (the sync state token)
ldapsearch -x -H ldap://ldap1.corp.com \
  -D &quot;cn=admin,dc=corp,dc=com&quot; -w password \
  -b &quot;dc=corp,dc=com&quot; -s base contextCSN
# contextCSN: 20260427010000.000000Z#000000#000#000000

# On consumer: should match after sync
ldapsearch -x -H ldap://ldap2.corp.com \
  -D &quot;cn=admin,dc=corp,dc=com&quot; -w password \
  -b &quot;dc=corp,dc=com&quot; -s base contextCSN
# Same CSN = in sync
</code></pre>
<hr />
<h2 id="multi-provider-accepting-writes-on-both-nodes">Multi-Provider: Accepting Writes on Both Nodes</h2>
<p>Standard SyncRepl has one provider and one or more consumers — only the provider accepts writes. Multi-Provider (formerly Multi-Master) lets every node accept writes.</p>
<pre><code class="" data-line=""># On each node — add mirrormode to the database config
dn: olcDatabase={1}mdb,cn=config
changetype: modify
add: olcMirrorMode
olcMirrorMode: TRUE
</code></pre>
<p>With mirrormode enabled and each node configured as both provider and consumer of the other, writes on either node replicate to the other. Conflict resolution is CSN-based (Change Sequence Number) — a monotonically increasing timestamp. Last write wins at the attribute level.</p>
<p>Multi-Provider does not prevent split-brain conflicts — if two clients write the same attribute on two different nodes during a network partition, the higher CSN wins when the partition heals. For most directory use cases (user passwords, group memberships), this is acceptable. For others, it requires careful thought.</p>
<hr />
<h2 id="production-gotchas"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Production Gotchas</h2>
<p><strong>MDB data file grows monotonically.</strong> LMDB never shrinks the data file automatically. Deleted entries leave free space inside the file that gets reused, but the file on disk doesn&#8217;t shrink. Use <code class="" data-line="">slapcat</code> to export and <code class="" data-line="">slapadd</code> to reimport if you need to reclaim disk space.</p>
<p><strong><code class="" data-line="">slapcat</code> is the only safe backup.</strong> <code class="" data-line="">slapcat</code> reads the MDB database directly and exports LDIF — it does not go through <code class="" data-line="">slapd</code>. Run it while <code class="" data-line="">slapd</code> is running (LMDB is MVCC-safe for readers), but never copy the raw MDB files while <code class="" data-line="">slapd</code> is running.</p>
<p><strong>Schema changes on a replicated directory require coordination.</strong> Load the new schema on the provider first. SyncRepl will propagate it to consumers — but if a consumer gets a new entry using the new schema before the schema itself is replicated, the import will fail. Load schemas manually on all nodes before adding entries that use them.</p>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>OpenLDAP uses LMDB (MDB backend) — a memory-mapped, ACID-compliant storage engine with no external dependency</li>
<li>OLC (<code class="" data-line="">cn=config</code>) is the right way to configure <code class="" data-line="">slapd</code> — changes apply without restarts</li>
<li>SyncRepl pulls changes from a provider to a consumer — <code class="" data-line="">refreshAndPersist</code> for near-real-time, <code class="" data-line="">refreshOnly</code> for poll-based</li>
<li>Always index <code class="" data-line="">uid</code>, <code class="" data-line="">cn</code>, <code class="" data-line="">entryCSN</code>, and <code class="" data-line="">entryUUID</code> — unindexed searches are full scans</li>
<li>Multi-Provider allows writes on all nodes with CSN-based last-write-wins conflict resolution</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>A single OpenLDAP server works. Two nodes with SyncRepl work better. EP07 goes further: how you put multiple LDAP servers behind a load balancer, how connection pooling works, what to monitor, and how 389-DS handles directories with tens of millions of entries.</p>
<p><em>Next: <a href="/ldap-high-availability-scale/">LDAP High Availability: Load Balancing and Production Architecture</a></em></p>
<p>Get EP07 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&amp;linkname=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fopenldap-setup-replication%2F&#038;title=OpenLDAP%20Setup%20and%20Replication%3A%20Running%20Your%20Own%20Directory" data-a2a-url="https://linuxcent.com/openldap-setup-replication/" data-a2a-title="OpenLDAP Setup and Replication: Running Your Own Directory"></a></p><p>The post <a href="https://linuxcent.com/openldap-setup-replication/">OpenLDAP Setup and Replication: Running Your Own Directory</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/openldap-setup-replication/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1787</post-id>	</item>
		<item>
		<title>How Kerberos Works: Tickets, KDC, and Why Enterprises Use It With LDAP</title>
		<link>https://linuxcent.com/how-kerberos-works/</link>
					<comments>https://linuxcent.com/how-kerberos-works/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 04 May 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[Identity Management]]></category>
		<category><![CDATA[Kerberos]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Security]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1784</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span>How Kerberos works: the KDC, TGT flow, ticket-granting service, and why enterprises pair it with LDAP — explained with kinit, klist, and wire-level detail.</p>
<p>The post <a href="https://linuxcent.com/how-kerberos-works/">How Kerberos Works: Tickets, KDC, and Why Enterprises Use It With LDAP</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 5</em><br />
<a href="/what-is-ldap/">EP01</a> → <a href="/ldap-internals-directory-structure/">EP02</a> → <a href="/ldap-authentication-linux-pam-nss/">EP03</a> → <a href="/sssd-linux-authentication/">EP04: SSSD</a> → <strong>EP05</strong> → <a href="/openldap-setup-replication/">EP06: OpenLDAP</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>Kerberos is a network authentication protocol — it proves identity without sending passwords over the network, using time-limited cryptographic tickets</li>
<li>Three actors: the client, the KDC (Key Distribution Center), and the service — the KDC issues tickets; clients use tickets to authenticate to services</li>
<li>The ticket flow: AS-REQ (get a TGT) → TGS-REQ (exchange TGT for a service ticket) → AP-REQ (present service ticket to the target service)</li>
<li>A TGT (Ticket-Granting Ticket) is a session credential — it lets you request service tickets without re-entering your password for the lifetime of the ticket (default 10 hours)</li>
<li>LDAP + Kerberos together: LDAP stores identity (who you are), Kerberos authenticates it (proves you are who you say you are) — Active Directory is exactly this combination</li>
<li><code class="" data-line="">kinit</code>, <code class="" data-line="">klist</code>, <code class="" data-line="">kdestroy</code> are the hands-on tools — run them and read the ticket output</li>
</ul>
<hr />
<h2 id="the-big-picture-three-actors-three-steps">The Big Picture: Three Actors, Three Steps</h2>
<pre><code class="" data-line="">         1. AS-REQ / AS-REP
Client ◄────────────────────► AS (Authentication Server)
  │                                     │
  │    (part of KDC)                    │
  │                                     ▼
  │         2. TGS-REQ / TGS-REP   TGS (Ticket-Granting Server)
  ├───────────────────────────────────►│
  │         (part of KDC)              │
  │                                    │
  │    3. AP-REQ / AP-REP              │
  └─────────────────────────────► Service (SSH, LDAP, NFS, HTTP...)

KDC = AS + TGS (usually the same process, same machine)
</code></pre>
<p>EP04 mentioned Kerberos tickets and clock skew requirements without explaining the protocol. This episode explains why Kerberos was invented, what a ticket actually is, and how the three-step flow works — so that when SSSD says &#8220;KDC unreachable&#8221; or <code class="" data-line="">kinit</code> fails with &#8220;pre-authentication required,&#8221; you know exactly what&#8217;s happening.</p>
<hr />
<h2 id="the-problem-kerberos-was-built-to-solve">The Problem Kerberos Was Built to Solve</h2>
<p>MIT&#8217;s Project Athena started in 1983 — a campus-wide computing initiative giving students access to thousands of workstations. The problem: how do you authenticate a student at workstation 847 to a file server across campus without sending their password over the network?</p>
<p>In 1988, Steve Miller and Clifford Neuman published Kerberos version 4. The core insight: a trusted third party (the KDC) can issue cryptographic proof that a user has authenticated, and that proof can be presented to any service on the network without the service ever seeing the user&#8217;s password.</p>
<p>The password never leaves the client machine after the initial authentication. Every subsequent authentication — to a different service, to the same service again — uses a ticket. The KDC knows both the client and the service. The client and service only need to trust the KDC.</p>
<hr />
<h2 id="keys-tickets-and-sessions">Keys, Tickets, and Sessions</h2>
<p>Before the protocol, the primitives:</p>
<p><strong>Long-term keys</strong> — derived from passwords. When you set a password in Kerberos, it&#8217;s hashed into a key stored in the KDC database (in the <code class="" data-line="">krbtgt</code> account on AD, in <code class="" data-line="">/var/lib/krb5kdc/principal</code> on MIT Kerberos). The client also derives this key from the password at authentication time. Neither ever sends the raw password.</p>
<p><strong>Session keys</strong> — temporary symmetric keys created by the KDC for a specific session. They&#8217;re valid for the ticket&#8217;s lifetime. After the ticket expires, the session key is useless.</p>
<p><strong>Tickets</strong> — encrypted blobs issued by the KDC. A ticket contains the session key, the client identity, the expiry time, and optional flags. It&#8217;s encrypted with the target service&#8217;s long-term key — only the service can decrypt it. The client carries the ticket but can&#8217;t read the contents.</p>
<hr />
<h2 id="the-three-step-flow">The Three-Step Flow</h2>
<h3 id="step-1-as-req-as-rep-getting-a-tgt">Step 1: AS-REQ / AS-REP — Getting a TGT</h3>
<pre><code class="" data-line="">Client                        KDC (AS component)
  │                                │
  │── AS-REQ ──────────────────────►
  │   {username, timestamp}         │
  │   (timestamp encrypted with     │
  │    client&#039;s long-term key)       │
  │                                 │
  │   KDC verifies: decrypts        │
  │   timestamp with stored key.    │
  │   If valid → issues TGT         │
  │                                 │
  ◄── AS-REP ──────────────────────│
      {session_key_enc_with_client, │
       TGT_enc_with_krbtgt_key}     │
</code></pre>
<p>The client decrypts the session key using its long-term key (derived from the password). The TGT is encrypted with the KDC&#8217;s own key (<code class="" data-line="">krbtgt</code>) — the client can&#8217;t read it, but carries it.</p>
<p>This is the step that requires the password. After this, the TGT is what the client uses for everything else.</p>
<h3 id="step-2-tgs-req-tgs-rep-getting-a-service-ticket">Step 2: TGS-REQ / TGS-REP — Getting a Service Ticket</h3>
<pre><code class="" data-line="">Client                        KDC (TGS component)
  │                                │
  │── TGS-REQ ─────────────────────►
  │   {TGT, authenticator,         │
  │    target_service_name}        │
  │   (authenticator encrypted      │
  │    with TGT session key)        │
  │                                 │
  │   KDC: decrypts TGT,           │
  │   verifies authenticator,       │
  │   issues service ticket         │
  │                                 │
  ◄── TGS-REP ────────────────────│
      {service_session_key_enc,    │
       service_ticket_enc_with_    │
       service_long_term_key}      │
</code></pre>
<p>No password involved. The client proves its identity by presenting the TGT (which only the KDC can issue) and an authenticator (a timestamp encrypted with the TGT&#8217;s session key, proving the client holds the session key without revealing it).</p>
<h3 id="step-3-ap-req-ap-rep-authenticating-to-the-service">Step 3: AP-REQ / AP-REP — Authenticating to the Service</h3>
<pre><code class="" data-line="">Client                        Service (sshd, LDAP, NFS...)
  │                                │
  │── AP-REQ ──────────────────────►
  │   {service_ticket,             │
  │    authenticator_enc_with_      │
  │    service_session_key}        │
  │                                 │
  │   Service: decrypts ticket      │
  │   with its long-term key,       │
  │   verifies authenticator        │
  │                                 │
  ◄── AP-REP (optional) ───────────│
      {mutual authentication}       │
</code></pre>
<p>The service decrypts the ticket using its own key. It extracts the client identity and session key. It verifies the authenticator. No communication with the KDC required — the service trusts what the KDC signed.</p>
<hr />
<h2 id="why-clock-skew-matters">Why Clock Skew Matters</h2>
<p>Every Kerberos authenticator contains a timestamp. The service rejects authenticators older than 5 minutes (by default) — this prevents replay attacks where an attacker captures an authenticator and replays it later.</p>
<p>This is why clock skew over 5 minutes breaks Kerberos authentication entirely. If your machine&#8217;s clock drifts 6 minutes from the KDC, every authenticator you generate is rejected as too old or too far in the future. No tickets. No AD logins. No SSSD authentication.</p>
<pre><code class="" data-line=""># Check time sync status
timedatectl status
chronyc tracking        # if using chrony
ntpq -p                 # if using ntpd

# If clock is off: force a sync
chronyc makestep        # immediate step correction (chrony)
</code></pre>
<hr />
<h2 id="hands-on-kinit-klist-kdestroy">Hands-On: kinit, klist, kdestroy</h2>
<pre><code class="" data-line=""># Get a TGT (will prompt for password)
kinit vamshi@CORP.COM

# Show current tickets
klist
# Credentials cache: FILE:/tmp/krb5cc_1001
# Principal: vamshi@CORP.COM
#
# Valid starting     Expires            Service principal
# 04/27/26 01:00:00  04/27/26 11:00:00  krbtgt/CORP.COM@CORP.COM
#   renew until 05/04/26 01:00:00

# Show encryption types used (the -e flag)
klist -e
# 04/27/26 01:00:00  04/27/26 11:00:00  krbtgt/CORP.COM@CORP.COM
#         Etype: aes256-cts-hmac-sha1-96, aes256-cts-hmac-sha1-96

# Get a service ticket for a specific service
kvno host/server.corp.com@CORP.COM
# host/server.corp.com@CORP.COM: kvno = 3

# Show all tickets including service tickets
klist -f
# Flags: F=forwardable, f=forwarded, P=proxiable, p=proxy, D=postdated,
#        d=postdated, R=renewable, I=initial, i=invalid, H=hardware auth

# Destroy all tickets
kdestroy
</code></pre>
<p>The <code class="" data-line="">Valid starting</code> and <code class="" data-line="">Expires</code> fields are the ticket lifetime. After expiry, you need to re-authenticate (or renew the ticket if it&#8217;s within the <code class="" data-line="">renew until</code> window). The <code class="" data-line="">renew until</code> date is when even renewal stops working.</p>
<hr />
<h2 id="etckrb5conf">/etc/krb5.conf</h2>
<pre><code class="" data-line="">[libdefaults]
    default_realm = CORP.COM
    dns_lookup_realm = false
    dns_lookup_kdc = true         # find KDCs via DNS SRV records
    ticket_lifetime = 10h
    renew_lifetime = 7d
    forwardable = true            # tickets can be forwarded to remote hosts (needed for SSH forwarding)
    rdns = false

[realms]
    CORP.COM = {
        kdc = dc01.corp.com
        kdc = dc02.corp.com       # failover KDC
        admin_server = dc01.corp.com
    }

[domain_realm]
    .corp.com = CORP.COM
    corp.com = CORP.COM
</code></pre>
<p>With <code class="" data-line="">dns_lookup_kdc = true</code>, Kerberos finds KDCs by querying DNS SRV records (<code class="" data-line="">_kerberos._tcp.corp.com</code>). AD sets these up automatically. On MIT Kerberos, you add them manually. DNS-based discovery is the recommended approach for AD environments — it picks up new DCs automatically.</p>
<hr />
<h2 id="kerberos-ldap-why-enterprises-run-both">Kerberos + LDAP: Why Enterprises Run Both</h2>
<p>LDAP and Kerberos solve different problems and are almost always deployed together:</p>
<pre><code class="" data-line="">LDAP answers:  &quot;Who is vamshi? What groups is he in? What&#039;s his home directory?&quot;
Kerberos answers: &quot;Is this really vamshi? Prove it without sending a password.&quot;
</code></pre>
<p>Active Directory is exactly this combination — the directory is LDAP-based, the authentication is Kerberos. When a Linux machine joins an AD domain via <code class="" data-line="">realm join</code> or <code class="" data-line="">adcli</code>, it gets:<br />
&#8211; LDAP access to the AD directory (for NSS: user and group lookups)<br />
&#8211; A Kerberos principal registered in AD (for PAM: ticket-based authentication)<br />
&#8211; A machine account (the machine&#8217;s identity in the directory)</p>
<p>When you SSH into an AD-joined Linux machine:<br />
1. SSSD issues a Kerberos AS-REQ for the user&#8217;s TGT<br />
2. SSSD uses the TGT to get a service ticket for the Linux machine&#8217;s PAM service<br />
3. Authentication is verified via the service ticket — no LDAP Bind with a password<br />
4. SSSD does an LDAP Search to get POSIX attributes (UID, GID, home dir)</p>
<p>Password-based LDAP Bind is the fallback when Kerberos isn&#8217;t available. Kerberos is the default on AD-joined systems — and it&#8217;s more secure because the password never leaves the client.</p>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;Kerberos sends your password to the KDC.&#8221;</strong> It doesn&#8217;t. The client derives a key from the password locally and uses that key to encrypt a timestamp (the pre-authentication data). The KDC verifies the timestamp using the stored key. The raw password never travels.</p>
<p><strong>&#8220;Kerberos is an authorization protocol.&#8221;</strong> Kerberos authenticates — it proves who you are. Authorization (what you can do) is a separate decision, usually handled by ACLs on the service or directory group membership.</p>
<p><strong>&#8220;Once you have a TGT, you&#8217;re authenticated to everything.&#8221;</strong> A TGT only proves your identity to the KDC. Each service requires a separate service ticket. The TGT is what lets you get those service tickets without re-entering your password.</p>
<p><strong>&#8220;Kerberos requires AD.&#8221;</strong> MIT Kerberos 5 is a standalone implementation. FreeIPA (EP08) runs MIT Kerberos. Heimdal is another implementation. AD uses a Microsoft-extended version of Kerberos 5, but the core protocol is the same RFC.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>Kerberos is the de facto enterprise authentication protocol — SSO, delegation, and service account authentication all depend on it</td>
</tr>
<tr>
<td>CISSP Domain 4: Communications and Network Security</td>
<td>Kerberos prevents credential sniffing and replay attacks — two of the core network authentication threat categories</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The KDC is a critical single point of trust — its availability, key management, and account (<code class="" data-line="">krbtgt</code>) rotation are architectural security decisions</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>Kerberos is a ticket-based protocol — the password is used once to get a TGT; from then on, tickets prove identity without the password</li>
<li>The three-step flow: get a TGT from the AS, exchange it for a service ticket at the TGS, present the service ticket to the target service</li>
<li>Clock skew over 5 minutes breaks Kerberos — time synchronization is a hard dependency</li>
<li>LDAP stores identity; Kerberos authenticates it — Active Directory is exactly this combination, and so is FreeIPA</li>
<li><code class="" data-line="">klist -e</code> shows the encryption types in use — <code class="" data-line="">aes256-cts-hmac-sha1-96</code> is what you want to see; <code class="" data-line="">arcfour-hmac</code> (RC4) is legacy and should be disabled</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP05 covered Kerberos as a protocol. EP06 goes hands-on: building a real LDAP directory with OpenLDAP, configuring replication, and understanding how the server-side components — <code class="" data-line="">slapd</code>, the MDB backend, SyncRepl — fit together.</p>
<p><em>Next: <a href="/openldap-setup-replication/">OpenLDAP Setup and Replication: Running Your Own Directory</a></em></p>
<p>Get EP06 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&amp;linkname=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fhow-kerberos-works%2F&#038;title=How%20Kerberos%20Works%3A%20Tickets%2C%20KDC%2C%20and%20Why%20Enterprises%20Use%20It%20With%20LDAP" data-a2a-url="https://linuxcent.com/how-kerberos-works/" data-a2a-title="How Kerberos Works: Tickets, KDC, and Why Enterprises Use It With LDAP"></a></p><p>The post <a href="https://linuxcent.com/how-kerberos-works/">How Kerberos Works: Tickets, KDC, and Why Enterprises Use It With LDAP</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/how-kerberos-works/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1784</post-id>	</item>
		<item>
		<title>SSSD: The Caching Daemon That Powers Every Enterprise Linux Login</title>
		<link>https://linuxcent.com/sssd-linux-authentication/</link>
					<comments>https://linuxcent.com/sssd-linux-authentication/#respond</comments>
		
		<dc:creator><![CDATA[Vamshi Krishna Santhapuri]]></dc:creator>
		<pubDate>Mon, 27 Apr 2026 05:00:00 +0000</pubDate>
				<category><![CDATA[Identity & Authentication]]></category>
		<category><![CDATA[Active Directory]]></category>
		<category><![CDATA[Authentication]]></category>
		<category><![CDATA[LDAP]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[linux-security]]></category>
		<category><![CDATA[PAM]]></category>
		<category><![CDATA[SSSD]]></category>
		<guid isPermaLink="false">https://linuxcent.com/?p=1781</guid>

					<description><![CDATA[<p><span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span>SSSD is the daemon behind every enterprise Linux login — offline caching, PAM integration, multi-domain support. Here's how it works and how to debug it.</p>
<p>The post <a href="https://linuxcent.com/sssd-linux-authentication/">SSSD: The Caching Daemon That Powers Every Enterprise Linux Login</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></description>
										<content:encoded><![CDATA[<span class="span-reading-time rt-reading-time" style="display: block;"><span class="rt-label rt-prefix">Reading Time: </span> <span class="rt-time"> 7</span> <span class="rt-label rt-postfix">minutes</span></span><style>
pre{position:relative;background:#1e1e1e;color:#d4d4d4;
    padding:16px 16px 16px 20px;border-radius:6px;overflow-x:auto;
    font-family:'JetBrains Mono','Fira Code','Cascadia Code',Consolas,'Courier New',monospace;
    font-size:.88em;line-height:1.6;border-left:4px solid #555}
code{background:#f4f4f4;padding:2px 5px;border-radius:3px;font-size:.9em}
pre code{background:transparent;padding:0;color:inherit}
pre[data-lang="bash"],pre[data-lang="sh"],
pre[data-lang="shell"],pre[data-lang="zsh"]{border-left-color:#4ec9b0}
pre[data-lang="yaml"],pre[data-lang="json"],
pre[data-lang="toml"],pre[data-lang="xml"]{border-left-color:#569cd6}
pre[data-lang="python"],pre[data-lang="go"],pre[data-lang="rust"],
pre[data-lang="java"],pre[data-lang="c"],pre[data-lang="cpp"]{border-left-color:#c586c0}
pre[data-lang="text"],pre[data-lang="output"],
pre[data-lang="console"]{border-left-color:#888}
.lc-copy-btn{position:absolute;top:8px;right:8px;background:#2d2d2d;color:#ccc;
    border:1px solid #444;border-radius:4px;padding:3px 9px;font-size:.75em;
    font-family:system-ui,sans-serif;cursor:pointer;opacity:0;
    transition:opacity .15s,background .15s;line-height:1.6}
pre:hover .lc-copy-btn{opacity:1}
.lc-copy-btn:hover{background:#3a3a3a;color:#fff}
.lc-copy-btn.copied{color:#4ec9b0;border-color:#4ec9b0}
.lc-lang-badge{position:absolute;top:8px;left:20px;font-family:system-ui,sans-serif;
    font-size:.7em;color:#666;text-transform:uppercase;letter-spacing:.04em;
    line-height:1;pointer-events:none;opacity:0;transition:opacity .15s}
pre:hover .lc-lang-badge{opacity:1}
table{border-collapse:collapse;width:100%;margin:16px 0}
th,td{border:1px solid #ddd;padding:10px 14px;text-align:left}
th{background:#f0f0f0;font-weight:600}
tr:nth-child(even){background:#fafafa}
</style>
<p><script>
(function(){
  if(window.__lcCodeEnhanced)return;
  window.__lcCodeEnhanced=true;
  function enhance(){
    document.querySelectorAll('pre').forEach(function(pre){
      var code=pre.querySelector('code');
      var lang='';
      if(code){var m=(code.className||'').match(/language-(\S+)/);if(m)lang=m[1].toLowerCase();}
      if(lang)pre.setAttribute('data-lang',lang);
      if(lang){var badge=document.createElement('span');badge.className='lc-lang-badge';badge.textContent=lang;pre.insertBefore(badge,pre.firstChild);}
      var btn=document.createElement('button');
      btn.className='lc-copy-btn';btn.textContent='Copy';btn.setAttribute('aria-label','Copy code to clipboard');
      pre.appendChild(btn);
      btn.addEventListener('click',function(){
        var text=code?code.innerText:pre.innerText;
        if(navigator.clipboard&&window.isSecureContext){
          navigator.clipboard.writeText(text).then(function(){ok(btn);}).catch(function(){fb(text,btn);});
        }else{fb(text,btn);}
      });
    });
  }
  function ok(btn){btn.textContent='Copied!';btn.classList.add('copied');setTimeout(function(){btn.textContent='Copy';btn.classList.remove('copied');},2000);}
  function fb(text,btn){
    try{var ta=document.createElement('textarea');ta.value=text;ta.style.cssText='position:fixed;left:-9999px;top:-9999px;opacity:0';document.body.appendChild(ta);ta.select();document.execCommand('copy');document.body.removeChild(ta);ok(btn);}
    catch(e){btn.textContent='✗ Failed';setTimeout(function(){btn.textContent='Copy';},2000);}
  }
  if(document.readyState==='loading'){document.addEventListener('DOMContentLoaded',enhance);}else{enhance();}
})();
</script></p>
<p><em>The Identity Stack, Episode 4</em><br />
<a href="/what-is-ldap/">EP01: What Is LDAP</a> → <a href="/ldap-internals-directory-structure/">EP02: LDAP Internals</a> → <a href="/ldap-authentication-linux-pam-nss/">EP03: LDAP Auth on Linux</a> → <strong>EP04</strong> → <a href="/how-kerberos-works-tickets-kdc/">EP05: Kerberos</a> → &#8230;</p>
<hr />
<h2 id="tldr">TL;DR</h2>
<ul>
<li>SSSD (System Security Services Daemon) is the caching and brokering layer between Linux and directory services — it handles LDAP, Kerberos, and AD so PAM and NSS don&#8217;t have to</li>
<li>Architecture: three tiers — responders (answer PAM/NSS queries), providers (talk to AD/LDAP/Kerberos), and a shared cache (LDB database on disk)</li>
<li>Credential caching means offline logins work — a user who authenticated yesterday can log in today even if the domain controller is unreachable</li>
<li>Key config: <code class="" data-line="">sssd.conf</code> — the <code class="" data-line="">[domain]</code> section is where almost all tuning happens</li>
<li>Debugging toolkit: <code class="" data-line="">sssctl</code>, <code class="" data-line="">sss_cache</code>, <code class="" data-line="">id</code>, <code class="" data-line="">getent</code>, <code class="" data-line="">journalctl -u sssd</code></li>
<li>The most common failure modes are: SSSD not running, stale cache, misconfigured <code class="" data-line="">ldap_search_base</code>, and clock skew breaking Kerberos</li>
</ul>
<hr />
<h2 id="the-big-picture-sssd-as-the-identity-broker">The Big Picture: SSSD as the Identity Broker</h2>
<pre><code class="" data-line="">PAM (pam_sss)         NSS (sss module)
      │                      │
      └──────────┬───────────┘
                 ▼
          SSSD Responders
          ┌────────────────────────────────────┐
          │  PAM responder   NSS responder      │
          │  (auth, account, (passwd, group,    │
          │   session)        shadow lookups)   │
          └────────────┬───────────────────────┘
                       │  shared cache (LDB)
                       ▼
          SSSD Providers
          ┌────────────────────────────────────┐
          │  identity provider  auth provider   │
          │  (user/group attrs) (credentials)   │
          └────────────┬───────────────────────┘
                       │
          ┌────────────┼────────────┐
          ▼            ▼            ▼
       LDAP          Kerberos    Local files
    (AD / OpenLDAP)  (KDC / AD)
</code></pre>
<p>EP03 showed that SSSD sits between PAM and LDAP. This episode goes inside it — the architecture, the config, and how to tell exactly what it&#8217;s doing on any given login attempt.</p>
<hr />
<h2 id="why-sssd-exists">Why SSSD Exists</h2>
<p>The problem before SSSD: <code class="" data-line="">nss_ldap</code> and <code class="" data-line="">pam_ldap</code> made direct LDAP connections for every query. No caching, no connection pooling, no failover, no offline support. On a system that makes dozens of <code class="" data-line="">getpwuid()</code> calls per second (every <code class="" data-line="">ls -l</code>, every process spawn), this meant dozens of LDAP roundtrips per second hitting the domain controller.</p>
<p>SSSD solved this with a single daemon that:<br />
&#8211; Maintains a persistent connection pool to the directory<br />
&#8211; Caches identity and credential data in an LDB (LDAP-like) database on disk<br />
&#8211; Handles failover across multiple directory servers<br />
&#8211; Satisfies PAM and NSS queries from cache when the directory is unreachable</p>
<p>The credential cache is the key insight. When you authenticate successfully, SSSD stores a hash of your credentials locally. If the domain controller is unreachable on your next login — network outage, laptop offline, VPN not connected — SSSD can verify your credentials against the local cache. You log in. You never knew the DC was down.</p>
<hr />
<h2 id="sssd-architecture">SSSD Architecture</h2>
<p>SSSD is a set of cooperating processes sharing a cache:</p>
<p><strong>Monitor</strong> — the parent process. Starts and restarts all other SSSD processes. If a responder or provider crashes, the monitor restarts it.</p>
<p><strong>Responders</strong> — answer queries from PAM and NSS. Each responder handles a specific interface:<br />
&#8211; <code class="" data-line="">sssd_nss</code> — answers <code class="" data-line="">getpwnam()</code>, <code class="" data-line="">getpwuid()</code>, <code class="" data-line="">getgrnam()</code>, <code class="" data-line="">initgroups()</code> calls<br />
&#8211; <code class="" data-line="">sssd_pam</code> — handles PAM authentication, account checks, and session management<br />
&#8211; <code class="" data-line="">sssd_autofs</code>, <code class="" data-line="">sssd_ssh</code>, <code class="" data-line="">sssd_sudo</code> — optional responders for specific services</p>
<p><strong>Providers</strong> — the backend processes that talk to the actual directory:<br />
&#8211; Each domain gets its own provider process (<code class="" data-line="">sssd_be[domain_name]</code>)<br />
&#8211; The provider connects to LDAP/Kerberos/AD, fetches data, and writes it to the shared cache<br />
&#8211; If the provider crashes or loses connectivity, responders fall back to serving from cache</p>
<p><strong>Cache</strong> — LDB files in <code class="" data-line="">/var/lib/sss/db/</code>. One database per configured domain, plus a cache for negative results (lookups that returned &#8220;not found&#8221;). The cache is an LDAP-like directory stored on disk — SSSD uses the same hierarchical structure for local storage as the remote directory uses.</p>
<pre><code class="" data-line=""># See the cache files
ls -la /var/lib/sss/db/
# cache_corp.com.ldb         ← user/group data for domain corp.com
# ccache_corp.com            ← Kerberos credential cache
# timestamps_corp.com.ldb   ← when entries were last refreshed
</code></pre>
<hr />
<h2 id="sssdconf-the-config-that-matters">sssd.conf: The Config That Matters</h2>
<p><code class="" data-line="">/etc/sssd/sssd.conf</code> has a <code class="" data-line="">[sssd]</code> section (global) and one <code class="" data-line="">[domain/name]</code> section per directory. The domain section is where almost all tuning happens.</p>
<pre><code class="" data-line="">[sssd]
services = nss, pam, sudo
domains = corp.com
config_file_version = 2

[domain/corp.com]
# What type of directory this is
id_provider = ad               # or: ldap, ipa, files
auth_provider = ad             # or: ldap, krb5, none
access_provider = ad           # controls who can log in

# The AD/LDAP server (can be a list for failover)
ad_domain = corp.com
ad_server = dc01.corp.com, dc02.corp.com

# Where to look for users and groups
ldap_search_base = dc=corp,dc=com

# Cache behavior
cache_credentials = true       # enable offline login
entry_cache_timeout = 5400     # how long before re-querying (seconds)
offline_credentials_expiration = 1  # days cached credentials stay valid offline

# What uid/gid range belongs to this domain (prevents UID conflicts)
ldap_id_mapping = true         # auto-map AD SIDs to UIDs (no uidNumber needed)
# OR for classical POSIX LDAP:
# ldap_id_mapping = false      # use uidNumber/gidNumber from directory

# Restrict logins to specific AD groups
# access_provider = simple
# simple_allow_groups = linux-admins, sre-team

# Home directory and shell defaults
override_homedir = /home/%u
default_shell = /bin/bash
fallback_homedir = /home/%u

# Enumerate all users (expensive on large dirs — disable unless needed)
enumerate = false
</code></pre>
<p>The two most commonly wrong settings:</p>
<p><strong><code class="" data-line="">ldap_search_base</code></strong> — if this doesn&#8217;t include the OU where your users live, SSSD won&#8217;t find them. On AD, the default searches the entire domain, which is usually correct. On OpenLDAP, you may need <code class="" data-line="">ou=people,dc=corp,dc=com</code>.</p>
<p><strong><code class="" data-line="">ldap_id_mapping</code></strong> — on AD, users typically don&#8217;t have <code class="" data-line="">uidNumber</code> attributes. Setting <code class="" data-line="">ldap_id_mapping = true</code> tells SSSD to derive a UID from the user&#8217;s SID algorithmically. This produces consistent UIDs across machines. Setting it to <code class="" data-line="">false</code> requires actual <code class="" data-line="">uidNumber</code> attributes in the directory.</p>
<hr />
<h2 id="credential-caching-and-offline-logins">Credential Caching and Offline Logins</h2>
<p>The cache is what separates SSSD from a simple proxy. When <code class="" data-line="">cache_credentials = true</code>:</p>
<ol>
<li>On successful authentication, SSSD stores a hash of the credential in the LDB cache</li>
<li>On the next authentication attempt, SSSD first tries the domain controller</li>
<li>If the DC is unreachable, SSSD falls back to the local credential hash</li>
<li>If the hash matches, login succeeds — even with no network</li>
</ol>
<p>The credential hash is not the cleartext password — it&#8217;s a salted hash stored in <code class="" data-line="">/var/lib/sss/db/cache_corp.com.ldb</code>. The security model is the same as <code class="" data-line="">/etc/shadow</code>: someone with root access to the machine can access the hashes.</p>
<p><code class="" data-line="">offline_credentials_expiration</code> controls how long cached credentials stay valid when the DC is unreachable. <code class="" data-line="">0</code> means forever (not recommended for high-security environments). <code class="" data-line="">1</code> means one day — after 24 hours offline, even cached credentials expire and the user must authenticate online.</p>
<hr />
<h2 id="the-debugging-toolkit">The Debugging Toolkit</h2>
<pre><code class="" data-line=""># 1. Is SSSD running?
systemctl status sssd
pgrep -a sssd    # shows all SSSD processes (monitor + responders + providers)

# 2. Domain connectivity status
sssctl domain-status corp.com
# Domain: corp.com
# Active servers:
#   LDAP: dc01.corp.com
#   KDC: dc01.corp.com
# Discovered servers:
#   LDAP: dc01.corp.com, dc02.corp.com

# 3. Can SSSD find a specific user?
sssctl user-checks vamshi
# user: vamshi
# user name: vamshi@corp.com
# POSIX attributes: UID=1001, GID=1001, ...
# Authentication: success (uses actual PAM auth stack)

# 4. What does NSS see?
getent passwd vamshi          # full passwd entry
id vamshi                     # uid, gid, groups

# 5. Flush stale cache entries
sss_cache -u vamshi           # invalidate one user
sss_cache -G engineers        # invalidate one group
sss_cache -E                  # invalidate everything (nuclear option)

# 6. Live logs
journalctl -u sssd -f         # tail all SSSD logs
# Then attempt login in another terminal — watch the auth flow in real time

# 7. Increase log verbosity temporarily
sssctl config-check            # validate sssd.conf syntax
# Edit sssd.conf: add debug_level = 6 under [domain/corp.com]
systemctl restart sssd
journalctl -u sssd -f          # now shows LDAP queries, cache hits/misses
</code></pre>
<p>The single most useful command is <code class="" data-line="">sssctl user-checks &lt;username&gt;</code>. It runs the full NSS + PAM auth stack internally and prints what SSSD would do on a real login — without creating a session or touching the running system.</p>
<hr />
<h2 id="breaking-sssd-and-what-each-failure-looks-like">Breaking SSSD (and What Each Failure Looks Like)</h2>
<p><strong>SSSD not running:</strong></p>
<pre><code class="" data-line="">ssh vamshi@server
# Permission denied (publickey,gssapi-keyex,gssapi-with-mic,password)
# getent passwd vamshi → (empty)
# Fix: systemctl start sssd
</code></pre>
<p><strong>Stale cache after AD password change:</strong></p>
<pre><code class="" data-line=""># User changed password in AD but SSSD still has old credential hash
ssh vamshi@server  # password accepted (wrong!) — cache hit with old hash
# Fix: sss_cache -u vamshi, then attempt login again
</code></pre>
<p><strong>Clock skew &gt; 5 minutes (breaks Kerberos):</strong></p>
<pre><code class="" data-line="">journalctl -u sssd | grep -i &quot;clock skew\|KDC\|kinit&quot;
# sssd_be[corp.com]: Kerberos authentication failed: Clock skew too great
# Fix: systemctl restart chronyd (or ntpd), verify time sync
</code></pre>
<p><strong><code class="" data-line="">ldap_search_base</code> wrong:</strong></p>
<pre><code class="" data-line="">getent passwd vamshi  # empty, but user exists in AD
sssctl user-checks vamshi  # &quot;User not found&quot;
# Check: ldap_search_base must include the OU containing users
# Test: ldapsearch -x -H ldap://dc -b &quot;ou=engineers,dc=corp,dc=com&quot; &quot;(uid=vamshi)&quot;
</code></pre>
<hr />
<h2 id="common-misconceptions"><img src="https://s.w.org/images/core/emoji/17.0.2/72x72/26a0.png" alt="⚠" class="wp-smiley" style="height: 1em; max-height: 1em;" /> Common Misconceptions</h2>
<p><strong>&#8220;Restarting SSSD logs everyone out.&#8221;</strong> Restarting SSSD doesn&#8217;t affect existing authenticated sessions. Active shell sessions, running processes — all unaffected. Only new authentication attempts are disrupted during the restart window, which takes a few seconds.</p>
<p><strong>&#8220;sss_cache -E fixes everything.&#8221;</strong> Flushing the entire cache forces SSSD to re-fetch all entries from the domain controller on the next lookup. On a system with many users or enumeration enabled, this can cause a brief spike in LDAP traffic and slow lookups. Use targeted flushes (<code class="" data-line="">-u username</code>, <code class="" data-line="">-G group</code>) when possible.</p>
<p><strong>&#8220;debug_level should always be high.&#8221;</strong> SSSD at <code class="" data-line="">debug_level = 9</code> logs every LDAP packet. On a production system with active logins, this generates gigabytes of logs quickly. Set it temporarily for debugging, then remove it and restart.</p>
<hr />
<h2 id="framework-alignment">Framework Alignment</h2>
<table>
<thead>
<tr>
<th>Domain</th>
<th>Relevance</th>
</tr>
</thead>
<tbody>
<tr>
<td>CISSP Domain 5: Identity and Access Management</td>
<td>SSSD is the runtime implementation of enterprise identity integration on Linux — understanding its caching model, failover behavior, and credential storage is foundational to IAM operations</td>
</tr>
<tr>
<td>CISSP Domain 3: Security Architecture and Engineering</td>
<td>The credential cache design (<code class="" data-line="">/var/lib/sss/db/</code>) creates a local credential store with specific security properties — architects need to understand the offline login trade-off</td>
</tr>
<tr>
<td>CISSP Domain 7: Security Operations</td>
<td>SSSD is a critical security service — monitoring it, understanding its failure modes, and knowing how to recover it quickly are operational security skills</td>
</tr>
</tbody>
</table>
<hr />
<h2 id="key-takeaways">Key Takeaways</h2>
<ul>
<li>SSSD is a three-tier system: responders (serve PAM/NSS), providers (talk to AD/LDAP), and a shared LDB cache — each tier is independently restartable</li>
<li>Credential caching enables offline logins — the security trade-off is a local hash store in <code class="" data-line="">/var/lib/sss/db/</code></li>
<li><code class="" data-line="">sssctl user-checks</code> is the first tool to reach for when a login fails — it simulates the full auth flow and shows exactly where it breaks</li>
<li><code class="" data-line="">ldap_id_mapping = true</code> is the right choice for AD environments without POSIX attributes; <code class="" data-line="">false</code> requires actual <code class="" data-line="">uidNumber</code>/<code class="" data-line="">gidNumber</code> in the directory</li>
<li>Clock skew over 5 minutes silently breaks Kerberos authentication — time sync is a hard dependency</li>
</ul>
<hr />
<h2 id="whats-next">What&#8217;s Next</h2>
<p>EP04 showed SSSD&#8217;s role as the caching and brokering layer. What it referenced repeatedly — &#8220;Kerberos ticket&#8221;, &#8220;KDC&#8221;, &#8220;GSSAPI&#8221; — is the authentication protocol that sits underneath AD-joined Linux logins. SSSD uses Kerberos to authenticate. LDAP carries the identity data. EP05 explains how Kerberos works.</p>
<p><em>Next: <a href="/how-kerberos-works-tickets-kdc/">How Kerberos Works: Tickets, KDC, and Why Enterprises Use It With LDAP</a></em></p>
<p>Get EP05 in your inbox when it publishes → <a href="https://linuxcent.com/subscribe">linuxcent.com/subscribe</a></p>
<p><a class="a2a_button_mastodon" href="https://www.addtoany.com/add_to/mastodon?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="Mastodon" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_email" href="https://www.addtoany.com/add_to/email?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="Email" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_whatsapp" href="https://www.addtoany.com/add_to/whatsapp?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="WhatsApp" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_reddit" href="https://www.addtoany.com/add_to/reddit?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="Reddit" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_x" href="https://www.addtoany.com/add_to/x?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="X" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_linkedin" href="https://www.addtoany.com/add_to/linkedin?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="LinkedIn" rel="nofollow noopener" target="_blank"></a><a class="a2a_button_copy_link" href="https://www.addtoany.com/add_to/copy_link?linkurl=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&amp;linkname=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" title="Copy Link" rel="nofollow noopener" target="_blank"></a><a class="a2a_dd addtoany_share_save addtoany_share" href="https://www.addtoany.com/share#url=https%3A%2F%2Flinuxcent.com%2Fsssd-linux-authentication%2F&#038;title=SSSD%3A%20The%20Caching%20Daemon%20That%20Powers%20Every%20Enterprise%20Linux%20Login" data-a2a-url="https://linuxcent.com/sssd-linux-authentication/" data-a2a-title="SSSD: The Caching Daemon That Powers Every Enterprise Linux Login"></a></p><p>The post <a href="https://linuxcent.com/sssd-linux-authentication/">SSSD: The Caching Daemon That Powers Every Enterprise Linux Login</a> appeared first on <a href="https://linuxcent.com">Linuxcent</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://linuxcent.com/sssd-linux-authentication/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1781</post-id>	</item>
	</channel>
</rss>

<!--
Performance optimized by W3 Total Cache. Learn more: https://www.boldgrid.com/w3-total-cache/?utm_source=w3tc&utm_medium=footer_comment&utm_campaign=free_plugin

Page Caching using Disk: Enhanced 

Served from: linuxcent.com @ 2026-08-25 08:36:20 by W3 Total Cache
-->