1

I can successfully run kinit to get following klist output:

Ticket cache: FILE:/tmp/krb5cc_1001

Default principal: [email protected]


Valid starting       Expires              Service principal

01.03.2022 17:24:01  02.03.2022 17:23:58  krbtgt/[email protected]

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
  • 138
  • 7
  • 1
    What client OS are you using, *which* browser settings have you changed in Firefox's about:config, and do Chromium and Firefox have the same $KRB5CCNAME in their environments as your terminal does, and does the server send `WWW-Authenticate: Negotiate` in response to unauthenticated requests? – u1686_grawity Mar 01 '22 at 15:35
  • @user1686 the client OS is Ubuntu. The Firefox config in question was `network.negotiate-auth.trusted-uris` ( I basically followed [this](https://docs.fedoraproject.org/en-US/Fedora/16/html/Security_Guide/sect-Security_Guide-Single_Sign_on_SSO-Configuring_Firefox_to_use_Kerberos_for_SSO.html)). I am not sure what you mean by $KRB5CCNAME environment variable. I don't think I had to set any environment variables for cURL to work. And yes, the server does respond with `WWW-Authenticate`. – Kerim Güney Mar 01 '22 at 22:49
  • 1
    @KerimGüney You need to edit your question to add these details; comments are casual and disposable. Adding details to your question assures more people will see those details and _might_ be able to help. – Giacomo1968 Mar 01 '22 at 22:56
  • 1
    @Giacomo1968 You are right. I added the additional information to the question itself. Thanks – Kerim Güney Mar 01 '22 at 23:10
  • 1
    Have you set `network.negotiate-auth.trusted-uris` in Firefox like you've done in Chrome? Can you run Firefox from a terminal after doing `export NSPR_LOG_MODULES="negotiateauth:5,NTLM:5" KRB5_TRACE="/dev/stderr"` (and after closing all runnng instances) and then report what's being logged when you try to visit a protected page? – u1686_grawity Mar 02 '22 at 05:31
  • @user1686 Yes, that's intentional. I was actually trying to redact the real domain name and replace it with a personal one in the outputs above. How did you find out? Did you decode the ticket? Would you please remove the real domain name from your comment? I'd appreciate it haha – Kerim Güney Mar 02 '22 at 07:53
  • 1
    Kerberos tickets contain the service principal name in clear text (it's only Base64-encoded in HTTP headers). The server even relies on it, to find the matching keytab entry. Requests to the KDC (to issue a ticket) also carry clear text names, unless FAST or KKDCP are used. – u1686_grawity Mar 02 '22 at 08:00
  • @user1686 makes sense. Thank you for the information. Let me run firefox with the env parameters, you sugested. – Kerim Güney Mar 02 '22 at 08:03
  • @user1686 I updated the original question with the Firefox error output. – Kerim Güney Mar 02 '22 at 08:25
  • 1
    Are you using Firefox from `apt`, or from Ubuntu's Snap Store, or from Mozilla's official binary downloads? It looks like it's unable to find either your ticket cache _or_ the Kerberos libraries (it might be affected by https://bugzilla.mozilla.org/show_bug.cgi?id=1734791). – u1686_grawity Mar 02 '22 at 10:20
  • @user1686 holy crap. You are right! I initially thought that that can't be the case, because it didn't work with Chrome/Chromium either but it turns out that both of those are installed as snap packages. I ran `snap remove firefox` and installed it with `apt` and now it works. Wow, this was hella annoying and difficult to debug. Thank you very much. If you respond to the question as an answer, I'll mark yours as the correct solution. – Kerim Güney Mar 02 '22 at 11:02
  • 1
    I would have suggested trying `export KRB5CCNAME="FILE:$HOME/krb5cc"` before kinit/firefox, in case the snap just doesn't allow access to /tmp. (Though home access might need to be [manually enabled](https://snapcraft.io/docs/home-interface) in Snap.) Failing that, either `"FILE:$HOME/.mozilla/krb5cc" or `"KEYRING:persistent:1001"` or `"DIR:$XDG_RUNTIME_DIR/krb5cc"` might also have worked, I'm not entirely sure what the profile allows. – u1686_grawity Mar 02 '22 at 11:12

2 Answers2

3

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.

u1686_grawity
  • 426,297
  • 64
  • 894
  • 966
  • `apt install firefox` will also install firefox as a snap package since Ubuntu 22.04. See https://askubuntu.com/questions/1399383/how-to-install-firefox-as-a-traditional-deb-package-without-snap-in-ubuntu-22 – shrx May 29 '23 at 09:27
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
  • 11
  • 2