Skip to content

ZOOKEEPER-5045: Fall back to TLSv1.2 default in FIPS mode#2380

Merged
anmolnar merged 9 commits into
apache:masterfrom
PDavid:ZOOKEEPER-5045
May 7, 2026
Merged

ZOOKEEPER-5045: Fall back to TLSv1.2 default in FIPS mode#2380
anmolnar merged 9 commits into
apache:masterfrom
PDavid:ZOOKEEPER-5045

Conversation

@PDavid
Copy link
Copy Markdown
Contributor

@PDavid PDavid commented May 4, 2026

No description provided.

@PDavid PDavid force-pushed the ZOOKEEPER-5045 branch 2 times, most recently from bb72473 to 72ddee6 Compare May 4, 2026 13:29
@PDavid PDavid marked this pull request as ready for review May 4, 2026 13:36
Copy link
Copy Markdown
Contributor

@anmolnar anmolnar left a comment

Choose a reason for hiding this comment

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

@PDavid Thanks for the patch. Overall lgtm. Just a few comments.

@PDavid PDavid force-pushed the ZOOKEEPER-5045 branch from 72ddee6 to 72e30cc Compare May 4, 2026 13:58
Comment thread zookeeper-server/src/test/java/org/apache/zookeeper/common/X509UtilTest.java Outdated
PDavid added 3 commits May 5, 2026 14:48
@PDavid
Copy link
Copy Markdown
Contributor Author

PDavid commented May 5, 2026

Hmm, X509UtilTest.testCreateSSLContext* tests failed in the PR build. I'll have a look.

@anmolnar
Copy link
Copy Markdown
Contributor

anmolnar commented May 5, 2026

How about the following?
No static field needed.

    private final AtomicReference<String> defaultProtocol = new AtomicReference<>();

    /**
     * Return TLSv1.2 when FIPS mode is enabled.
     * Otherwise, returns TLSv1.3 or TLSv1.2 depending on Java runtime version being used.
     * TLSv1.3 was first introduced in JDK11 and back-ported to OpenJDK 8u272.
     */
    public String defaultTlsProtocol(ZKConfig config) {
        String proto = defaultProtocol.get();
        if (proto != null) {
            return proto;
        }
        String protocol = TLS_1_2;
        if (!getFipsMode(config)) {
            List<String> supported = new ArrayList<>();
            try {
                supported = Arrays.asList(SSLContext.getDefault().getSupportedSSLParameters().getProtocols());
                LOG.info("Supported TLS protocols are {}", supported);
                if (supported.contains(TLS_1_3)) {
                    protocol = TLS_1_3;
                }
            } catch (NoSuchAlgorithmException e) {
                // Ignore.
            }
        }
        if (defaultProtocol.compareAndSet(null, protocol)) {
            LOG.info("Default TLS protocol is {}", protocol);
        } else {
            protocol = defaultProtocol.get();
        }
        return protocol;
    }

@anmolnar
Copy link
Copy Markdown
Contributor

anmolnar commented May 5, 2026

@meszibalu Actually it could be static/singleton, we don't need to calculate it for every single instance, do we?

Comment on lines +911 to +913
System.clearProperty("javax.net.ssl.trustStore");
System.clearProperty("javax.net.ssl.trustStorePassword");
System.clearProperty("javax.net.ssl.trustStoreType");
Copy link
Copy Markdown
Contributor

@anmolnar anmolnar May 5, 2026

Choose a reason for hiding this comment

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

I don't think we can implement a test like this. SSLContext.getDefault() will cache the problem and subsequent tests will fail even if the properties are reverted.

Copy link
Copy Markdown
Contributor

@meszibalu meszibalu May 6, 2026

Choose a reason for hiding this comment

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

According to the javadoc:

If a default context was set using the SSLContext.setDefault() method, it is returned. Otherwise, the first call of this method triggers the call SSLContext.getInstance("Default").

And the code in java 25:

public static SSLContext getDefault() throws NoSuchAlgorithmException {
    SSLContext temporaryContext = defaultContext;
    if (temporaryContext == null) {
        temporaryContext = SSLContext.getInstance("Default");
        if (!VH_DEFAULT_CONTEXT.compareAndSet(null, temporaryContext)) {
            temporaryContext = defaultContext;
        }
    }

    return temporaryContext;
}

Which means we can call SSLContext.getInstance("Default") and it will NOT cache.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Just a question: So if SSLContext.getDefault() will cache and not create a new SSL context, do we need our own complex caching solution to avoid calling SSLContext.getDefault() on every method call? Or can we just use the original solution which was without the field (e.g. the first commit)? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

OK, I tried to use SSLContext.getInstance("Default") instead of SSLContext.getDefault() but was not able to make the tests work.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I was not able to make this test work, not even with SSLContext.setDefault.

So I removed it. 🤷‍♂️

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Me neither. I spent a few hours yesterday with this, but I'm unable to "reset" the property values. All tests are green if run without the new test or alone, but not with it. There's some state change in the background which I couldn't find.

@PDavid PDavid force-pushed the ZOOKEEPER-5045 branch from 73d53b9 to 8a8f99c Compare May 6, 2026 10:03
@PDavid PDavid requested review from anmolnar and meszibalu May 6, 2026 13:53
Copy link
Copy Markdown
Contributor

@anmolnar anmolnar left a comment

Choose a reason for hiding this comment

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

Just one more thing.

}

public static final String DEFAULT_PROTOCOL = defaultTlsProtocol();
private final AtomicReference<String> defaultProtocol = new AtomicReference<>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Make it static, because we shouldn't calculate it for every instance.

because we shouldn't calculate it for every instance.
@PDavid PDavid requested a review from anmolnar May 6, 2026 19:39
PDavid added 2 commits May 7, 2026 08:28
defaultTlsProtocol should not depend on if it was called in FIPS mode or not.
Condense logging so that supported TLS protocols are not logged multiple times.
@PDavid
Copy link
Copy Markdown
Contributor Author

PDavid commented May 7, 2026

org.apache.zookeeper.server.admin.SnapshotAndRestoreCommandTest.testSnapshotAndRestoreCommand_streaming failed in the Jenkins PR build. I think it is unrelated.

@anmolnar anmolnar merged commit d4e15d3 into apache:master May 7, 2026
16 checks passed
@anmolnar
Copy link
Copy Markdown
Contributor

anmolnar commented May 7, 2026

Merged to master branch. Thanks @PDavid and @meszibalu !
Please create separate pull request for branch-3.9

@PDavid PDavid deleted the ZOOKEEPER-5045 branch May 7, 2026 13:59
@PDavid
Copy link
Copy Markdown
Contributor Author

PDavid commented May 7, 2026

Many thanks for the reviews @anmolnar and @meszibalu! 👍

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