harbar.net component based software & platform hygiene

Configuring Kerberos Constrained Delegation with Protocol Transition and the Claims to Windows Token Service using Windows PowerShell

Print | posted on Tuesday, June 02, 2015 9:05 PM

Recently I’ve done a few pieces of work with SharePoint 2013 Business Intelligence and I have also delivered the “legendary”* Kerberos and Claims to Windows Service talk a few times this year. This reminded me to post my Windows PowerShell snippets for the required Active Directory configuration.

This topic area is perhaps one of the most misunderstood areas of SharePoint Server, and there is an utterly staggering amount of misinformation, out of date information, single server documentation and good old fashioned 100% bullshit out there. That’s a surprise with SharePoint stuff, huh?

Every guide or document out there that I could find talks to configuring Delegation using Active Directory Users and Computers (ADUC). They all also reference configuring Local Security Policy manually, or via Group Policy (without providing the details).

Of course there’s nothing wrong with doing it that way, and it sure makes for a better explanation of the concepts. However back in 2009 when we were working on pre-release materials I put together some Windows PowerShell to achieve the same configuration. So here they are in all their very simple glory.

* “Legendary” – I don’t know about that so much, but the Kerberos talks and in particular the AuthN+Z module of the SharePoint 2007, 2010 and 2013 MCM programs were recently described to me as such by five different SharePoint luminaries with rock solid credibility. Those people know who they are.

Every time I give this talk I get hassled for the “magic scripts”. They aren’t magic, but they always seem to surprise people as there is a misconception that delegation settings cannot be set using Windows PowerShell!

As you should be aware, in order to configure identity delegation for a Web Application in Claims mode within SharePoint Server 2010 or 2013 we must configure Kerberos Constrained Delegation with Protocol Transition. No ifs, no buts. It’s the only way it can work because in Claims mode there is no identity with which to perform either impersonation, basic delegation or true Constrained Delegation using Kerberos.

Thus, we make use of a component of the Windows Identity Framework, the Claims to Windows Token Service (C2WTS) to mock real delegation using a Windows Logon Token. C2WTS itself makes use of Service For User (S4U). S4U does NOT perform real delegation, it cannot because there are no user credentials to delegate. It instead grabs a bunch of SIDs for the user (in this case a service identity). What all this means is that there is a hard requirement to use Protocol Transition. Protocol Transition is named in the UI of ADUC as “Use any authentication protocol”.

Thus, in order to set things up, our settings in Active Directory for the C2WTS service identity and the application pool identity of the service application endpoint must be configured to perform Kerberos Constrained Delegation using Protocol Transition to the back end services.

In the example below I am allowing the C2WTS account to delegate to SQL Server Database Services and SQL Server Analysis Services using the SPNs which already exist on their service accounts. I of course repeat the exact same configuration on the application pool identity of the service application endpoint.

image

In order to complete this configuration using ADUC we are told we must create a “mock” or “fake” Service Principal Name (SPN) on the accounts first. Otherwise the Delegation tab in the account properties does not show up.

The reality is we can easily configure the attributes we are interested in using ADUC in Advanced Features mode, or ADSIEdit. However, there must be an SPN for the delegation to succeed. So it’s not a “mock” SPN at all. It’s not just about exposing the delegation tab. We must have a SPN!

It’s a complete breeze to configure the same settings using the Active Directory module for Windows PowerShell.

  • The services to delegate to are exposed by the AD schema extended attribute msDS-AllowedToDelegateTo. This can be manipulated using the standard Set-ADUser –Add pattern. As can the SPN itself. 
     
  • The setting for Protocol Transition is actually a UserAccountControl attribute.  It’s enumeration is ADS_UF_TRUSTED_FOR_DELEGATION or 524288. Remember this attribute is a cumulative bitmask. But the thing is we DON’T need to care! We don’t need some stinky “library” or utility function to manage the bitmask stuff or any of that noise. It can all be handled with the Set-ADAccountControl cmdlet with the –TrustedToAuthForDelegation parameter.
     
  • Note TrustedToAuthForDelegation == Protocol Transition, –TrustedForDelegation == Kerberos Only

And that’s it. Two cmdlets basically. A complete snap. Now as always, there’s some slinging needed to do this neatly for real requirements and perform end to end configuration. Here’s the Windows PowerShell script I use for basic setups:

<#
    Configures accounts in Active Directory to support identity delegation
    spence@harbar.net
    February 16th 2009

    1. Configures SPNs for SQL DB and SQL AS
       - does not check for duplicates
    2. Configures SPNs for SharePoint service identities
       (C2WTS and Service App Endpoint Identity)
    3. Configures Kerberos Constrained Delegation with 
       Protocol Transition to SPNs in #2
    
#>
Import-Module ActiveDirectory

## VARS
$sqlDBaccount = "sqldb"
$sqlASaccount = "sqlas"
$c2wtsAccount = "c2wts"
$servicesAccount = "sppservices"

$c2wtsSpn = "SP/c2wts"
$servicesSpn = "SP/Services"
$sqlDbSpns = @("MSSQLSvc/fabsql1.fabrikam.com:1433", "MSSQLSvc/fabsql1:1433")
$sqlAsSpns = @("MSOLAPSvc.3/fabsql1.fabrikam.com", "MSOLAPSvc.3/fabsql1")
$delegateToSpns = $sqlDbSpns + $sqlAsSpns
## END VARS

$delegationProperty = "msDS-AllowedToDelegateTo"

Write-Host "Configuring SPNs for SQL Server Services..."
$account = Get-ADUser $sqlDBaccount
$sqlDbSpns | %  {Set-AdUser -Identity $account -ServicePrincipalNames @{Add=$_}}
$account = Get-ADUser $sqlASaccount
$sqlAsSpns | %  {Set-AdUser -Identity $account -ServicePrincipalNames @{Add=$_}}

function ConfigKCDwPT($account, $spn) {
    $account = Get-ADUser $account
    $account | Set-ADUser -ServicePrincipalNames @{Add=$spn}
    $account  | Set-ADObject -add @{$delegationProperty=$delegateToSpns}
    Set-ADAccountControl $account -TrustedToAuthForDelegation $true
}

Write-Host "Configuring KCDwPT for C2WTS and Services Account..."
ConfigKCDwPT $c2wtsAccount $c2wtsSpn
ConfigKCDwPT $servicesAccount $servicesSpn

Write-Host "KCDwPT configuration complete!"

OK, so that’s the AD account configuration settings all taken care of. What about the C2WTS itself?

If we run C2WTS as it’s default identity, LocalSystem, we don’t need to do anything. But that’s a really stupid configuration. Why? Because in a real farm you have more than one machine running C2WTS. That means multiple points of configuration (on each computer object in AD). In addition any mistakes you make during configuration (say you fat finger the SPN) require a machine restart for corrections to take effect.  Thus there is a compromise between manageability, configuration approach and security.

The reality is that the security element of the compromise is completely null and void from a technical or information security perspective. The old arguments about TCB are now completely out-dated, and besides were invented by people who didn’t know information security and were designed for single server solutions! However, if you are unlucky enough to work with those customers with out-dated security policies it remains part of the compromise on those grounds alone.

Everyone else with any sense will change the identity to a named service account. If we do this, we also have to grant additional User Rights Assignments to the account in order for it to be able to call S4U. These are Act as part of the Operating System and Impersonate a Client after Authentication. The account must also be a member of the Local Administrators group on each server it runs. All of this can be done via Computer Management and Local Security Policy, or properly via Group Policy.

However it’s also a complete snap to configure this stuff using Windows PowerShell, making use of an old school utility NTRights from the Windows Server Resource Kit or the Carbon library. Here’s the script:

<#
    Configures C2WTS service identity with appropriate user rights
    spence@harbar.net
    February 16th 2009

    1. Configures Local Admins memebership
    2. Configures User Rights Assignments using NTRights 
       (update with path to WSRK)
    3. Configures User Rights Assignments using Carbon 
       (http://sourceforge.net/projects/morgansource/files/
       Third-Party-Sources/Carbon-1.6.0.zip/download)
    
#>
asnp Microsoft.SharePoint.PowerShell

## VARS
$user = "fabrikam\c2wts"
$CarbonDllPath = "C:\Tools\Carbon-1.6.0\Carbon\bin\Carbon.dll"
## END VARS


# adds user to local admins group
NET LOCALGROUP Administrators $user /ADD

# sets up the neccessary local user rights assignments using NTRights
C:\Tools\rk\NTRights.exe +r SeImpersonatePrivilege -u $user
C:\Tools\rk\NTRights.exe +r SeTcbPrivilege -u $user

# sets up the neccessary local user rights assignments
[Reflection.Assembly]::LoadFile($CarbonDllPath)
[Carbon.Lsa]::GrantPrivileges($user, "SeImpersonatePrivilege")
[Carbon.Lsa]::GrantPrivileges($user, "SeTcbPrivilege")

Note we do NOT have to set the c2wts account to Logon as a Service, as this User Rights Assignment is granted when we change the service identity within SharePoint…..

On a related note, I’ve also been asked for my snippets for managing the C2WTS process identity. TechNet has incorrect scripts for this work, which will only ever work on a single server farm (ooops!). Here’s how to change it properly, and also how to reset it back to LocalSystem (properly!).

<#
    Configures C2WTS service identity 
    spence@harbar.net
    February 16th 2009

    1. Sets dependency
    2. Sets desired process identity
#>
asnp Microsoft.SharePoint.PowerShell

## VARS
$accountName = "FABRIKAM\c2wts"
$serviceInstanceType = "Claims to Windows Token Service"
## END VARS

sc.exe config c2wts depend=CryptSvc

# configure to use a managed account
# Should use farm, otherwise in multi server farm you have an array of objects!
$farmServices = Get-SPFarm
$c2wts = $farmServices.Services | Where {$_.TypeName -eq $serviceInstanceType}
$managedAccount = Get-SPManagedAccount $accountName
$c2wts.ProcessIdentity.CurrentIdentityType = "SpecificUser";
$c2wts.ProcessIdentity.ManagedAccount = $managedAccount
$c2wts.ProcessIdentity.Update();
$c2wts.ProcessIdentity.Deploy();
$c2wts.ProcessIdentity 

# reset to local system
# Should use farm, otherwise in multi server farm you have an array of objects!
$farmServices = Get-SPFarm
$c2wts = $farmServices.Services | Where {$_.TypeName -eq $serviceInstanceType}
$c2wts.ProcessIdentity.CurrentIdentityType=0; #LocalSystem
$c2wts.ProcessIdentity.Update();
$c2wts.ProcessIdentity.Deploy();
$c2wts.ProcessIdentity 

Note I use this script to also configure the missing dependency on the Windows Service itself. We can of course start the C2WTS easily as well:

# start c2wts on server(s)
$servers = @("FABSP1", "FABSP2")
foreach ($server in $servers)
{
    Get-SPServiceInstance -Server $server | Where {$_.TypeName -eq $serviceInstanceType} | Start-SPServiceInstance
}

Nice and easy. No pointy clickity click click or “Working on it…” needed. The entire end to end configuration in Windows PowerShell takes less than 90 seconds.

 

 

s.