Hi Serge Caron,
How was the issue? It seems your previous answer has been deleted. I could not read it.
VP
This browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
In order to reduce complexity, I am using a test domain consisting of a single domain controller ON PREMISES and a single user, the domain administrator.
There is a single role installed: Remote Desktop Gateway. None of the other 5 RDS roles are installed. Direct Access and/or VPNs are not allowed in this test.
The AD domain is MyDomain.local and the internal FQDN is MyServer.MyDomain.local
There is a Let's Encrypt certificate installed on this server for MyServer.MyDomain.TLD.
Finally, there is a single port 443 forwarded to the server from the firewall.
I can RDP into this server from the Internet to MyServer.MyDomain.local via RDG MyServer.MyDomain.TLD.
However, all logons are downgraded to NTLMv2 even if I set
rdgiskdcproxy:i:1
kdcproxyname:s:MyServer.Mydomain.TLD
In this test case, I am using a non domain joined Windows 10 Pro client (even if I know it is deprecated): we need to demonstrate RDG working with BYOD, including non Windows devices.
Is there a way to do this in Windows Server 2025 ?
Hi Serge Caron,
How was the issue? It seems your previous answer has been deleted. I could not read it.
VP
Hello VPHAN,
Thank you for your answer.
In Windows Server 2025, Microsoft sets up a better configuration than what was done in previous versions. There is a detailled capture of the current setup at the bottom of this answer.
The defaults settings match your answer in Steps 1 and 2.
In step 3, "authentication level:i:2" is the standard setting meaning "If server authentication fails, show a warning, and choose to connect or refuse the connection".
This usually refers to the INTERNAL certificate of the server which is self signed. It is the default in my test configuration and I do not get this warning when connecting to MyServer.MyDomain.TLD.
I know for a fact that this test client can get Kerberos tickets from this test server because, at some point in the testing process, I did get some using the UPN.
Unfortunately, I can only guess that some detail changed in my configuration and this picture perfect setup does not work!
Regards,
The following SPNs are created when installing ONLY the RD Gateway Role in Windows Server 2025
netsh http show urlacl url="https://+:443/remoteDesktopGateway/"
netsh http show urlacl url="https://+:443/kdcproxy/"
The following registry entries are created when installing ONLY the RD Gateway Role in Windows Server 2025 and the KPSSVC service startup is set to automatic:
Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KPSSVC
DependOnService : {rpcss, http}
Description : @%systemroot%\system32\kpssvc.dll,-101
DisplayName : @%systemroot%\system32\kpssvc.dll,-100
ErrorControl : 1
FailureActions : {128, 81, 1, 0...}
FailureCommand : not used
ImagePath : C:\Windows\system32\svchost.exe -k KpsSvcGroup
ObjectName : NT AUTHORITY\NetworkService
RebootMessage : not used
Start : 2
Type : 32
Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KPSSVC\Parameters
ServiceDll : C:\Windows\system32\kpssvc.dll
ServiceDllUnloadOnStop : 1
Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings
DisallowUnprotectedPasswordAuth : 0
HttpsClientAuth : 0
HttpsUrlGroup : {+:443, , , ...}
Hello VPHAN,
The Regisgtry keys dump was to display the DEFAULT Windows Server 2025 Standard setup.
The actual LibNames is created with:
PS C:\Users\Administrator> $KpsSvcSettingsReg = "HKLM:\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings"
PS C:\Users\Administrator> New-ItemProperty -Path $KpsSvcSettingsReg -Name "LibNames" -Type MultiString -Value @("kerberos.dll") -Force
LibNames : {kerberos.dll}
PSPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings
PSParentPath : Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\KPSSVC
I have enabled Kerberos logging on the server and I can see the client contacting the proxy:
- <EventData>
<Data Name="LogonSession">MYDOMAIN\Administrator</Data>
<Data Name="ClientTime" />
<Data Name="ServerTime">20:33:17.0000 12/8/2025 Z</Data>
<Data Name="ErrorCode">0x19</Data>
<Data Name="ErrorMessage">KDC_ERR_PREAUTH_REQUIRED</Data>
<Data Name="ExtendedError" />
<Data Name="ClientRealm" />
<Data Name="ClientName" />
<Data Name="ServerRealm">MYDOMAIN</Data>
<Data Name="ServerName">krbtgt/MYDOMAIN</Data>
<Data Name="TargetName">krbtgt/MYDOMAIN@MYDOMAIN</Data>
<Data Name="ErrorText" />
<Data Name="File">onecore\ds\security\protocols\kerberos\client2\logonapi.cxx</Data>
<Data Name="Line">1135</Data>
<Binary>[ ... hex string ... ]</Binary>
</EventData>
I am told this KDC_ERR_PREAUTH_REQUIRED message is normal Kerberos behavior but I can see in the NTLM audit log that the logon was downgraded. There are no other Kerberos event logged.
Of course, no ticket was issued, even if ksetup on the client shows the realm in uppercase and the FQDN of the kdc.
I can verify that no logon occurs if I add the user to the Protected Users group.
Is there a way to understand or trace why the authentication is silently downgraded to NTLMv2 ?
Regards,
Hi Serge Caron,
The fact that you provided the registry dump allows us to pinpoint exactly why the "picture perfect" setup on Server 2025 is still failing to proxy the Kerberos tickets. You are 99% there, but the 1% you are missing is critical for the KDC Proxy service (KpsSvc) to actually function as a proxy for Kerberos traffic.
Based on your registry dump and the behavior you are describing, here is the solution.
You mentioned that the default settings match my previous answer, but looking at your registry dump for HKLM\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings, there is one specific Multi-String value missing.
Your dump shows:
DisallowUnprotectedPasswordAuth : 0
HttpsClientAuth : 0
HttpsUrlGroup : {+:443, ...}
It is missing: LibNames
Without the LibNames value, the KDC Proxy service is running and listening on port 443, but it has not loaded the Kerberos DLL to handle the actual authentication payloads. It is effectively a "hollow" service right now.
=> On your Server 2025 Domain Controller: Open regedit and go to HKLM\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings.
Create a Multi-String Value (REG_MULTI_SZ) named LibNames.
Set the value to: kerberos.dll
Restart the KDC Proxy Server service (KpsSvc).
Since your client is a non-domain joined Windows 10 Pro machine, relying solely on the .rdp file directives (kdcproxyname) can sometimes be flaky if the client OS doesn't inherently know that MYDOMAIN.LOCAL belongs to that proxy.
To "force" the issue and prove Kerberos works, you should manually map the realm on the client using ksetup. This is often required for BYOD scenarios to work reliably.
=> On the Windows 10 Client (Command Prompt as Admin): Map the Realm to the Proxy: Tells the client: "Anytime you need a ticket for MYDOMAIN.LOCAL, go to this HTTPS URL."
ksetup /addkdc MYDOMAIN.LOCAL MyServer.MyDomain.TLD
Map the Realm to the KDC (Internal mapping): this helps the client understand that the KDC logic resides at that address.
ksetup /addkdc MYDOMAIN.LOCAL MyServer.MyDomain.local
Flush and Test:
klist purge
Now try connecting via your RDP file again.
For Kerberos to function, the Service Principal Name (SPN) requested by the client must match the machine account in AD.
Internal Server Name: MyServer.MyDomain.local
External Name: MyServer.MyDomain.TLD
In your .rdp file, ensure the connection target is the Internal FQDN: full address:s:MyServer.MyDomain.local
The client authenticates to the proxy (using the TLD name), but it requests a ticket for the terminal server (the Internal name). If you put the TLD in the full address line, the client will request a ticket for TERMSRV/MyServer.MyDomain.TLD, which does not exist in your Active Directory, causing an immediate fallback to NTLM.
So,
Server: Add LibNames = kerberos.dll to the registry.
Client: Run ksetup /addkdc MYDOMAIN.LOCAL MyServer.MyDomain.TLD.
Client: Ensure RDP file connects to MyServer.MyDomain.local (Internal Name).
Once you add that LibNames key, you should see the klist on the client populate with tickets for krbtgt/MYDOMAIN.LOCAL almost immediately upon connection.
VP
Hi,
When you install only the RD Gateway role without the full RDS farm (Connection Broker, etc.), the underlying KDC Proxy Service (KpsSvc) is often not automatically configured to listen for generic Kerberos traffic, or it is configured to expect Client Certificate authentication (Smart Card) rather than Password authentication, forcing the fallback to NTLM.
To achieve Kerberos over HTTPS for a non-domain joined client on Windows Server 2025, you must manually configure the KDC Proxy registry settings and service state.
Step 1: Configure the KDC Proxy Registry (Server Side)
By default, the KDC Proxy service might be restricting authentication methods. You need to explicitly allow password-based authentication for the tunnel and define the Kerberos library: On your Server 2025 domain controller (where RDG is running), open regedit. Navigate to: HKLM\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings
(Note: If the Settings key does not exist, create it.)
Create/Modify the following DWORD value:
Name: HttpsClientAuth
Value: 0
Create/Modify the following Multi-String Value (REG_MULTI_SZ):
Name: LibNames
Value: kerberos.dll
Step 2: Ensure the KDC Proxy Service is Running: On standalone RDG installations, this service is sometimes set to Manual or not running => Open services.msc. Locate KDC Proxy Server service (KpsSvc). Set the Startup type to Automatic. Start or Restart the service. Restart the Remote Desktop Gateway service as well to ensure it picks up the hook.
Step 3: Verify the RDP File Configuration (Client Side)
Your settings look correct, but the syntax must be precise for the client to understand that MyServer.MyDomain.TLD is the authoritative KDC for the realm MYDOMAIN.LOCAL.
Open your .rdp file in Notepad and ensure these lines are present:
rdgiskdcproxy:i:1
kdcproxyname:s:MyServer.MyDomain.TLD
authentication level:i:2
Step 4: For a non-domain joined client to trigger the Kerberos process via the proxy, it must know the Realm (Domain) name to request a ticket for.
Do not use: MyDomain\Administrator
You MUST use: Administrator@MyDomain.local (UPN Format)
After applying the registry changes and restarting the services: Connect via the RDP file. Open a Command Prompt on the Windows 10 Client. Run klist. You should see a Kerberos ticket for krbtgt/MYDOMAIN.LOCAL in the list.
I hope you've found something useful here. If it helps you get more insight into the issue, it's appreciated to accept the answer then. Should you have more questions, feel free to leave a message. Have a nice day!
VP