Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
- Bumped pyOpenSSL dependency upper boundary from <25.0.0 to <26.0.0.
- Removed the workaround for a Python 2.7 bug.
- Added a <19.0.0 pin to pyarrow as a workaround to a bug affecting Azure Batch.
- Deprecated `insecure_mode` connection property and replaced it with `disable_ocsp_checks` with the same behavior as the former property.

- v3.13.2(January 29, 2025)
- Changed not to use scoped temporary objects.
Expand Down
53 changes: 38 additions & 15 deletions src/snowflake/connector/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def _get_private_bytes_from_file(
# add the new client type to the server to support these features.
"internal_application_name": (CLIENT_NAME, (type(None), str)),
"internal_application_version": (CLIENT_VERSION, (type(None), str)),
"insecure_mode": (False, bool), # Error security fix requirement
"disable_ocsp_checks": (False, bool),
"ocsp_fail_open": (True, bool), # fail open on ocsp issues, default true
"inject_client_pause": (0, int), # snowflake internal
"session_parameters": (None, (type(None), dict)), # snowflake session parameters
Expand Down Expand Up @@ -327,8 +327,10 @@ class SnowflakeConnection:
Use connect(..) to get the object.

Attributes:
insecure_mode: Whether or not the connection is in insecure mode. Insecure mode means that the connection
validates the TLS certificate but doesn't check revocation status.
insecure_mode (deprecated): Whether or not the connection is in OCSP disabled mode. It means that the connection
validates the TLS certificate but doesn't check revocation status with OCSP provider.
disable_ocsp_checks: Whether or not the connection is in OCSP disabled mode. It means that the connection
validates the TLS certificate but doesn't check revocation status with OCSP provider.
ocsp_fail_open: Whether or not the connection is in fail open mode. Fail open mode decides if TLS certificates
continue to be validated. Revoked certificates are blocked. Any other exceptions are disregarded.
session_id: The session ID of the connection.
Expand Down Expand Up @@ -438,6 +440,25 @@ def __init__(
elif "streamlit" in sys.modules:
kwargs["application"] = "streamlit"

if "insecure_mode" in kwargs:
warnings.warn(
"The 'insecure_mode' connection property is deprecated. "
"Please use 'disable_ocsp_checks' instead",
DeprecationWarning,
stacklevel=2,
)

if (
"disable_ocsp_checks" in kwargs
and kwargs["disable_ocsp_checks"] != kwargs["insecure_mode"]
):
logger.warning(
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
"Using the value of 'disable_ocsp_checks."
)
else:
self._disable_ocsp_checks = kwargs["insecure_mode"]

self.converter = None
self.query_context_cache: QueryContextCache | None = None
self.query_context_cache_size = 5
Expand Down Expand Up @@ -469,19 +490,23 @@ def __init__(
# check SNOW-1218851 for long term improvement plan to refactor ocsp code
atexit.register(self._close_at_exit)

# Deprecated
@property
def insecure_mode(self) -> bool:
return self._insecure_mode
return self._disable_ocsp_checks

@property
def disable_ocsp_checks(self) -> bool:
return self._disable_ocsp_checks

@property
def ocsp_fail_open(self) -> bool:
return self._ocsp_fail_open

def _ocsp_mode(self) -> OCSPMode:
"""OCSP mode. INSEC
URE, FAIL_OPEN or FAIL_CLOSED."""
if self.insecure_mode:
return OCSPMode.INSECURE
"""OCSP mode. DISABLE_OCSP_CHECKS, FAIL_OPEN or FAIL_CLOSED."""
if self.disable_ocsp_checks:
return OCSPMode.DISABLE_OCSP_CHECKS
elif self.ocsp_fail_open:
return OCSPMode.FAIL_OPEN
else:
Expand Down Expand Up @@ -1288,7 +1313,7 @@ def __config(self, **kwargs):
)

if self.ocsp_fail_open:
logger.info(
logger.debug(
"This connection is in OCSP Fail Open Mode. "
"TLS Certificates would be checked for validity "
"and revocation status. Any other Certificate "
Expand All @@ -1297,12 +1322,10 @@ def __config(self, **kwargs):
"connectivity."
)

if self.insecure_mode:
logger.info(
"THIS CONNECTION IS IN INSECURE MODE. IT "
"MEANS THE CERTIFICATE WILL BE VALIDATED BUT THE "
"CERTIFICATE REVOCATION STATUS WILL NOT BE "
"CHECKED."
if self.disable_ocsp_checks:
logger.debug(
"This connection runs with disabled OCSP checks. "
"Revocation status of the certificate will not be checked against OCSP Responder."
)

def cmd_query(
Expand Down
4 changes: 3 additions & 1 deletion src/snowflake/connector/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -354,12 +354,14 @@ class OCSPMode(Enum):
FAIL_OPEN: A response indicating a revoked certificate results in a failed connection. A response with any
other certificate errors or statuses allows the connection to occur, but denotes the message in the logs
at the WARNING level with the relevant details in JSON format.
INSECURE: The connection will occur anyway.
INSECURE (deprecated): The connection will occur anyway.
DISABLE_OCSP_CHECKS: The OCSP check will not happen. If the certificate is valid then connection will occur.
"""

FAIL_CLOSED = "FAIL_CLOSED"
FAIL_OPEN = "FAIL_OPEN"
INSECURE = "INSECURE"
DISABLE_OCSP_CHECKS = "DISABLE_OCSP_CHECKS"


@unique
Expand Down
27 changes: 11 additions & 16 deletions src/snowflake/connector/ocsp_snowflake.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ def __init__(self) -> None:
self.cache_enabled = False
self.cache_hit = False
self.fail_open = False
self.insecure_mode = False
self.disable_ocsp_checks = False

def set_event_sub_type(self, event_sub_type: str) -> None:
"""
Expand Down Expand Up @@ -380,8 +380,8 @@ def set_cache_hit(self, cache_hit) -> None:
def set_fail_open(self, fail_open) -> None:
self.fail_open = fail_open

def set_insecure_mode(self, insecure_mode) -> None:
self.insecure_mode = insecure_mode
def set_disable_ocsp_checks(self, disable_ocsp_checks) -> None:
self.disable_ocsp_checks = disable_ocsp_checks

def generate_telemetry_data(
self, event_type: str, urgent: bool = False
Expand All @@ -396,7 +396,7 @@ def generate_telemetry_data(
TelemetryField.KEY_OOB_OCSP_REQUEST_BASE64.value: self.ocsp_req,
TelemetryField.KEY_OOB_OCSP_RESPONDER_URL.value: self.ocsp_url,
TelemetryField.KEY_OOB_ERROR_MESSAGE.value: self.error_msg,
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.insecure_mode,
TelemetryField.KEY_OOB_INSECURE_MODE.value: self.disable_ocsp_checks,
TelemetryField.KEY_OOB_FAIL_OPEN.value: self.fail_open,
TelemetryField.KEY_OOB_CACHE_ENABLED.value: self.cache_enabled,
TelemetryField.KEY_OOB_CACHE_HIT.value: self.cache_hit,
Expand Down Expand Up @@ -1091,7 +1091,7 @@ def validate_certfile(self, cert_filename, no_exception: bool = False):
cert_map = {}
telemetry_data = OCSPTelemetryData()
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
telemetry_data.set_insecure_mode(False)
telemetry_data.set_disable_ocsp_checks(False)
telemetry_data.set_sfc_peer_host(cert_filename)
telemetry_data.set_fail_open(self.is_enabled_fail_open())
try:
Expand Down Expand Up @@ -1137,7 +1137,7 @@ def validate(

telemetry_data = OCSPTelemetryData()
telemetry_data.set_cache_enabled(self.OCSP_CACHE_SERVER.CACHE_SERVER_ENABLED)
telemetry_data.set_insecure_mode(False)
telemetry_data.set_disable_ocsp_checks(False)
telemetry_data.set_sfc_peer_host(hostname)
telemetry_data.set_fail_open(self.is_enabled_fail_open())

Expand Down Expand Up @@ -1224,15 +1224,10 @@ def is_enabled_fail_open(self) -> bool:
return self.FAIL_OPEN

@staticmethod
def print_fail_open_warning(ocsp_log) -> None:
static_warning = (
"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:"
)
ocsp_warning = f"{static_warning} \n {ocsp_log}"
logger.warning(ocsp_warning)
def print_fail_open_debug(ocsp_log) -> None:
static_debug = "OCSP responder didn't respond correctly. Assuming certificate is not revoked. Details: "
ocsp_debug = f"{static_debug} \n {ocsp_log}"
logger.debug(ocsp_debug)

def validate_by_direct_connection(
self,
Expand Down Expand Up @@ -1320,7 +1315,7 @@ def verify_fail_open(self, ex_obj, telemetry_data):
)
return ex_obj
else:
SnowflakeOCSP.print_fail_open_warning(
SnowflakeOCSP.print_fail_open_debug(
telemetry_data.generate_telemetry_data("RevocationCheckFailure")
)
return None
Expand Down
10 changes: 4 additions & 6 deletions src/snowflake/connector/ssl_wrap_socket.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ def ssl_wrap_socket_with_ocsp(*args: Any, **kwargs: Any) -> WrappedSocket:
FEATURE_OCSP_MODE.name,
FEATURE_OCSP_RESPONSE_CACHE_FILE_NAME,
)
if FEATURE_OCSP_MODE != OCSPMode.INSECURE:
if FEATURE_OCSP_MODE != OCSPMode.DISABLE_OCSP_CHECKS:
from .ocsp_asn1crypto import SnowflakeOCSPAsn1Crypto as SFOCSP

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

return ret
Expand Down
73 changes: 70 additions & 3 deletions test/integ/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ def test_bogus(db_parameters):
host=db_parameters["host"],
port=db_parameters["port"],
login_timeout=5,
insecure_mode=True,
disable_ocsp_checks=True,
)

with pytest.raises(DatabaseError):
Expand Down Expand Up @@ -1357,19 +1357,86 @@ def test_server_session_keep_alive(conn_cnx):


@pytest.mark.skipolddriver
def test_ocsp_mode_insecure(conn_cnx, is_public_test, caplog):
def test_ocsp_mode_disable_ocsp_checks(conn_cnx, is_public_test, caplog):
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
with conn_cnx(disable_ocsp_checks=True) as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
assert "This connection does not perform OCSP checks." in caplog.text
caplog.clear()

with conn_cnx() as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
if is_public_test:
assert "snowflake.connector.ocsp_snowflake" in caplog.text
assert "This connection does not perform OCSP checks." not in caplog.text
else:
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
assert "This connection does not perform OCSP checks." not in caplog.text


@pytest.mark.skipolddriver
def test_ocsp_mode_insecure_mode(conn_cnx, is_public_test, caplog):
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
with conn_cnx(insecure_mode=True) as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
assert "The 'insecure_mode' connection property is deprecated." in caplog.text
assert "This connection does not perform OCSP checks." in caplog.text


@pytest.mark.skipolddriver
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_match(
conn_cnx, is_public_test, caplog
):
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
with conn_cnx(
insecure_mode=True, disable_ocsp_checks=True
) as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
assert "The 'insecure_mode' connection property is deprecated." in caplog.text
assert (
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
"Using the value of 'disable_ocsp_checks."
) not in caplog.text
assert "This connection does not perform OCSP checks." in caplog.text


@pytest.mark.skipolddriver
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_disabled(
conn_cnx, is_public_test, caplog
):
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
with conn_cnx(
insecure_mode=False, disable_ocsp_checks=True
) as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
assert "snowflake.connector.ocsp_snowflake" not in caplog.text
assert "The 'insecure_mode' connection property is deprecated." in caplog.text
assert (
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
"Using the value of 'disable_ocsp_checks."
) in caplog.text
assert "This connection does not perform OCSP checks." in caplog.text


@pytest.mark.skipolddriver
def test_ocsp_mode_insecure_mode_and_disable_ocsp_checks_mismatch_ocsp_enabled(
conn_cnx, is_public_test, caplog
):
caplog.set_level(logging.DEBUG, "snowflake.connector.ocsp_snowflake")
with conn_cnx(
insecure_mode=True, disable_ocsp_checks=False
) as conn, conn.cursor() as cur:
assert cur.execute("select 1").fetchall() == [(1,)]
assert "snowflake.connector.ocsp_snowflake" in caplog.text
assert "The 'insecure_mode' connection property is deprecated." in caplog.text
assert (
"The values for 'disable_ocsp_checks' and 'insecure_mode' differ. "
"Using the value of 'disable_ocsp_checks."
) in caplog.text
assert "This connection does not perform OCSP checks." not in caplog.text


@pytest.mark.skipolddriver
Expand Down
Loading