Skip to main content
Systems Engineering

Fixing Double-Hop Kerberos With Constrained Delegation

Resource-based constrained delegation replaces SPN-bound trust chains, fixing Kerberos double-hop failures without domain-wide delegation risk.

Fixing Double-Hop Kerberos With Constrained Delegation

In this guide

Share

A front-end IIS server authenticates a user successfully via Kerberos

, then makes a call to a back-end SQL Server or SharePoint API on that user’s behalf, and the request silently falls back to anonymous or the application pool identity. No error, no audit trail explaining why. This is the Kerberos double-hop problem, and in any enterprise running multi-tier services, it eventually forces a choice between weakening the trust boundary with unconstrained delegation or correctly implementing constrained delegation at the resource level. Most teams pick the former because it is faster to configure and just as fast to get flagged in a penetration test. This article covers the architecture and implementation of resource-based constrained delegation (RBCD), the mechanism that should have replaced classic delegation models years ago in any domain that still cares about lateral movement risk.

#The Problem: Why Kerberos Breaks on the Second Hop

Kerberos tickets are scoped to a single service principal. When a client authenticates to Service A, the ticket-granting service (TGS) issues a service ticket valid only for A’s SPN. If A then needs to call Service B while impersonating the original user, it has no ticket to present, because the user’s ticket was never issued for B. NTLM avoids this by design (it doesn’t chain trust the same way), which is precisely why so many broken multi-tier deployments end up quietly downgrading to NTLM and losing mutual authentication in the process.

Microsoft’s answer, long before RBCD existed, was classic constrained delegation (KCD), configured via the msDS-AllowedToDelegateTo attribute on the front-end service account. This works, but it has two structural weaknesses: it requires a domain administrator (or an account with SeEnableDelegationPrivilege) to modify the front-end account, and it does not function across domain or forest boundaries without additional trust configuration. In large enterprises running federated AD forests or per-business-unit domains, that second limitation is the one that actually breaks production.

#Architectural Breakdown: S4U2Self and S4U2Proxy

Constrained delegation is built on two Kerberos extensions defined in MS-SFU: Service-for-User-to-Self (S4U2Self) and Service-for-User-to-Proxy (S4U2Proxy).

  • S4U2Self allows a service to request a Kerberos ticket to itself on behalf of a user, without that user presenting credentials directly. This is how the front-end obtains a usable ticket for the user’s identity even when the initial connection came in over NTLM or a non-Kerberos channel.
  • S4U2Proxy takes that ticket and exchanges it, via the KDC, for a service ticket to the back-end resource, provided the delegation relationship is authorised.

The critical architectural shift with resource-based constrained delegation is where that authorisation lives. Classic KCD stores the allow-list on the front-end account (msDS-AllowedToDelegateTo) — the delegating party controls who it can impersonate to. RBCD stores it on the back-end resource account, via msDS-AllowedToActOnBehalfOfOtherIdentity. The resource owner decides who is allowed to delegate to it. This inversion is what removes the cross-domain and privileged-write dependency: the back-end owner (who may sit in a different domain) can grant the relationship without needing rights over the front-end account at all.

constrained delegation

Rendering diagram...

#
Constrained Delegation vs Unconstrained and Classic KCD

ModelTrust ScopeCross-Domain SupportPrivilege Required to ConfigurePrimary Risk
Unconstrained delegationAny service, any resourceYesDomain AdminFull TGT theft on front-end compromise; effectively domain-wide impersonation
Classic constrained delegation (KCD)Explicit SPN list on front-endNo (same domain only, pre-2012 functional level constraints)SeEnableDelegationPrivilege on front-end objectFront-end account misconfiguration grants broad SPN access
Resource-based constrained delegation (RBCD)Explicit SID list on back-endYes, including cross-forest with claims transformationWrite access on back-end object onlyMachine account takeover if RBCD combined with weak object ACLs

#Implementation Logic

Deploying resource-based constrained delegation correctly follows a fixed sequence. Skipping the verification step is the single most common cause of “it works from my laptop but not from the load-balanced pool.”

  1. Identify the exact service account run as by the front-end (application pool identity, gMSA, or computer account) and its SID.
  2. Identify the back-end resource’s computer or service account object.
  3. Write the front-end SID into the back-end object’s msDS-AllowedToActOnBehalfOfOtherIdentity attribute as a security descriptor.
  4. Confirm the SPN registered against the back-end service matches exactly what the client requests (case and host-name form matter).
  5. Validate with a live ticket trace, not just a successful HTTP response — a silent NTLM fallback can mask a delegation failure entirely.
1# Grant the front-end web server's computer account
2# rights to act on behalf of users when calling SQLSVC
3$frontEnd = Get-ADComputer -Identity "WEB01"
4Set-ADComputer -Identity "SQLSVC" -PrincipalsAllowedToDelegateToAccount $frontEnd
5
6# Verify the attribute was written correctly
7Get-ADComputer -Identity "SQLSVC" -Properties PrincipalsAllowedToDelegateToAccount |
8    Select-Object -ExpandProperty PrincipalsAllowedToDelegateToAccount

For gMSA-backed application pools, the front-end principal is the managed service account object rather than the computer account, and it must already be in PrincipalsAllowedToRetrieveManagedPassword on its own gMSA before any of this matters — a separate but frequently confused chain.

1# Validate on the front-end host that a forwardable ticket
2# for the back-end SPN is actually being cached
3klist tickets | findstr /i "MSSQLSvc"
4
5# Confirm the KDC is issuing service tickets, not NTLM fallback,
6# by checking Security event 4769 (Kerberos Service Ticket Requested)
7Get-WinEvent -LogName Security -FilterXPath "*[System[EventID=4769]]" -MaxEvents 20

When troubleshooting event logs across domain boundaries, filter specifically for the failure code embedded in event 4769 — 0x20 indicates the ticket expired mid-chain, and 0x2D indicates the delegation is not authorised for the requested SPN, which almost always traces back to a stale or missing SID in the back-end’s delegation attribute.

#
Structuring the Trust Grant as Code

For teams managing this through infrastructure-as-code rather than ad hoc PowerShell, the delegation relationship should be declared alongside the resource definition, not the front-end. This mirrors the resource-owns-the-trust model of the underlying protocol and keeps the grant auditable in version control.

1{
2  "resourceAccount": "SQLSVC",
3  "allowedDelegators": [
4    {
5      "principal": "WEB01$",
6      "sid": "S-1-5-21-...-1104",
7      "justification": "Tier-2 API fan-out, ticket CHG0041233"
8    }
9  ],
10  "reviewCycleDays": 90
11}

This structure feeds directly into a periodic access-review job — enumerate every account with a non-empty msDS-AllowedToActOnBehalfOfOtherIdentity and reconcile it against the declared manifest. Drift here is exactly the kind of silent privilege accumulation that architectural patterns for zero-trust identity are supposed to eliminate, and constrained delegation grants are one of the most under-audited object attributes in most AD environments precisely because they are invisible in the standard GUI tooling unless you know to look for them.

Fixing Double-Hop Kerberos With Constrained Delegation architecture diagram 2

#Failure Modes and Edge Cases

Resource-based constrained delegation removes the cross-domain write dependency but introduces its own class of failure.

  • SID history and account renames. If the front-end service account is renamed or migrated between domains, the SID stored in the back-end’s security descriptor becomes stale. The delegation silently fails with 0x2D rather than throwing a rename-related error, because the KDC is doing a raw SID comparison, not a name lookup.
  • Claims stripping across forest trust. When RBCD spans a forest trust, compound authentication and claims can be stripped depending on the trust’s msDS-SupportedEncryptionTypes and whether selective authentication is enabled. Back-end resources that rely on claims-based authorisation rather than group membership will fail authorisation checks even though the Kerberos exchange itself succeeds.
  • PAC validation overhead at scale. Every S4U2Proxy exchange triggers a Privilege Attribute Certificate signature check against a domain controller. Under high fan-out (a front-end calling dozens of back-end microservices per request), this adds measurable latency per hop unless PAC caching is tuned appropriately on the DCs handling the load.
  • Protocol transition abuse. S4U2Self does not require the user to prove possession of credentials to the requesting service — this is by design, but it means any account granted delegation rights that is also compromised can impersonate any user to the resources it’s authorised against, without that user ever authenticating. This is why the back-end SID list must be reviewed with the same rigor as a privileged group membership, not treated as a low-risk plumbing setting.

#Scaling and Security Trade-offs

Rolling constrained delegation out fleet-wide surfaces trade-offs that don’t appear in a single-server proof of concept.

  • Blast radius vs operational overhead: RBCD’s per-resource SID list is more granular than KCD’s per-front-end SPN list, but at scale (hundreds of back-end services) it means hundreds of individually-managed security descriptors rather than a centralised delegation table — automation is not optional past a handful of services.
  • Cross-forest latency: Selective authentication combined with RBCD across a forest trust adds an additional referral ticket round-trip per hop; for latency-sensitive synchronous call chains, this can push service-to-service response times up by tens of milliseconds compared to same-domain delegation.
  • Auditability: Unlike group-based access, delegation grants don’t appear in standard AD group membership reports. Security teams need a dedicated LDAP query or SIEM rule against msDS-AllowedToActOnBehalfOfOtherIdentity to maintain visibility, or the grants become effectively invisible shadow trust relationships.
  • Migration risk: Moving from unconstrained to resource-based constrained delegation is not atomic — during the transition window, some front-ends may hold both an unconstrained flag and a resource-scoped grant, and removing the unconstrained flag prematurely breaks any legacy integration that was silently depending on it.

None of this makes RBCD optional in a modern AD estate that still runs multi-tier Windows services. Unconstrained delegation on a front-end account is functionally equivalent to storing a domain-wide impersonation key on whichever server happens to be compromised first, and every red-team engagement treats it as exactly that. Getting constrained delegation right — resource-owned, SID-audited, and reviewed on the same cadence as privileged group membership — is one of the few AD hardening tasks that directly closes a well-understood, frequently exploited lateral-movement path without requiring a platform migration or a new security product to operate it.

Evidence trail

Sources and verification

Primary documentation and external technical references used in this article.

  1. 01MS-SFUlearn.microsoft.com
Marcus Thorne

Marcus Thorne

Systems Engineering Editor

Marcus Thorne is a pragmatic software architect focused on highly concurrent, distributed transactional systems.

Published Last changed
View Profile
Reader Interaction

Comments

Add a thoughtful note on Fixing Double-Hop Kerberos With Constrained Delegation. Comments are checked for spam and held for moderation before appearing.

Loading comments...
Comment submission is disabled until Cloudflare Turnstile keys are configured.

Discover more

Lexicon Definitions

Learn More About KBY

Was this useful?

Engineering insights, direct to you.

Receive the latest Systems Engineering tutorials, production guides, Engineering Labs and operational best practices.