How to Configure Kerberos Authentication in AD on Windows Server 2025

Kerberos is the default authentication protocol for Active Directory environments, replacing the older NTLM protocol for all domain-joined resources. Every time a domain user accesses a file share, a web application using Windows Authentication, an SQL Server instance, or any other AD-integrated service, Kerberos is working behind the scenes to prove the user’s identity without transmitting the password across the network. Windows Server 2025 fully supports Kerberos with improvements in constrained delegation auditing and resource-based constrained delegation configuration. Understanding Kerberos deeply — from SPN registration to delegation models and troubleshooting tools — allows administrators to configure services correctly, diagnose authentication failures quickly, and maintain a secure environment. This tutorial covers the complete Kerberos lifecycle in an Active Directory domain.

Prerequisites

  • A Windows Server 2025 Active Directory domain with at least one domain controller.
  • Active Directory PowerShell module (Add-WindowsFeature RSAT-AD-PowerShell).
  • Administrative credentials — Domain Admin level required for SPN management and delegation configuration.
  • The setspn.exe utility (included with Windows Server and RSAT AD DS tools).
  • Time synchronisation across all domain members. Kerberos tolerates a maximum clock skew of 5 minutes by default — excessive skew causes immediate authentication failures.

Step 1: Understand the Kerberos Authentication Flow

Kerberos in Active Directory involves three principals: the client, the Key Distribution Center (KDC) running on every domain controller, and the service the client wants to access. Authentication proceeds in two phases:

  1. Authentication Service (AS) Exchange — obtaining the TGT: The client sends an AS-REQ to the KDC, which validates the user’s credentials (using the user’s password-derived key) and returns a Ticket Granting Ticket (TGT) encrypted with the krbtgt account’s key. The client caches the TGT locally.
  2. Ticket Granting Service (TGS) Exchange — obtaining a service ticket: The client presents its TGT to the KDC’s Ticket Granting Service and requests a Service Ticket (TGS) for the target service, identified by its Service Principal Name (SPN). The KDC issues a service ticket encrypted with the service account’s key. The client presents this ticket to the service, which decrypts it and verifies the client’s identity — no password ever traverses the network.

Step 2: View Current Kerberos Tickets with klist

klist is the built-in Windows tool for inspecting cached Kerberos tickets:

# View all cached Kerberos tickets for the current user
klist

# View tickets for a specific logon session
klist sessions
klist -li 0x3e7   # 0x3e7 is the LUID for the SYSTEM logon session

# Purge all cached tickets (useful when troubleshooting stale tickets)
klist purge

# View tickets on a remote machine (requires PSRemoting)
Invoke-Command -ComputerName "WEBSERVER01" -ScriptBlock { klist }

# Check the krbtgt account details (TGT issuer)
Get-ADUser krbtgt | Select-Object Name, SamAccountName, PasswordLastSet, Enabled

Step 3: Manage Service Principal Names (SPNs)

An SPN is a unique identifier for a service instance in Active Directory. When a client wants to authenticate to a service, it looks up the SPN to identify the service account, enabling the KDC to issue an appropriate service ticket. Missing or duplicate SPNs are the most common cause of Kerberos failures in real environments.

# List all SPNs registered for a specific service account
Get-ADUser -Identity "svc-webapp" -Properties ServicePrincipalName | 
    Select-Object -ExpandProperty ServicePrincipalName

# List SPNs for a computer account
Get-ADComputer -Identity "WEBSERVER01" -Properties ServicePrincipalName |
    Select-Object -ExpandProperty ServicePrincipalName

# Register an SPN using Set-ADUser (PowerShell method)
Set-ADUser "svc-webapp" -ServicePrincipalNames @{Add="HTTP/webserver01.corp.local","HTTP/webserver01"}

# Register an SPN using setspn.exe (classic method)
setspn -A HTTP/webserver01.corp.local corpsvc-webapp
setspn -A HTTP/webserver01 corpsvc-webapp

# Verify SPN registration
setspn -L svc-webapp

# Search for duplicate SPNs across the entire forest (critical — duplicates break Kerberos)
setspn -X -F

# Find a specific SPN across the domain
setspn -Q HTTP/webserver01.corp.local

SPN format: ServiceClass/FQDN:Port. The port is omitted for standard ports (80 for HTTP, 443 for HTTPS, 1433 for SQL). For non-standard ports, include them: HTTP/webserver01.corp.local:8443.

Step 4: Configure Constrained Delegation

Kerberos delegation allows a service to act on behalf of a user when accessing a downstream resource — for example, a web application that needs to query a database using the calling user’s identity. There are three delegation models:

  • Unconstrained delegation: The service can impersonate the user to any service in the forest. Avoid in modern environments — represents significant security risk.
  • Constrained delegation (KCD): The service can impersonate the user only to explicitly listed SPNs. Configured on the service account.
  • Resource-based constrained delegation (RBCD): The target resource controls which services can delegate to it. More flexible and auditable than traditional KCD.
# --- Traditional Constrained Delegation (KCD) ---
# Allow svc-webapp to delegate to the SQL service on SQLSERVER01

# Enable constrained delegation using the "Use Kerberos only" protocol
Set-ADUser "svc-webapp" `
    -TrustedForDelegation $false `
    -Add @{"msDS-AllowedToDelegateTo" = @("MSSQLSvc/SQLSERVER01.corp.local:1433","MSSQLSvc/SQLSERVER01.corp.local")}

# Set the delegation type to Kerberos only (not protocol transition)
Set-ADAccountControl "svc-webapp" -TrustedToAuthForDelegation $false

# Verify the delegation configuration
Get-ADUser "svc-webapp" -Properties "msDS-AllowedToDelegateTo","TrustedForDelegation","TrustedToAuthForDelegation" |
    Select-Object Name, TrustedForDelegation, TrustedToAuthForDelegation, "msDS-AllowedToDelegateTo"

# --- Resource-Based Constrained Delegation (RBCD) ---
# Allow WEBSERVER01 computer account to delegate to SQLSERVER01

# Get the SID of the source computer (the one that will do the delegating)
$webServer = Get-ADComputer -Identity "WEBSERVER01"

# Configure RBCD on the target resource (SQLSERVER01)
Set-ADComputer "SQLSERVER01" `
    -PrincipalsAllowedToDelegateToAccount $webServer

# Verify the RBCD configuration on the target
Get-ADComputer "SQLSERVER01" `
    -Properties "msDS-AllowedToActOnBehalfOfOtherIdentity" |
    Select-Object Name, "msDS-AllowedToActOnBehalfOfOtherIdentity"

# Read the security descriptor to see which principals can delegate
$target = Get-ADComputer "SQLSERVER01" -Properties "msDS-AllowedToActOnBehalfOfOtherIdentity"
$sd = New-Object System.Security.AccessControl.RawSecurityDescriptor(
    $target."msDS-AllowedToActOnBehalfOfOtherIdentity", 0)
$sd.DiscretionaryAcl | ForEach-Object { 
    $sid = $_.SecurityIdentifier
    $obj = Get-ADObject -Filter { objectSid -eq $sid } -Properties Name
    Write-Host "Allowed principal: $($obj.Name) ($sid)"
}

Step 5: Troubleshoot Kerberos Failures via Event Log

Kerberos events are logged in the Security event log on domain controllers. Two critical Event IDs cover the most common failure modes:

  • Event ID 4771 — Kerberos pre-authentication failed: The user’s password was incorrect, the account is locked out, or the account does not require Kerberos pre-authentication (a security risk if disabled). Check the Failure Code field for specific sub-reasons (0x18 = wrong password, 0x12 = account disabled/expired/locked).
  • Event ID 4769 — Kerberos service ticket was requested: Generated on the DC each time a TGS is issued. Filter for failure codes to identify service ticket failures (0x1F = integrity check failed, 0x20 = ticket expired, 0x32 = KDC cannot accommodate the requested option).
# Query DC Security log for Kerberos pre-authentication failures (Event 4771)
Get-WinEvent -ComputerName "DC01" -LogName Security -MaxEvents 1000 |
    Where-Object { $_.Id -eq 4771 } |
    ForEach-Object {
        $xml = [xml]$_.ToXml()
        $data = $xml.Event.EventData.Data
        [PSCustomObject]@{
            Time          = $_.TimeCreated
            Account       = ($data | Where-Object { $_.Name -eq "TargetUserName" })."#text"
            FailureCode   = ($data | Where-Object { $_.Name -eq "Status" })."#text"
            ClientAddress = ($data | Where-Object { $_.Name -eq "IpAddress" })."#text"
        }
    } | Format-Table -AutoSize

# Query for service ticket failures (Event 4769)
Get-WinEvent -ComputerName "DC01" -LogName Security -MaxEvents 500 |
    Where-Object { $_.Id -eq 4769 -and $_.LevelDisplayName -eq "Information" } |
    Where-Object { $_.Message -match "Failure Code:s+0x" -and $_.Message -notmatch "0x0" } |
    Select-Object TimeCreated, Message | Format-List

# Enable verbose Kerberos logging on a client for deep troubleshooting
# (writes Kerberos operational events to the System event log)
Set-ItemProperty `
    -Path "HKLM:SYSTEMCurrentControlSetControlLsaKerberosParameters" `
    -Name "LogLevel" `
    -Value 1 `
    -Type DWord

# Disable verbose Kerberos logging after troubleshooting
Set-ItemProperty `
    -Path "HKLM:SYSTEMCurrentControlSetControlLsaKerberosParameters" `
    -Name "LogLevel" `
    -Value 0 `
    -Type DWord

Step 6: Resolve SPN Conflicts

Duplicate SPNs — the same SPN registered to two different accounts — cause the KDC to be unable to determine which account to encrypt the service ticket with, resulting in authentication failures. This is one of the most common Kerberos misconfigurations in enterprise environments:

# Perform a forest-wide duplicate SPN scan
setspn -X -F

# If a duplicate is found, remove the incorrect SPN registration
# Example: SPN accidentally registered on a computer account instead of service account
setspn -D HTTP/webserver01.corp.local CORPWEBSERVER01$

# Verify the correct account now has sole ownership
setspn -Q HTTP/webserver01.corp.local

# Use PowerShell to find all accounts with HTTP SPNs and audit for duplicates
Get-ADObject -Filter { ServicePrincipalName -like "HTTP/*" } `
    -Properties ServicePrincipalName, DistinguishedName |
    ForEach-Object {
        $dn = $_.DistinguishedName
        $_.ServicePrincipalName | Where-Object { $_ -like "HTTP/*" } | ForEach-Object {
            [PSCustomObject]@{ SPN = $_; Account = $dn }
        }
    } | Sort-Object SPN | Format-Table -AutoSize

Step 7: Fix Clock Skew Issues

Kerberos requires all participants — clients, servers, and domain controllers — to have clocks within 5 minutes of each other. Clock skew beyond this threshold causes immediate ticket failures with KRB_AP_ERR_SKEW errors:

# Check time synchronisation status on the local machine
w32tm /query /status

# Check time source hierarchy
w32tm /query /peers

# Force a time resynchronisation
w32tm /resync /force

# Configure a domain member to sync from DC (default for domain members)
w32tm /config /syncfromflags:domhier /update
Restart-Service w32tm

# On the PDC Emulator — configure it to sync from an external NTP source
w32tm /config `
    /manualpeerlist:"time.windows.com,0x8 pool.ntp.org,0x8" `
    /syncfromflags:manual `
    /reliable:yes `
    /update
Restart-Service w32tm
w32tm /resync /force

# Verify time on all DCs and alert if skew exceeds 2 minutes
$dcs = Get-ADDomainController -Filter *
$referenceTime = Get-Date

foreach ($dc in $dcs) {
    $remoteTime = Invoke-Command -ComputerName $dc.HostName -ScriptBlock { Get-Date }
    $skew = [Math]::Abs(($remoteTime - $referenceTime).TotalSeconds)
    $status = if ($skew -gt 120) { "WARNING" } else { "OK" }
    Write-Host "[$status] $($dc.HostName): ${skew}s skew"
}

Conclusion

Kerberos authentication is both powerful and unforgiving — when SPN registrations are correct, delegation is properly configured, and clocks are synchronised, it operates invisibly and securely. When any of these prerequisites are violated, authentication failures can cascade across multiple services in ways that are difficult to trace without knowing where to look. On Windows Server 2025, the combination of klist for ticket inspection, setspn for SPN auditing, PowerShell’s Active Directory module for delegation configuration, and Security event log analysis for failure code interpretation gives administrators a complete toolkit for both proactive configuration and reactive troubleshooting. Understanding resource-based constrained delegation in particular is increasingly important as organisations move toward microservices architectures where individual services must delegate precisely to specific backend resources without the broad authority of traditional unconstrained delegation.