2

I can successfully run kinit to get following klist output:

Ticket cache: FILE:/tmp/krb5cc_1001

Default principal: ramus@KERIM.IO

Valid starting Expires Service principal

01.03.2022 17:24:01 02.03.2022 17:23:58 krbtgt/KERIM.IO@KERIM.IO

and the following cURL command also works:

curl --negotiate -u: http://test.kerim.io:8081/skin?test=foo

with the following output:

* Connected to test.kerim.io (192.168.1.100) port 8081 (#0)
* Server auth using Negotiate with user '';
> GET /skin?test=foo HTTP/1.1
> Host: test.kerim.io:8081
> Authorization: Negotiate YIICWQYGKwYBBQUCoIIC[redacted]
> User-Agent: curl/7.74.0
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Date: Tue, 01 Mar 2022 16:29:15 GMT
< Server: Apache
< WWW-Authenticate: Negotiate oYG3MIG0oAMKAQChCwYJKoZIgvcSA[redacted]
< Cache-Control: no-cache, no-store
< Access-Control-Allow-Origin: localhost
< Access-Control-Allow-Methods: GET, POST, OPTIONS
< Access-Control-Max-Age: 1000
< Access-Control-Allow-Headers: X-Requested-With, Content-Type, Origin, Authorization, Accept
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8

I tried setting all sorts of browser settings in firefox (via about:config, following this guide) and chrome's command line arguments (--auth-server-whitelist="*.kerim.io"), but it seems as though the browsers just refuse to negotiate for the above mentioned URL.

The client OS is Ubuntu. The webserver does respond with WWW-Authenticate: Negotiate.

Calling Firefox with the following env variables:

export NSPR_LOG_MODULES="negotiateauth:5,NTLM:5" KRB5_TRACE="/dev/stderr"

shows this error:

[Parent 21580: Main Thread]: D/negotiateauth   service = test.kerim.io

[Parent 21580: Main Thread]: D/negotiateauth using negotiate-gss

[Parent 21580: Main Thread]: D/negotiateauth entering nsAuthGSSAPI::nsAuthGSSAPI()

[Parent 21580: Main Thread]: D/negotiateauth Attempting to load gss functions

[Parent 21580: Main Thread]: D/negotiateauth entering nsAuthGSSAPI::Init()

[Parent 21580: BgIOThreadPool #1]: D/negotiateauth nsHttpNegotiateAuth::GenerateCredentials() [challenge=Negotiate]

[Parent 21580: BgIOThreadPool #1]: D/negotiateauth entering nsAuthGSSAPI::GetNextToken()

[Parent 21580: BgIOThreadPool #1]: D/negotiateauth gss_init_sec_context() failed: Unspecified GSS failure. Minor code may provide more information

SPNEGO cannot find mechanisms to negotiate

[Parent 21580: BgIOThreadPool #1]: D/negotiateauth leaving nsAuthGSSAPI::GetNextToken [rv=80004005]

Could this be a problem with the name resolution of the KDC? I currently have the hostname only in my /etc/hosts and not registered with the DNS.

Kerim Güney
  • 148
  • 1
  • 7

2 Answers2

4

You're using Ubuntu, which provides Firefox through its Snap sandboxing system. It's a known problem that Kerberos currently doesn't work in the Snap package of Firefox, and the easiest workaround is to replace it with the apt package which is still available.

snap remove firefox
apt install firefox

But more specifically, the GSSAPI error message "SPNEGO cannot find mechanisms to negotiate" will show up when the program thinks you don't have any Kerberos credentials (and without Kerberos, is left with an empty list of GSSAPI mechanisms).

This happens because your default Kerberos ticket cache location is in /tmp, and snapd gives each app an isolated instance of /tmp, preventing it from seeing the same files. You can verify this by visiting file:///tmp in Firefox, or by poking around in snap run --shell firefox.

(I have not investigated Chromium, but I expect it's going to be pretty much the same problem.)

Snapd does however allow access to /home; at least the Firefox snap in particular has the correct entitlements ("plugs") connected to automatically have home directory access. So you can work around this problem by moving the ticket cache to your home directory – either through environment:

export KRB5CCNAME="FILE:$HOME/krb5cc"

or system-wide, by editing /etc/krb5.conf to say:

[libdefaults]
    default_ccache_name = FILE:/home/%{username}/krb5cc

Note that Snapd uses AppArmor to precisely limit what paths the app may access, and its generated AppArmor profile explicitly excludes "toplevel hidden directories" when granting home access, so ~/krb5cc would be allowed but ~/.cache/krb5cc would not be.

(You might dislike that the particular "/home/USER" layout will be hardcoded in your krb5.conf, as it cannot reference $HOME nor look up something like %{home}/krb5cc... but that doesn't matter because AppArmor already has the same problem anyway; its profiles also hardcode /home.)

Another alternative would be to use the "runtime directory" FILE:/run/user/%{uid}/krb5cc, but unfortunately this is not permitted by AppArmor either.

(Also, running dmesg actually reveals some AppArmor denials related to /etc/gss/mech.d, but this is not really a problem for Kerberos; that path would only be used by gss-ntlmssp.)


After fixing this problem, you may run into another: the Firefox snap bundles its own Kerberos libraries rather than using the system ones (much like with Docker, this is considered to a feature, allowing snaps to potentially provide newer libraries than the system has), but does not include the k5tls.so plugin which is required for krb5 to access KDCs via HTTPS (i.e. using the MS-KKDCP protocol).

So if your realm has DNS URI records indicating that KKDCP is the only option (rather than cleartext TCP or UDP), then krb5 will not be able to talk to the KDC without the k5tls plugin. (When URI records are found, it will also not fall back to using SRV records for cleartext TCP/UDP even if those do exist.)

If this becomes an issue, URI record lookup could be disabled via /etc/krb5.conf forcing fallback to SRV (or a static KDC hostname could be set), but this only helps if the realm's KDCs are indeed accessible via cleartext. There seems to be no good way to actually include additional files inside a Snap container (not to mention the possible version mismatches this may cause), so if the realm uses TLS/KKDCP exclusively (like Azure AD Kerberos does), then it'll be easier to just apt install firefox krb5-k5tls rather than wrestle with Snap.

grawity
  • 501,077
1

It's possible to make this work in all browsers, no matter where they are installed from (Snap, Flatpak, DEB, and so on).

This is the plan:

  1. Move the credential cache inside $HOME and hide it from users, so they don't delete it inadvertently;
  2. Create policies for browsers to trust our company domains;
  3. ...
  4. Profit!

Let's also keep this manageable in an enterprise environment while we're at it – things should just work with no user action.

Move the credential cache

Change the path where each user's credential cache is stored. This assumes that your users' homes follow the /home/<name> pattern:

sudo sed -i '/\[libdefaults\]/a default_ccache_name = FILE:/home/%{username}/krb5cc' /etc/krb5.conf

Make sure the file is hidden for any user that logs in for the first time:

echo krb5cc | sudo tee -a /etc/skel/.hidden

Also hide it in your current user account:

echo krb5cc | tee -a ${HOME}/.hidden

You'll need to repeat this for any users that have already logged in to the machine, since files from /etc/skel are only read on the first authentication.

Create browser policies

Firefox

Create the directory for system-wide configuration:

sudo mkdir -p /etc/firefox/policies

Drop a JSON policy document at that path:

echo '{"policies": {"Authentication": {"SPNEGO": ["internal.example.com"], "AllowProxies": {"SPNEGO": true}, "Locked": true, "PrivateBrowsing": true}}}' \
  | sudo tee /etc/firefox/policies/policies.json

Replace internal.example.com with your domain name. Do not use wildcards, all subdomains are implicitly included. If you don't want to whitelist the entire domain, add each subdomain to the list instead: ["hr.internal.example.com", "intranet.internal.example.com", ...]

If you already deploy policies.json in your domain, then incorporate the changes above to your existing file, don't overwrite it.

Restart Firefox and check the active policies at about:policies.

Log out or reboot the computer and log in again. Single Sign-On should work in Firefox now.

Chromium and Google Chrome

Create the directories for managed policies:

sudo mkdir -p /etc/chromium-browser/policies/{managed,recommended}
sudo mkdir -p /etc/opt/chrome; sudo ln -s /etc/chromium-browser/policies /etc/opt/chrome/

Create the policy document:

echo '{ "AuthServerAllowlist": "*.internal.example.com" }' \
  | sudo tee /etc/chromium-browser/policies/managed/auth_server_whitelist.json

Replace internal.example.com with your domain name. Asterisk is supported here, so subdomains are automatically trusted. If you want to explicitly name every subdomain, replace it with a list – just like you did for Firefox.

Restart the browser and check the active policy at chrome://policy.

Log out of your desktop session and log in again. SSO should be working now in all Chromium-based browsers.


I have tested this on Kubuntu 22.04 with browsers installed from the Snap store and from DEB packages.

Sergiu
  • 9