From 895b0980d4eefde012550515c8b0afbab9f656f8 Mon Sep 17 00:00:00 2001 From: vikram barbade Date: Fri, 10 Jan 2025 16:33:10 -0800 Subject: [PATCH] SNOW-1821504: [JDBC] Initialal OCSP deprecation plan steps (#2008) --- .../net/snowflake/client/core/HttpUtil.java | 8 +- .../net/snowflake/client/core/OCSPMode.java | 11 +- .../snowflake/client/core/SFBaseSession.java | 22 +++- .../client/core/SFSessionProperty.java | 6 + .../snowflake/client/core/SFTrustManager.java | 10 +- .../net/snowflake/client/jdbc/ErrorCode.java | 3 +- .../client/jdbc/ConnectionLatestIT.java | 26 ++++ ...ConnectionWithDisableOCSPModeLatestIT.java | 121 ++++++++++++++++++ 8 files changed, 192 insertions(+), 15 deletions(-) create mode 100644 src/test/java/net/snowflake/client/jdbc/ConnectionWithDisableOCSPModeLatestIT.java diff --git a/src/main/java/net/snowflake/client/core/HttpUtil.java b/src/main/java/net/snowflake/client/core/HttpUtil.java index 23b83df09..41ea57f32 100644 --- a/src/main/java/net/snowflake/client/core/HttpUtil.java +++ b/src/main/java/net/snowflake/client/core/HttpUtil.java @@ -345,9 +345,9 @@ public static CloseableHttpClient buildHttpClient( } TrustManager[] trustManagers = null; - if (key != null && key.getOcspMode() != OCSPMode.INSECURE) { - // A custom TrustManager is required only if insecureMode is disabled, - // which is by default in the production. insecureMode can be enabled + if (key != null && key.getOcspMode() != OCSPMode.DISABLE_OCSP_CHECKS) { + // A custom TrustManager is required only if disableOCSPChecks is disabled, + // which is by default in the production. disableOCSPChecks can be enabled // 1) OCSP service is down for reasons, 2) PowerMock test that doesn't // care OCSP checks. // OCSP FailOpen is ON by default @@ -742,7 +742,7 @@ public static String executeRequest( HttpClientSettingsKey ocspAndProxyKey, ExecTimeTelemetryData execTimeData) throws SnowflakeSQLException, IOException { - boolean ocspEnabled = !(ocspAndProxyKey.getOcspMode().equals(OCSPMode.INSECURE)); + boolean ocspEnabled = !(ocspAndProxyKey.getOcspMode().equals(OCSPMode.DISABLE_OCSP_CHECKS)); logger.debug("Executing request with OCSP enabled: {}", ocspEnabled); execTimeData.setOCSPStatus(ocspEnabled); return executeRequestInternal( diff --git a/src/main/java/net/snowflake/client/core/OCSPMode.java b/src/main/java/net/snowflake/client/core/OCSPMode.java index 1b9144a81..03c2a3bbb 100644 --- a/src/main/java/net/snowflake/client/core/OCSPMode.java +++ b/src/main/java/net/snowflake/client/core/OCSPMode.java @@ -15,8 +15,15 @@ public enum OCSPMode { */ FAIL_OPEN(1), - /** Insure mode. No OCSP check is made. */ - INSECURE(2); + /** + * @deprecated Use {@link #DISABLE_OCSP_CHECKS} for clarity. This configuration option is used to + * disable OCSP verification. Insure mode. No OCSP check is made. + */ + @Deprecated + INSECURE(2), + + /** Disable OCSP checks. It's used to disable OCSP verification. */ + DISABLE_OCSP_CHECKS(3); private final int value; diff --git a/src/main/java/net/snowflake/client/core/SFBaseSession.java b/src/main/java/net/snowflake/client/core/SFBaseSession.java index 0bb62bed6..bd3dbca4f 100644 --- a/src/main/java/net/snowflake/client/core/SFBaseSession.java +++ b/src/main/java/net/snowflake/client/core/SFBaseSession.java @@ -712,14 +712,32 @@ public void unsetInvalidProxyHostAndPort() { * Get OCSP mode * * @return {@link OCSPMode} + * @throws SnowflakeSQLException */ - public OCSPMode getOCSPMode() { + public OCSPMode getOCSPMode() throws SnowflakeSQLException { OCSPMode ret; + Boolean disableOCSPChecks = + (Boolean) connectionPropertiesMap.get(SFSessionProperty.DISABLE_OCSP_CHECKS); Boolean insecureMode = (Boolean) connectionPropertiesMap.get(SFSessionProperty.INSECURE_MODE); if (insecureMode != null && insecureMode) { + logger.warn( + "The 'insecureMode' connection property is deprecated. Please use 'disableOCSPChecks' instead."); + } + + if ((disableOCSPChecks != null && insecureMode != null) + && (disableOCSPChecks != insecureMode)) { + logger.error( + "The values for 'disableOCSPChecks' and 'insecureMode' must be identical. " + + "Please unset insecureMode."); + throw new SnowflakeSQLException( + ErrorCode.DISABLEOCSP_INSECUREMODE_VALUE_MISMATCH, + "The values for 'disableOCSPChecks' and 'insecureMode' " + "must be identical."); + } + if ((disableOCSPChecks != null && disableOCSPChecks) + || (insecureMode != null && insecureMode)) { // skip OCSP checks - ret = OCSPMode.INSECURE; + ret = OCSPMode.DISABLE_OCSP_CHECKS; } else if (!connectionPropertiesMap.containsKey(SFSessionProperty.OCSP_FAIL_OPEN) || (boolean) connectionPropertiesMap.get(SFSessionProperty.OCSP_FAIL_OPEN)) { // fail open (by default, not set) diff --git a/src/main/java/net/snowflake/client/core/SFSessionProperty.java b/src/main/java/net/snowflake/client/core/SFSessionProperty.java index f282b3019..7cc07d00b 100644 --- a/src/main/java/net/snowflake/client/core/SFSessionProperty.java +++ b/src/main/java/net/snowflake/client/core/SFSessionProperty.java @@ -37,7 +37,13 @@ public enum SFSessionProperty { APP_ID("appId", false, String.class), APP_VERSION("appVersion", false, String.class), OCSP_FAIL_OPEN("ocspFailOpen", false, Boolean.class), + /** + * @deprecated Use {@link #DISABLE_OCSP_CHECKS} for clarity. This configuration option is used to + * disable OCSP verification. + */ + @Deprecated INSECURE_MODE("insecureMode", false, Boolean.class), + DISABLE_OCSP_CHECKS("disableOCSPChecks", false, Boolean.class), QUERY_TIMEOUT("queryTimeout", false, Integer.class), STRINGS_QUOTED("stringsQuotedForColumnDef", false, Boolean.class), APPLICATION("application", false, String.class), diff --git a/src/main/java/net/snowflake/client/core/SFTrustManager.java b/src/main/java/net/snowflake/client/core/SFTrustManager.java index 740c70fe3..275037eb0 100644 --- a/src/main/java/net/snowflake/client/core/SFTrustManager.java +++ b/src/main/java/net/snowflake/client/core/SFTrustManager.java @@ -841,10 +841,8 @@ private void executeRevocationStatusChecks( } private String generateFailOpenLog(String logData) { - return "WARNING!!! Using fail-open to connect. Driver is connecting to an " - + "HTTPS endpoint without OCSP based Certificate Revocation checking " - + "as it could not obtain a valid OCSP Response to use from the CA OCSP " - + "responder. Details: \n" + return "OCSP responder didn't respond correctly. Assuming certificate is " + + "not revoked. Details: " + logData; } @@ -981,7 +979,7 @@ private void executeOneRevocationStatusCheck( ocspLog = telemetryData.generateTelemetry(SF_OCSP_EVENT_TYPE_VALIDATION_ERROR, error); if (isOCSPFailOpen()) { // Log includes fail-open warning. - logger.error(generateFailOpenLog(ocspLog), false); + logger.debug(generateFailOpenLog(ocspLog), false); } else { // still not success, raise an error. logger.debug(ocspLog, false); @@ -1163,7 +1161,7 @@ private OCSPResp fetchOcspResponse( new DecorrelatedJitterBackoff(sleepTime, MAX_SLEEPING_TIME_IN_MILLISECONDS); boolean success = false; - final int maxRetryCounter = isOCSPFailOpen() ? 1 : 3; + final int maxRetryCounter = isOCSPFailOpen() ? 1 : 2; Exception savedEx = null; CloseableHttpClient httpClient = ocspCacheServerClient.computeIfAbsent( diff --git a/src/main/java/net/snowflake/client/jdbc/ErrorCode.java b/src/main/java/net/snowflake/client/jdbc/ErrorCode.java index b9cc71491..124cdd98a 100644 --- a/src/main/java/net/snowflake/client/jdbc/ErrorCode.java +++ b/src/main/java/net/snowflake/client/jdbc/ErrorCode.java @@ -83,7 +83,8 @@ public enum ErrorCode { INVALID_OKTA_USERNAME(200060, SqlState.CONNECTION_EXCEPTION), GCP_SERVICE_ERROR(200061, SqlState.SYSTEM_ERROR), AUTHENTICATOR_REQUEST_TIMEOUT(200062, SqlState.CONNECTION_EXCEPTION), - INVALID_STRUCT_DATA(200063, SqlState.DATA_EXCEPTION); + INVALID_STRUCT_DATA(200063, SqlState.DATA_EXCEPTION), + DISABLEOCSP_INSECUREMODE_VALUE_MISMATCH(200064, SqlState.INVALID_PARAMETER_VALUE); public static final String errorMessageResource = "net.snowflake.client.jdbc.jdbc_error_messages"; diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java index b58643460..0fd3a7d30 100644 --- a/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionLatestIT.java @@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -1636,4 +1637,29 @@ public void shouldFailOnSslExceptionWithLinkToTroubleShootingGuide() throws Inte "https://docs.snowflake.com/en/user-guide/client-connectivity-troubleshooting/overview")); } } + + /** + * Test production connectivity with disableOCSPChecksMode enabled. This test applies to driver + * versions after 3.21.0 + */ + @Test + public void testDisableOCSPChecksMode() throws SQLException { + + String deploymentUrl = "jdbc:snowflake://sfcsupport.snowflakecomputing.com"; + Properties properties = new Properties(); + + properties.put("user", "fakeuser"); + properties.put("password", "fakepwd"); + properties.put("account", "fakeaccount"); + properties.put("disableOCSPChecks", true); + SQLException thrown = + assertThrows( + SQLException.class, + () -> { + DriverManager.getConnection(deploymentUrl, properties); + }); + + assertThat( + thrown.getErrorCode(), anyOf(is(INVALID_CONNECTION_INFO_CODE), is(BAD_REQUEST_GS_CODE))); + } } diff --git a/src/test/java/net/snowflake/client/jdbc/ConnectionWithDisableOCSPModeLatestIT.java b/src/test/java/net/snowflake/client/jdbc/ConnectionWithDisableOCSPModeLatestIT.java new file mode 100644 index 000000000..ff6e5f83e --- /dev/null +++ b/src/test/java/net/snowflake/client/jdbc/ConnectionWithDisableOCSPModeLatestIT.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2025 Snowflake Computing Inc. All right reserved. + */ +package net.snowflake.client.jdbc; + +import static org.hamcrest.CoreMatchers.anyOf; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.Properties; +import net.snowflake.client.category.TestTags; +import net.snowflake.client.core.SFTrustManager; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Test; + +/** Tests for connection with DisableOCSPchecks and insecuremode settings. */ +@Tag(TestTags.CONNECTION) +public class ConnectionWithDisableOCSPModeLatestIT extends BaseJDBCTest { + public static final int INVALID_CONNECTION_INFO_CODE = 390100; + private static final int DISABLE_OCSP_INSECURE_MODE_MISMATCH = 200064; + public static final int BAD_REQUEST_GS_CODE = 390400; + + @BeforeEach + public void setUp() { + SFTrustManager.deleteCache(); + } + + @AfterEach + public void tearDown() { + SFTrustManager.cleanTestSystemParameters(); + } + + /** + * Test connectivity with disableOCSPChecksMode and insecure mode enabled. This test applies to + * driver versions after 3.21.0 + */ + @Test + public void testDisableOCSPChecksModeAndInsecureModeSet() throws SQLException { + + boolean disableOCSPChecks = true; + boolean insecureMode = true; + assertThat( + returnErrorCodeFromConnection(disableOCSPChecks, insecureMode), + anyOf(is(INVALID_CONNECTION_INFO_CODE), is(BAD_REQUEST_GS_CODE))); + } + + /** + * Test production connectivity with only disableOCSPChecksMode enabled. This test applies to + * driver versions after 3.21.0 + */ + @Test + public void testDisableOCSPChecksModeSet() throws SQLException { + boolean disableOCSPChecks = true; + assertThat( + returnErrorCodeFromConnection(disableOCSPChecks, null), + anyOf(is(INVALID_CONNECTION_INFO_CODE), is(BAD_REQUEST_GS_CODE))); + } + + /** + * Test production connectivity with only insecureMode enabled. This test applies to driver + * versions after 3.21.0 + */ + @Test + public void testInsecureModeSet() throws SQLException { + boolean insecureMode = true; + assertThat( + returnErrorCodeFromConnection(null, insecureMode), + anyOf(is(INVALID_CONNECTION_INFO_CODE), is(BAD_REQUEST_GS_CODE))); + } + + /** + * Test production connectivity with disableOCSPChecksMode enabled AND insecureMode disabled. This + * test applies to driver versions after 3.21.0 + */ + @Test + public void testDisableOCSPChecksModeAndInsecureModeMismatched() throws SQLException { + boolean disableOCSPChecks = true; + boolean insecureMode = false; + assertThat( + returnErrorCodeFromConnection(disableOCSPChecks, insecureMode), + anyOf(is(DISABLE_OCSP_INSECURE_MODE_MISMATCH))); + } + + /** + * Helper method to return error code from connection. + * + * @param disableOSCPChecks + * @param isInsecureMode + * @return SF Error code + * @throws SQLException + */ + public int returnErrorCodeFromConnection(Boolean disableOSCPChecks, Boolean isInsecureMode) + throws SQLException { + String deploymentUrl = "jdbc:snowflake://sfcsupport.snowflakecomputing.com"; + Properties properties = new Properties(); + + properties.put("user", "fakeuser"); + properties.put("password", "fakepwd"); + properties.put("account", "fakeaccount"); + if (disableOSCPChecks != null) { + properties.put("disableOCSPChecks", disableOSCPChecks); + } + if (isInsecureMode != null) { + properties.put("insecureMode", isInsecureMode); + } + + SQLException thrown = + assertThrows( + SQLException.class, + () -> { + DriverManager.getConnection(deploymentUrl, properties); + }); + + return (thrown.getErrorCode()); + } +}