Skip to content

Commit 529c8c1

Browse files
sfc-gh-pbulawasfc-gh-pczajka
authored andcommitted
SNOW-1825610: Initial OCSP Deprecation (#2165)
1 parent 201aa58 commit 529c8c1

File tree

6 files changed

+157
-41
lines changed

6 files changed

+157
-41
lines changed

DESCRIPTION.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,13 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
1212
- Removed the workaround for a Python 2.7 bug.
1313
- Added a <19.0.0 pin to pyarrow as a workaround to a bug affecting Azure Batch.
1414
- Optimized distribution package lookup to speed up import.
15-
- Fixed a bug where privatelink OCSP Cache url could not be determined if privatelink account name was specified in uppercase
15+
- Fixed a bug where privatelink OCSP Cache url could not be determined if privatelink account name was specified in uppercase.
16+
- Added support for iceberg tables to `write_pandas`.
17+
- Fixed base64 encoded private key tests.
18+
- Fixed a bug where file permission check happened on Windows.
19+
- Added support for File types.
20+
- Added `unsafe_file_write` connection parameter that restores the previous behaviour of saving files downloaded with GET with 644 permissions.
21+
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.
1622

1723
- v3.12.4(TBD)
1824
- Fixed a bug where multipart uploads to Azure would be missing their MD5 hashes.

src/snowflake/connector/connection.py

Lines changed: 38 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def _get_private_bytes_from_file(
197197
# add the new client type to the server to support these features.
198198
"internal_application_name": (CLIENT_NAME, (type(None), str)),
199199
"internal_application_version": (CLIENT_VERSION, (type(None), str)),
200-
"insecure_mode": (False, bool), # Error security fix requirement
200+
"disable_ocsp_checks": (False, bool),
201201
"ocsp_fail_open": (True, bool), # fail open on ocsp issues, default true
202202
"inject_client_pause": (0, int), # snowflake internal
203203
"session_parameters": (None, (type(None), dict)), # snowflake session parameters
@@ -321,8 +321,10 @@ class SnowflakeConnection:
321321
Use connect(..) to get the object.
322322
323323
Attributes:
324-
insecure_mode: Whether or not the connection is in insecure mode. Insecure mode means that the connection
325-
validates the TLS certificate but doesn't check revocation status.
324+
insecure_mode (deprecated): Whether or not the connection is in OCSP disabled mode. It means that the connection
325+
validates the TLS certificate but doesn't check revocation status with OCSP provider.
326+
disable_ocsp_checks: Whether or not the connection is in OCSP disabled mode. It means that the connection
327+
validates the TLS certificate but doesn't check revocation status with OCSP provider.
326328
ocsp_fail_open: Whether or not the connection is in fail open mode. Fail open mode decides if TLS certificates
327329
continue to be validated. Revoked certificates are blocked. Any other exceptions are disregarded.
328330
session_id: The session ID of the connection.
@@ -432,6 +434,25 @@ def __init__(
432434
elif "streamlit" in sys.modules:
433435
kwargs["application"] = "streamlit"
434436

437+
if "insecure_mode" in kwargs:
438+
warn_message = "The 'insecure_mode' connection property is deprecated. Please use 'disable_ocsp_checks' instead"
439+
warnings.warn(
440+
warn_message,
441+
DeprecationWarning,
442+
stacklevel=2,
443+
)
444+
445+
if (
446+
"disable_ocsp_checks" in kwargs
447+
and kwargs["disable_ocsp_checks"] != kwargs["insecure_mode"]
448+
):
449+
logger.warning(
450+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
451+
"Using the value of 'disable_ocsp_checks."
452+
)
453+
else:
454+
self._disable_ocsp_checks = kwargs["insecure_mode"]
455+
435456
self.converter = None
436457
self.query_context_cache: QueryContextCache | None = None
437458
self.query_context_cache_size = 5
@@ -463,19 +484,23 @@ def __init__(
463484
# check SNOW-1218851 for long term improvement plan to refactor ocsp code
464485
atexit.register(self._close_at_exit)
465486

487+
# Deprecated
466488
@property
467489
def insecure_mode(self) -> bool:
468-
return self._insecure_mode
490+
return self._disable_ocsp_checks
491+
492+
@property
493+
def disable_ocsp_checks(self) -> bool:
494+
return self._disable_ocsp_checks
469495

470496
@property
471497
def ocsp_fail_open(self) -> bool:
472498
return self._ocsp_fail_open
473499

474500
def _ocsp_mode(self) -> OCSPMode:
475-
"""OCSP mode. INSEC
476-
URE, FAIL_OPEN or FAIL_CLOSED."""
477-
if self.insecure_mode:
478-
return OCSPMode.INSECURE
501+
"""OCSP mode. DISABLE_OCSP_CHECKS, FAIL_OPEN or FAIL_CLOSED."""
502+
if self.disable_ocsp_checks:
503+
return OCSPMode.DISABLE_OCSP_CHECKS
479504
elif self.ocsp_fail_open:
480505
return OCSPMode.FAIL_OPEN
481506
else:
@@ -1276,7 +1301,7 @@ def __config(self, **kwargs):
12761301
)
12771302

12781303
if self.ocsp_fail_open:
1279-
logger.info(
1304+
logger.debug(
12801305
"This connection is in OCSP Fail Open Mode. "
12811306
"TLS Certificates would be checked for validity "
12821307
"and revocation status. Any other Certificate "
@@ -1285,12 +1310,10 @@ def __config(self, **kwargs):
12851310
"connectivity."
12861311
)
12871312

1288-
if self.insecure_mode:
1289-
logger.info(
1290-
"THIS CONNECTION IS IN INSECURE MODE. IT "
1291-
"MEANS THE CERTIFICATE WILL BE VALIDATED BUT THE "
1292-
"CERTIFICATE REVOCATION STATUS WILL NOT BE "
1293-
"CHECKED."
1313+
if self.disable_ocsp_checks:
1314+
logger.debug(
1315+
"This connection runs with disabled OCSP checks. "
1316+
"Revocation status of the certificate will not be checked against OCSP Responder."
12941317
)
12951318

12961319
def cmd_query(

src/snowflake/connector/constants.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -354,12 +354,14 @@ class OCSPMode(Enum):
354354
FAIL_OPEN: A response indicating a revoked certificate results in a failed connection. A response with any
355355
other certificate errors or statuses allows the connection to occur, but denotes the message in the logs
356356
at the WARNING level with the relevant details in JSON format.
357-
INSECURE: The connection will occur anyway.
357+
INSECURE (deprecated): The connection will occur anyway.
358+
DISABLE_OCSP_CHECKS: The OCSP check will not happen. If the certificate is valid then connection will occur.
358359
"""
359360

360361
FAIL_CLOSED = "FAIL_CLOSED"
361362
FAIL_OPEN = "FAIL_OPEN"
362363
INSECURE = "INSECURE"
364+
DISABLE_OCSP_CHECKS = "DISABLE_OCSP_CHECKS"
363365

364366

365367
@unique

src/snowflake/connector/ocsp_snowflake.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def __init__(self) -> None:
175175
self.cache_enabled = False
176176
self.cache_hit = False
177177
self.fail_open = False
178-
self.insecure_mode = False
178+
self.disable_ocsp_checks = False
179179

180180
def set_event_sub_type(self, event_sub_type: str) -> None:
181181
"""
@@ -224,8 +224,12 @@ def set_cache_hit(self, cache_hit) -> None:
224224
def set_fail_open(self, fail_open) -> None:
225225
self.fail_open = fail_open
226226

227+
# Deprecated
227228
def set_insecure_mode(self, insecure_mode) -> None:
228-
self.insecure_mode = insecure_mode
229+
self.disable_ocsp_checks = insecure_mode
230+
231+
def set_disable_ocsp_checks(self, disable_ocsp_checks) -> None:
232+
self.disable_ocsp_checks = disable_ocsp_checks
229233

230234
def generate_telemetry_data(
231235
self, event_type: str, urgent: bool = False
@@ -240,7 +244,7 @@ def generate_telemetry_data(
240244
TelemetryField.KEY_OOB_OCSP_REQUEST_BASE64.value: self.ocsp_req,
241245
TelemetryField.KEY_OOB_OCSP_RESPONDER_URL.value: self.ocsp_url,
242246
TelemetryField.KEY_OOB_ERROR_MESSAGE.value: self.error_msg,
243-
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.insecure_mode,
247+
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.disable_ocsp_checks,
244248
TelemetryField.KEY_OOB_FAIL_OPEN.value: self.fail_open,
245249
TelemetryField.KEY_OOB_CACHE_ENABLED.value: self.cache_enabled,
246250
TelemetryField.KEY_OOB_CACHE_HIT.value: self.cache_hit,
@@ -935,7 +939,7 @@ def validate_certfile(self, cert_filename, no_exception: bool = False):
935939
cert_map = {}
936940
telemetry_data = OCSPTelemetryData()
937941
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
938-
telemetry_data.set_insecure_mode(False)
942+
telemetry_data.set_disable_ocsp_checks(False)
939943
telemetry_data.set_sfc_peer_host(cert_filename)
940944
telemetry_data.set_fail_open(self.is_enabled_fail_open())
941945
try:
@@ -981,7 +985,7 @@ def validate(
981985

982986
telemetry_data = OCSPTelemetryData()
983987
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
984-
telemetry_data.set_insecure_mode(False)
988+
telemetry_data.set_disable_ocsp_checks(False)
985989
telemetry_data.set_sfc_peer_host(hostname)
986990
telemetry_data.set_fail_open(self.is_enabled_fail_open())
987991

@@ -1068,15 +1072,10 @@ def is_enabled_fail_open(self) -> bool:
10681072
return self.FAIL_OPEN
10691073

10701074
@staticmethod
1071-
def print_fail_open_warning(ocsp_log) -> None:
1072-
static_warning = (
1073-
"WARNING!!! Using fail-open to connect. Driver is connecting to an "
1074-
"HTTPS endpoint without OCSP based Certificate Revocation checking "
1075-
"as it could not obtain a valid OCSP Response to use from the CA OCSP "
1076-
"responder. Details:"
1077-
)
1078-
ocsp_warning = f"{static_warning} \n {ocsp_log}"
1079-
logger.warning(ocsp_warning)
1075+
def print_fail_open_debug(ocsp_log) -> None:
1076+
static_debug = "OCSP responder didn't respond correctly. Assuming certificate is not revoked. Details: "
1077+
ocsp_debug = f"{static_debug} \n {ocsp_log}"
1078+
logger.debug(ocsp_debug)
10801079

10811080
def validate_by_direct_connection(
10821081
self,
@@ -1164,7 +1163,7 @@ def verify_fail_open(self, ex_obj, telemetry_data):
11641163
)
11651164
return ex_obj
11661165
else:
1167-
SnowflakeOCSP.print_fail_open_warning(
1166+
SnowflakeOCSP.print_fail_open_debug(
11681167
telemetry_data.generate_telemetry_data("RevocationCheckFailure")
11691168
)
11701169
return None

src/snowflake/connector/ssl_wrap_socket.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
8181
FEATURE_OCSP_MODE.name,
8282
FEATURE_OCSP_RESPONSE_CACHE_FILE_NAME,
8383
)
84-
if FEATURE_OCSP_MODE != OCSPMode.INSECURE:
84+
if FEATURE_OCSP_MODE != OCSPMode.DISABLE_OCSP_CHECKS:
8585
from .ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto as SFOCSP
8686

8787
v = SFOCSP(
@@ -98,11 +98,9 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
9898
errno=ER_OCSP_RESPONSE_CERT_STATUS_REVOKED,
9999
)
100100
else:
101-
log.info(
102-
"THIS CONNECTION IS IN INSECURE "
103-
"MODE. IT MEANS THE CERTIFICATE WILL BE "
104-
"VALIDATED BUT THE CERTIFICATE REVOCATION "
105-
"STATUS WILL NOT BE CHECKED."
101+
log.debug(
102+
"This connection does not perform OCSP checks. "
103+
"Revocation status of the certificate will not be checked against OCSP Responder."
106104
)
107105

108106
return ret

test/integ/test_connection.py

Lines changed: 91 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ def test_bogus(db_parameters):
324324
host=db_parameters["host"],
325325
port=db_parameters["port"],
326326
login_timeout=5,
327-
insecure_mode=True,
327+
disable_ocsp_checks=True,
328328
)
329329

330330
with pytest.raises(DatabaseError):
@@ -1370,9 +1370,9 @@ def test_server_session_keep_alive(conn_cnx):
13701370

13711371

13721372
@pytest.mark.skipolddriver
1373-
def test_ocsp_mode_insecure(conn_cnx, is_public_test, caplog):
1373+
def test_ocsp_mode_disable_ocsp_checks(conn_cnx, is_public_test, caplog):
13741374
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1375-
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
1375+
with conn_cnx(disable_ocsp_checks=True) as conn, conn.cursor() as cur:
13761376
assert cur.execute("select 1").fetchall() == [(1,)]
13771377
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
13781378
caplog.clear()
@@ -1381,10 +1381,98 @@ def test_ocsp_mode_insecure(conn_cnx, is_public_test, caplog):
13811381
assert cur.execute("select 1").fetchall() == [(1,)]
13821382
if is_public_test:
13831383
assert "snowflake.connector.ocsp_snowflake" in caplog.text
1384+
assert "This connection does not perform OCSP checks." not in caplog.text
1385+
else:
1386+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1387+
1388+
1389+
@pytest.mark.skipolddriver
1390+
def test_ocsp_mode_insecure_mode(conn_cnx, is_public_test, caplog):
1391+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1392+
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
1393+
assert cur.execute("select 1").fetchall() == [(1,)]
1394+
if is_public_test:
1395+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1396+
assert "This connection does not perform OCSP checks." in caplog.text
1397+
else:
1398+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1399+
1400+
1401+
@pytest.mark.skipolddriver
1402+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_match(
1403+
conn_cnx, is_public_test, caplog
1404+
):
1405+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1406+
with conn_cnx(
1407+
insecure_mode=True, disable_ocsp_checks=True
1408+
) as conn, conn.cursor() as cur:
1409+
assert cur.execute("select 1").fetchall() == [(1,)]
1410+
if is_public_test:
1411+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1412+
assert (
1413+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1414+
"Using the value of 'disable_ocsp_checks."
1415+
) not in caplog.text
1416+
assert "This connection does not perform OCSP checks." in caplog.text
1417+
else:
1418+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1419+
1420+
1421+
@pytest.mark.skipolddriver
1422+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_disabled(
1423+
conn_cnx, is_public_test, caplog
1424+
):
1425+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1426+
with conn_cnx(
1427+
insecure_mode=False, disable_ocsp_checks=True
1428+
) as conn, conn.cursor() as cur:
1429+
assert cur.execute("select 1").fetchall() == [(1,)]
1430+
if is_public_test:
1431+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1432+
assert (
1433+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1434+
"Using the value of 'disable_ocsp_checks."
1435+
) in caplog.text
1436+
assert "This connection does not perform OCSP checks." in caplog.text
1437+
else:
1438+
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
1439+
1440+
1441+
@pytest.mark.skipolddriver
1442+
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_enabled(
1443+
conn_cnx, is_public_test, caplog
1444+
):
1445+
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
1446+
with conn_cnx(
1447+
insecure_mode=True, disable_ocsp_checks=False
1448+
) as conn, conn.cursor() as cur:
1449+
assert cur.execute("select 1").fetchall() == [(1,)]
1450+
if is_public_test:
1451+
assert "snowflake.connector.ocsp_snowflake" in caplog.text
1452+
assert (
1453+
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
1454+
"Using the value of 'disable_ocsp_checks."
1455+
) in caplog.text
1456+
assert "This connection does not perform OCSP checks." not in caplog.text
13841457
else:
13851458
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
13861459

13871460

1461+
@pytest.mark.skipolddriver
1462+
def test_ocsp_mode_insecure_mode_deprecation_warning(conn_cnx):
1463+
with warnings.catch_warnings(record=True) as w:
1464+
warnings.simplefilter("ignore")
1465+
warnings.filterwarnings(
1466+
"always", category=DeprecationWarning, message=".*insecure_mode"
1467+
)
1468+
with conn_cnx(insecure_mode=True):
1469+
assert len(w) == 1
1470+
assert issubclass(w[0].category, DeprecationWarning)
1471+
assert "The 'insecure_mode' connection property is deprecated." in str(
1472+
w[0].message
1473+
)
1474+
1475+
13881476
@pytest.mark.skipolddriver
13891477
def test_connection_atexit_close(conn_cnx):
13901478
"""Basic Connection test without schema."""

0 commit comments

Comments
 (0)