Skip to content

Conversation

bucaojit
Copy link
Member

Adding properties:
jaasconfigpath: Set a custom jaas.config file path if the default doesn't work or can't be modified. This was used since the default jaas.conf that was getting picked up by Tableau was in the jdbcserver.jar needed to be changed to get a working connection.
gssnativemode: Used on Windows to allow the use of MIT Kerberos vs the native Windows Kerberos. Set this to false to use MIT Kerberos.

Tested on Mac with a locally setup KDC using Docker containers.
Tested on Windows against ldaptest.10gen.cc using MIT Kerberos.

@bucaojit bucaojit requested a review from a team as a code owner August 15, 2025 22:41
Copy link
Collaborator

@jchemburkar jchemburkar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm!

Copy link
Collaborator

@nbagnard nbagnard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a good start, but if we are able to set the config only for the driver that would be much better.
If not, we want to discuss that with Alexi, and maybe the people at Tableau.

Could we add an integration test too?

MongoCredential.OIDC_HUMAN_CALLBACK_KEY, oidcCallback);
settingsBuilder.credential(credential);
} else if (authMechanism.equals(GSSAPI)) {

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change


String jaasPath = connectionProperties.getJaasConfigPath();
if (jaasPath != null && !jaasPath.isEmpty()) {
System.setProperty("java.security.auth.login.config", jaasPath);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The conf changes will affect more than our driver. It will affect everything currently running JVM.
This is a problem, especially with tools like Tableau. We need to talk about it a bit more.
Here is a starting point from Gemini on how we could use a local conf (possibly, we want to try it out to make sure this is true):

import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import java.util.Collections;
import java.util.Map;
import java.util.logging.Logger;

public class MongoJaasProgrammatic {

    private static final Logger LOGGER = Logger.getLogger(MongoJaasProgrammatic.class.getName());

    public static void main(String[] args) {
        // Step 1: Set the custom JAAS Configuration programmatically
        // MyJaasConfig.loadConfiguration(); // Reuse the class from the previous answer

        Subject authenticatedSubject = null;
        try {
            LoginContext loginContext = new LoginContext("MyApplication");
            loginContext.login();
            authenticatedSubject = loginContext.getSubject();
        } catch (Exception e) {
            LOGGER.severe("JAAS login failed: " + e.getMessage());
            return;
        }

        if (authenticatedSubject == null) {
            LOGGER.severe("Authenticated subject is null. Exiting.");
            return;
        }

        // Step 2: Create a MongoCredential with the authenticated subject
        Map<String, String> properties = Collections.singletonMap(MongoCredential.JAVA_SUBJECT_KEY, authenticatedSubject.toString());
        MongoCredential credential = MongoCredential.createGSSAPICredential(properties);

        // Step 3: Build MongoClientSettings with the credential
        MongoClientSettings settings = MongoClientSettings.builder()
                .credential(credential)
                .build();
        
        // Step 4: Create MongoClient using the custom settings
        try (MongoClient mongoClient = MongoClients.create(settings)) {
            // Your database operations here
            System.out.println("Successfully connected to MongoDB using programmatic JAAS config.");
        } catch (Exception e) {
            LOGGER.severe("MongoDB connection failed: " + e.getMessage());
        }
    }
}


String gssNative = connectionProperties.getGssNativeMode();
if (gssNative != null && !gssNative.isEmpty()) {
System.setProperty("sun.security.jgss.native", gssNative);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, we want to look at using a local conf to make sure we don't impact other drivers when using Tableau for example.
Here is another Gemini quickstart:

import com.mongodb.MongoClientSettings;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;

import javax.security.auth.Subject;
import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.Configuration;
import javax.security.auth.login.LoginContext;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;

public class GssapiMongoNoSystemProp {

    private static final Logger LOGGER = Logger.getLogger(GssapiMongoNoSystemProp.class.getName());

    public static void main(String[] args) {

        // Step 1: Create a custom JAAS Configuration in memory.
        Configuration jaasConfig = new Configuration() {
            @Override
            public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
                if ("MongoConnection".equals(name)) {
                    Map<String, String> options = new HashMap<>();
                    options.put("useKeyTab", "true");
                    options.put("keyTab", "/path/to/your/keytab/file.keytab");
                    options.put("principal", "[email protected]");
                    options.put("storeKey", "true");
                    options.put("doNotPrompt", "true");
                    
                    return new AppConfigurationEntry[]{
                        new AppConfigurationEntry(
                            "com.sun.security.auth.module.Krb5LoginModule",
                            AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
                            options
                        )
                    };
                }
                return null;
            }
        };

        Subject authenticatedSubject = null;
        try {
            // Step 2: Use the custom Configuration to perform the login.
            // No need for a separate krb5.conf file if your
            // principal and keytab are correct. Kerberos will
            // try to auto-discover the KDC.
            LoginContext loginContext = new LoginContext("MongoConnection", authenticatedSubject, null, jaasConfig);
            loginContext.login();
            authenticatedSubject = loginContext.getSubject();
            LOGGER.info("Kerberos login successful using in-memory config.");
        } catch (Exception e) {
            LOGGER.severe("Kerberos login failed: " + e.getMessage());
            return;
        }

        if (authenticatedSubject == null) {
            LOGGER.severe("Authenticated subject is null. Cannot proceed.");
            return;
        }

        // Step 3: Create a MongoCredential with the authenticated subject.
        Map<String, Object> properties = Collections.singletonMap(
                MongoCredential.JAVA_SUBJECT_KEY, authenticatedSubject
        );

        MongoCredential credential = MongoCredential.createGSSAPICredential(
                // The principal must match the one used in the login module options.
                "[email protected]" 
        ).withMechanismProperties(properties);
        
        // Build MongoClientSettings
        MongoClientSettings settings = MongoClientSettings.builder()
                .credential(credential)
                .build();
        
        // Create MongoClient using the custom settings.
        try (MongoClient mongoClient = MongoClients.create(settings)) {
            System.out.println("Successfully connected to MongoDB with GSSAPI using programmatic configs.");
            mongoClient.listDatabaseNames().forEach(System.out::println);
        } catch (Exception e) {
            LOGGER.severe("MongoDB connection failed: " + e.getMessage());
        }
    }
}

String gssNativeModeVal = info.getProperty(GSS_NATIVE_MODE.getPropertyName());
if (gssNativeModeVal != null) {
gssNativeModeVal = gssNativeModeVal.trim().toLowerCase();
if (!"true".equals(gssNativeModeVal) && !"false".equals(gssNativeModeVal)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to be less restrictive.

Suggested change
if (!"true".equals(gssNativeModeVal) && !"false".equals(gssNativeModeVal)) {
if (!"true".equalsIgnoreCase(gssNativeModeVal) && !"false".equalsIgnoreCase(gssNativeModeVal)) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants