I am able to succesfully kinit + klist
to verify a ticket on Mac + Windows. I even carried over my Kerberos configuration to "KerberosForWindows".
It does not seem that any browser on Windows can do Kerberos style Negotiate instead of Windows-style NTLM. Is that the case?
I've tried all of the Local Site / Intranet / Trusted Domain / browser config propagate domain for negotiation auth. I just want to confirm if Windows browsers can do GSSAPI-style Negotiate.
// Kerberos authentication middleware
async function kerberosAuth(req, res, next) {
// Check for the Authorization header and extract the token
const authHeader = req.headers['authorization'];
if (!authHeader || !authHeader.startsWith('Negotiate ')) {
res.setHeader('WWW-Authenticate', 'Negotiate');
return res.status(401).send('Kerberos authentication required');
}
const token = authHeader.slice('Negotiate '.length);
// Base64 decode the token
const decodedToken = Buffer.from(token, 'base64');
// Check if it's NTLM
if (decodedToken.toString('hex').startsWith('4e544c4d')) {
// always hit on Windows
return res.status(500).send('NTLM is not supported. Please use Kerberos authentication.');
} else {
// only ever hit on Mac/Linux
}
Kerberos over HTTP Negotiate
is also very much "Windows-style", as the HTTP mechanism was invented by Microsoft and is defined to use MS-SPNEGO (thus the name 'Negotiate') rather than raw Kerberos tokens.
All popular browsers support Kerberos via HTTP Negotiate
on Windows, but the MIT "Kerberos for Windows" package (gssapi.dll) is only supported by Mozilla/Firefox/Thunderbird, whereas IE and Chrome/Edge support Kerberos only through the Windows built-in SSPI interface (which is also the default in Firefox).
For that reason, I generally avoid installing KfW on workstations and configure Windows SSPI instead, as that gives GSSAPI support to a much longer list of software.
(If the realm is not an AD domain) Mark the realm as "MIT realm" so that Windows won't expect its KDCs to answer Netlogon pings. Skip this for Active Directory realms.
sudo ksetup /SetRealmFlags EXAMPLE.COM TcpSupported
Actual flags are optional; the presence of the realm entry in itself is what indicates the realm type being non-AD.
(If the realm is not an AD domain and someone was too lazy to add DNS SRV records) Manually define the KDC hostname.
sudo ksetup /AddKdc EXAMPLE.COM foo.example.com
Properly managed realms (both MIT and AD) should have _kerberos._udp.<domain>
SRV records pointing at the KDCs and won't need this configuration. Note that the presence of this entry marks it as a MIT/non-AD realm, as above.
(If the client uses a local account for Windows logon) Store the Kerberos credentials in the Windows Credential Manager:
cmdkey /add:*.example.com /user:foo@EXAMPLE.COM /pass
For non-domain logons, this is the closest thing SSPI has to correspond to kinit
, and it will automatically acquire a TGT using stored credentials when a program tries to obtain a service ticket.
If credentials for a TGT are not available, IE and Edge/Chrome will prompt for them using a Basic-style dialog, but Firefox doesn't support that (as that's generally not something GSSAPI applications do).
Side note: Current Firefox versions have a bug where they're unable to use Kerberos via SSPI when DoH is enabled in-browser. Make sure to disable it in Firefox settings.
Make sure your credentials are stored for the correct host name. Unlike MIT Kerberos/KfW, native Windows Kerberos doesn't perform canonicalization (neither rDNS nor CNAME), so if you visit http://foo
, that becomes the SPN HTTP/foo
, not HTTP/foo.example.com
– and therefore you need to store credentials specifically for foo
; it is not enough to store them for *.example.com
.
For the same reason, make sure the realm has the correct SPN in the first place. Use Wireshark or watch your KDC's logs to see what is being requested.
Instead of saved credentials, try to launch the browser using runas
which puts them in the "logon session" (as if it had been a domain logon):
runas /u:foo@EXAMPLE.COM /netonly cmd
Then launch Firefox or any other Kerberos-capable client from this Cmd window (I don't think this will work with Chrome/Edge though).
Run klist get <spn>
and check if a ticket for that principal shows up in the resulting list. (Make sure you're running the Windows built-in klist, not the KfW klist or Java klist!)
PS C:\> where.exe klist
C:\Program Files\Eclipse Adoptium\jre-21.0.3.9-hotspot\bin\klist.exe
C:\Windows\System32\klist.exe
PS C:\> C:\Windows\System32\klist get HTTP/foo.example.com
For Firefox, visit about:logging
and enable logging for the negotiateauth:5
module.
(Alternatively, put that in the NSPR_LOG_MODULES
environment variable, and either set NSPR_LOG_FILE
as well, or run Firefox with -console
.)
For IE and Edge/Chrome, manually add your hostnames or *.example.com
to the "Local Intranet" zone, and make sure "Automatic logon only in Intranet zone" is chosen.
Call SSPI directly, bypassing all browser-specific logic:
pip install pyspnego
or pip install sspilib
import spnego
ctx = spnego.client(hostname="foo.example.com",
service="HTTP",
protocol="negotiate")
out_token = ctx.step(None)
or
import sspilib
creds = sspilib.UserCredential(usage="initiate",
protocol="Negotiate",
protocol_list=["!ntlm"])
ctx = sspilib.ClientSecurityContext(credential=creds,
target_name="HTTP/foo.example.com")
out_token = ctx.step(none)
or
import sspi
ctx = sspi.ClientAuth("Negotiate", targetspn="HTTP/foo.example.com")
err, bufs = ctx.authorize(None)
out_token = bufs[0].Buffer
...and verify that it returns a Kerberos token in out_token
.