Subject: [pgjdbc/pgjdbc] PR #3451: Support default GSS credentials in the Java Postgres client

From: "nrhall (@nrhall)" <[email-masked]>
Date: Thu, 21 Nov 2024 18:14:34 +0000

When using the GSSAPI for authentication without JAAS, the postgres Java client library assumes that the GSS (kerberos) principal name is `$user@$REALM`, and hard-codes the username in the call to `GSSManager.createCredential()` before trying to create the credential.  In most kerberos environments, this is a perfectly reasonable assumption, but it breaks when the user running the process does not have a user formatted in that way.

`GSSManager.createCredential()` has a different overload that doesn't take any hints over the principal to use to create a credential, which instead looks in your Kerberos ccache to see what the default credential is and uses that instead.  This continues to assume that you already have a valid Kerberos ccache (which many corporate environments, such as ours, create automatically for you).

This PR allows the user to provide a new option in the postgres connection properties called `gssUseDefaultCreds` which causes the code to call the no-hint version of `createCredential()` to avoid this particular issue.  I'll note that in most cases, getting the default creds is exactly what you want, and it might even be that a better fix is for this to be the default, but I've not done that here.

A reasonable question at this point is: well, why not use JAAS to do that for you?  The answer is nuanced - if you happen to use a file-based ccache, then you can indeed use JAAS for this.  Unfortunately though, JAAS uses the "pure Java" part of the JGSS codebase which (at least on Linux) does not have a "native" implementation, and is limited in the types of ccache supported.  In particular, it does not support KCM, which is a standard way to use a host-based daemon to store the key material for you.  There are several implementations of KCM (Heimdal Kerberos has one, as do RedHat with SSSD), and using KCM requires the use of the system libraries via the JGSS 'native' implementation, which JAAS does not support.

Thus in addition to avoiding the naming assuming, this PR also allows for the use of existing default credentials via the 'native' JGSS implementation on Linux, and hence supports all ccache types that the system libraries do.

### All Submissions:

* [x] Have you followed the guidelines in our [Contributing](https://github.com/pgjdbc/pgjdbc/blob/master/CONTRIBUTING.md) document?
* [x] Have you checked to ensure there aren't other open [Pull Requests](../../pulls) for the same update/change?

<!-- You can erase any parts of this template not applicable to your Pull Request. -->

### New Feature Submissions:

1. [x] Does your submission pass tests?  Yes
2. [x] Does `./gradlew styleCheck` pass ?  Yes
3. [x] Have you added your new test classes to an existing test suite in alphabetical order?  (see below re tests)

### Changes to Existing Features:

* [x] Does this break existing behaviour? If so please explain.

No breaks to existing behaviour, as the option defaults to 'false'.  It only changes one piece of code, introducing a conditional to use default GSS credentials instead of assuming that the non-fully qualified principal name is the same as the postgres 'user'.

* [x] Have you added an explanation of what your changes do and why you'd like us to include them?

Documentation updated, motivation above.

* [x] Have you written new tests for your core changes, as applicable?

I'm happy to write tests, but I can't see how the existing GSS code-paths are tested (and I imagine there isn't a sandbox on github where they can be tested).  If you have pointers, please provide and I'll work within that.

* [x] Have you successfully run tests with your changes locally?

Yes - these changes work in our Kerberos environment, where we have a slightly unusual setup where the principals that people use day-to-day are not strictly user-principals (this is perfectly valid, but unusual), and where the 'short' (no realm) principal name is derived from the username, but does not equal it.

Tested on RHEL8.

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 22 Nov 2024 17:37:59 +0000

Thanks very much @vlsi and @davecramer!

---

From: "ecki (@ecki)" <[email-masked]>
Date: Tue, 21 Jan 2025 12:37:46 +0000

Is that also supposed to work with default/native cache on Windows SPSS?

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Tue, 21 Jan 2025 12:40:26 +0000

Since we don't have a windows environment to test it's hard to know. If you can test this it would be appreciated.

---

From: "ecki (@ecki)" <[email-masked]>
Date: Wed, 22 Jan 2025 01:45:35 +0000

I am on it. maybe it’s not relevant or I had a th8nko, since it has it’s own spnego and sspi modes (lets see… what i can see)

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 10:59:48 +0000

Some testing this morning on Windows suggests that this isn't needed - it just works as it should - you need "jaasLogin=false", and it'll skip trying to use JAAS and just use native Windows auth - `GSS_LIB` defaults to `auto`, so it'll try and use SSPI automatically and succeed.  Windows/Kerberos "just works" very well in general.

This doesn't work in the same way on Linux, where you could have a variety of different kerberos configurations - e.g. if you have a setup where username@DEFAULT_REALM isn't your default principal name, or you need to use something like KCM as opposed to just a ccache on disk (JAAS/JGSS don't support KCM using the pure Java code, you need it to drop to using the system libs with `com.sun.security.gss.native`, which the JAAS Krb5LoginModule will never do - but the GSSAPI primitives do).

In our corp environment, if I just set `jaasLogin=false`, it fails to find a TGT (gets both the realm and my default principal name wrong, and even if it had, it would fail to find an on-disk ccache); adding `gssUseDefaultCreds=true` just says "make no assumptions, just get the TGT that the running user has using the system libraries/configuration".  One could argue this is actually the most sensible default (very much in-line with the way SSPI works on Windows), but it's not the way the library has worked historically, so this PR didn't make that be the case.

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:18:32 +0000

Java's support for Kerberos has always been...strained, not helped by enterprise vendors offering Kerberos support with clearly no understanding on how it really works.

JAAS in the JVM itself is one of the worst - all the docs make it sound like you basically have to use a keytab, and the set of options to just make it use the local ccache without any other magic are pretty baffling to a new user.  Enabling 'native' JGSS support in the JVM doesn't work for credential acquisition via JAAS on Linux (yet does on Mac/Windows) - so if you really need the system libraries/config to be used (e.g. to support KCM), you can't.  And nearly everything uses JAAS... :(

We have a corporate Kerberos environment where we ensure that users always have the right TGT/credentials wherever they need them - we nearly never want a user to obtain a fresh set of credentials from somewhere, because that's almost certainly not what they want.

That all said - I should say that pgjdbc was already good in that it supported a lot of the right things - e.g. auto mode for SSPI, and more straightforward Linux MIT/Heimdal setups probably just work out the box with a file based ccache and `jaasLogin=false`.  The extension here just means more esoteric setups work better.

It's possible that with a little more work a sensible `auto` mode could work on Linux too - e.g. it's actually possible that making the changes in this PR the default behaviour might actually work for nearly all cases where you've set `jaasLogin=false`.  It would also be nice to not need to set `jaasLogin=false` so that you need no arguments for integrated GSSAPI based logins to work but I'm not sure if there are other gotchas there.

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:31:30 +0000

Some further testing suggests that `jaasLogin=false` on it's own works well if you have a default setup (`user@DEFAULT_REALM` as principal, file-based ccache).  Adding the `gssUseDefaultCreds=true` doesn't change the way that works much - with all defaults, this code will acquire the same creds in each branch of the conditional - either by using the hard-coded principal name and calling `createCredential`, or just setting it to null and letting the Kerberos system libs figure it out:
```
        if (gssUseDefaultCreds) {
          clientCreds = manager.createCredential(GSSCredential.INITIATE_ONLY);
        } else {
          GSSName clientName = manager.createName(principalName, GSSName.NT_USER_NAME);
          clientCreds = manager.createCredential(clientName, 8 * 3600, desiredMechs,
              GSSCredential.INITIATE_ONLY);
        }
```
Anyway, just thought it was worth mentioning!

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:42:37 +0000

@nrhall This is awesome, you are correct it is poorly implemented and documented. 
Any chance you could run tests for us to keep us honest. Setting up the environment is the hard part.

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:50:07 +0000

I'd be happy to help - I can't promise to have a lot of spare time to do actual dev work, but happy to try and get to a point where we have a working test environment that makes it easy to run the tests.  I might need a few pointers on which of the tests are relevant - I did spend a little time looking through when I submitted this originally but I wasn't quite sure.

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:51:20 +0000

(I should add that over time, I think we'll rely quite a lot on this library, and we're an OSS-friendly company, so we'll definitely contribute back fixes wherever we can)

---

From: "davecramer (@davecramer)" <[email-masked]>
Date: Fri, 24 Jan 2025 11:53:02 +0000

I wrote this https://github.com/pgjdbc/pgjdbc/tree/master/test-gss at one point. I'm pretty sure it's stale as I haven't run it in a while. Perhaps you can use it as a starting point ?

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 12:18:38 +0000

Thanks @davecramer - I'll try and get some time to look at this.  No timescale promises, but it would be good to have a working set of tests here.

---

From: "ecki (@ecki)" <[email-masked]>
Date: Fri, 24 Jan 2025 14:43:27 +0000

> Anyway, just thought it was worth mentioning!

Very much apreciated! (if you want to add to it, can you say if you use SSPI on Windows clients with (Linux) Servers in a Domain without local credential files? Does that work now despite credential guard?)

---

From: "nrhall (@nrhall)" <[email-masked]>
Date: Fri, 24 Jan 2025 15:41:49 +0000

> Very much apreciated! (if you want to add to it, can you say if you use SSPI on Windows clients with (Linux) Servers in a Domain without local credential files? Does that work now despite credentia…

As it happened, I was testing on a Win11 host, which has CredentialGuard enabled...

And yes - those clients talk to Linux servers, some in MIT Kerberos realms using Linux MIT KDCs, others using Windows AD KDCs.