There's a little known feature in Windows called the KDC Proxy that lets clients communicate with KDC servers over an HTTPS channel instead of TCP. This is useful for remote workloads that don't have line of sight to a Domain Controller.

The gist of this service is that it's a web listener that sits on https://+:443/kdcproxy and relays messages to a Domain Controller. Clients are enlightened to know that a proxy exists and if all else fails try and communicate over that instead. This can be used when you need clients to authenticate with a Domain Controller for things like authentication or password changes but don't have a VPN in place or don't want to expose external clients to Domain Controllers directly.

The KDC Proxy was originally built for services like RDP Gateway and DirectAccess, but these days it's looking to be more useful for general remote authentication.

Here's how you deploy it.

Server Deployment

First, you need a domain-joined server. It needs to at least have a public network interface with a domain name pointed to it. Clients will connect to it over the internet. Networking-wise you can just forward port 443 if necessary.

You will need to create a trusted certificate for the public domain name of the proxy endpoint.

Domain: corp.identityintervention.com
DC: dc01.corp.identityintervention.com
KDC Proxy: proxy.identityintervention.com

The proxy service is already present on the server, but not configured to start.

Configure Server

The service needs to be configured before it can be started.

  1. Start an elevated command prompt and configure a URL ACL for the endpoint.

    netsh http add urlacl url=https://+:443/KdcProxy user="NT authority\Network Service"
  2. Associate the certificate created previously with the endpoint (this tells HTTP.SYS to use the certificate when you connect over HTTPS). Note that for appid you should generate a random GUID here (from PowerShell try [Guid]::NewGuid()). The certhash is the thumbprint of your certificate.

    netsh http add sslcert ipport=0.0.0.0:443 certhash=$aaaabbbbccccddddeeeeffff0000111122223333 appid={aaaaaaaa-bbbb-cccc-dddd-aaaabbbbcccc}
  3. If you're not using smart card certificates (or Windows Hello) for authentication you should disable the certificate authentication requirement.

    REG ADD "HKLM\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings" /v HttpsClientAuth /t REG_DWORD /d 0x0 /f 

  4. Additionally if you're not using certificates you need to enable password authentication.

    REG ADD "HKLM\SYSTEM\CurrentControlSet\Services\KPSSVC\Settings" /v DisallowUnprotectedPasswordAuth /t REG_DWORD /d 0x0 /f 

  5. Configure the KDC Proxy Service (KPSSVC) to start automatically.

    sc config kpssvc start=auto

  6. Finally you can start the service.

    net start kpssvc

The service should start without issue.

Configure the Client

Clients need to be enlightened to use a KDC Proxy. This can be done through GPO or through modifying the registry directly. The GPO path is:

Administrative Templates\System\Kerberos\Specify KDC proxy servers for Kerberos clients

Enabling this policy requires setting Realm-to-Value mapping. The realm is your domain name (corp.identityintervention.com as I used aboved) and the value is specially crafted.

<https proxy.identityintervention.com:443:kdcproxy />

If you're using a custom path or port you can specify it explicitly above, otherwise you can leave them empty and the client will fill in the rest:

<https proxy.identityintervention.com />

With this policy deployed give the client a reboot (or wait a while -- there's some caching that goes on) and try to log in. You should find users are now authenticating over the KDC proxy endpoint.

To verify, from a logged in session launch a command prompt and try the following command:

klist get krbtgt

You should see a ticket or two show up.

klist.png

Registry Settings

On the off chance you're stuck trying to deploy these settings on a machine that can't pull down group policy updates, you can manually configure the registry keys for the client:

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos]

"KdcProxyServer_Enabled"=dword:00000001

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\KdcProxy\ProxyServers]

"corp.identityintervention.com"="<https proxy.identityintervention.com />"

 

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\Parameters]

"NoRevocationCheck"=dword:00000000

Password Change

Password changes should work through the normal CTRL+ALT+DEL "Change a password" screen.

Troubleshooting

Standard events are collected throughout the lifetime of the service and can be viewed in Event Viewer under the system log as well as the Kerberos-KdcProxy\Operational logs.

There's also a set of performance counters for the service under the KPSSVC counter group.

kpssvc-perf.png

The Kerberos.NET library relies on a collection of cryptographic primitives to implement the protocol. This post describes what those primitives are and how they're used.

Kerberos as used in the real world is an implementation of a handful of specifications. If you take a look at the deep dive document you can see the various specs that make up the functional aspects of the protocol. There are additional specs that define the cryptographic algorithms used by Kerberos:

You can follow those specs further down the rabbit hole into distinct primitives. These specs in particular define the characteristics of things like key derivation, padding, negotiation, etc.

This library implements the RFC's above and, with one exception, relies on the platform to do any real crypto operations. All of these primitives are exposed via a platform abstraction layer, the CryptoPal. This Pal exposes the dozen or so operations required by the library and in most cases just wraps .NET's APIs.

The code is structured into 3 broad areas: publicly-consumable APIs (CryptoService, KerberosCryptoTransformer, DecryptedKrbMessage, etc.), algorithm-specific implementations (Aes128Transformer, etc.), and platform-specific primitive implementations or wrappers (BCryptDiffieHellman, etc.).

There is one primitive implemented directly in the library: RC4. It's only there for backwards compatibility and should not be used.

The publicly consumable API's are the Kerberos-specific algorithms. They directly implement the above RFCs, including things like key-stretching and derivation. The logical structure looks something like this:

kerbcrypto.png

 

Kerberos.NET is built to be used across multiple platforms, however there are some caveats.

Historically people have tended to think .NET is Windows-only and therefore any library built on it is only going to run on Windows. This hasn't been the reality for a few years now and .NET will run mostly anywhere these days. Kerberos.NET will run on any supported .NET Standard 2.0 runtime, which today is Windows, Linux, and Mac OS. The recommended method is using the nuget package.

PM> Install-Package Kerberos.NET

However, there are some important caveats.

Cryptographic Support

The cryptographic primitives used by the Kerberos protocols are available in .NET across all platforms, however there are extensions to the protocol that rely on primitives that are not exposed at the framework level. There are two extensions that do not work for time being:

  1. RFC 4757: RC4-HMAC -- This would be better described as the MD4/RC4/HMACMD5 suite. It's the defacto suite introduced in Windows 2000 for compatibility reasons and has since been deprecated by the standards bodies. The MD4 hash is not exposed through .NET (for good reason) and must be P/Invoke'd or written in managed code. It's not a particularly complicated hashing algorithm, but it's also not worth taking on for the sake of cross-platform support. You're not affected in any way if you're using AES (as you should be).
  2. RFC 4556: PKINIT -- This relies on a Diffie-Hellman key agreement, which is also not exposed through .NET (also for good reason). This is somewhat more problematic because it's an important extension and should be supported everywhere. It's not a particularly complicated algorithm and in fact there's a poorly written implemention in the test harness. This needs to be better before it would be considered for library support. This might also be moot once Elliptic Curve support is added.

Adding Your own Cryptographic Implementation

If you're so inclined and need either of the above two extensions you can bring your own implementation by registering your own platform layer:

CryptoPal.RegisterPal(() => new MyCustomCryptoPalImplementation());

It needs to be called before any crypto calls occur, otherwise you're going to defaulted to the existing platform implementation.

DNS Resolution Support

Kerberos relies on DNS for resolving KDC locations by checking for SRV records of the Kerberos service at _kerberos.tcp.realm.net (or UDP). This is necessary if you don't know the location of the KDC you're needing to authenticate against. Unfortunately .NET doesn't expose a way to query SRV records, and the library relies on P/Invoke into the Windows DnsQuery_W function to do this. You can bring your own DNS query logic by overriding the QueryDomain method on the client transport classes TcpKerberosTransport, UdpKerberosTransport, and HttpsKerberosTransport.

Unit Tests

The unit tests are inherently Windows-centric and P/Invoke for a myriad of interop and compatibility reasons. This isn't a problem for developers just grabbing the nuget package, but it is a bit problematic for anyone wanting to extend the library or fix the above limitations on non-Windows platforms. Eventually the tests may be reorganized and tagged as platform-specific, but that's not a high priority at the moment.