From b477fbb9fca3e7d08297fcba40be29653055641e Mon Sep 17 00:00:00 2001 From: Julien Langlois Date: Wed, 5 Jun 2024 08:53:36 -0700 Subject: [PATCH] Custom revert of 3e72b44a to fix the SSLEOFError Could not "git revert" because the files have changed too much --- docs/reference.rst | 7 ++++ shotgun_api3/shotgun.py | 68 +++------------------------------------ tests/test_api.py | 71 ----------------------------------------- 3 files changed, 11 insertions(+), 135 deletions(-) diff --git a/docs/reference.rst b/docs/reference.rst index 8c9e6af6..71f9e44b 100644 --- a/docs/reference.rst +++ b/docs/reference.rst @@ -949,6 +949,13 @@ Stores the number of milliseconds to wait between request retries. By default, In the case that both this environment variable and the config's ``rpc_attempt_interval`` property are set, the value in ``rpc_attempt_interal`` will be used. + +SHOTGUN_DISABLE_SSL_VALIDATION +============================== + +TODO + + ************ Localization ************ diff --git a/shotgun_api3/shotgun.py b/shotgun_api3/shotgun.py index 288ef121..10bea3a8 100644 --- a/shotgun_api3/shotgun.py +++ b/shotgun_api3/shotgun.py @@ -112,6 +112,8 @@ def _is_mimetypes_broken(): have a self-signed internal certificate that isn't included in our certificate bundle, you may not require the added security provided by enforcing this. """ +if os.environ.get("SHOTGUN_DISABLE_SSL_VALIDATION", False): + NO_SSL_VALIDATION = True # ---------------------------------------------------------------------------- # Version @@ -367,12 +369,11 @@ def __init__(self): self.py_version = ".".join(str(x) for x in sys.version_info[:2]) - # extract the OpenSSL version if we can. The version is only available in Python 2.7 and - # only if we successfully imported ssl + # extract the OpenSSL version if we can. self.ssl_version = "unknown" try: self.ssl_version = ssl.OPENSSL_VERSION - except (AttributeError, NameError): + except AttributeError: pass def __str__(self): @@ -3374,18 +3375,6 @@ def _get_certs_file(cls, ca_certs): cert_file = os.path.join(cur_dir, "lib", "certifi", "cacert.pem") return cert_file - def _turn_off_ssl_validation(self): - """ - Turn off SSL certificate validation. - """ - global NO_SSL_VALIDATION - self.config.no_ssl_validation = True - NO_SSL_VALIDATION = True - # reset ssl-validation in user-agents - self._user_agents = ["ssl %s (no-validate)" % self.client_caps.ssl_version - if ua.startswith("ssl ") else ua - for ua in self._user_agents] - # Deprecated methods from old wrapper def schema(self, entity_type): """ @@ -3587,55 +3576,6 @@ def _make_call(self, verb, path, body, headers): attempt += 1 try: return self._http_request(verb, path, body, req_headers) - except ssl.SSLEOFError as e: - # SG-34910 - EOF occurred in violation of protocol (_ssl.c:2426) - # This issue seems to be related to proxy and keep alive. - # It looks like, sometimes, the proxy drops the connection on - # the TCP/TLS level despites the keep-alive. So we need to close - # the connection and make a new attempt. - LOG.debug("SSLEOFError: {}".format(e)) - self._close_connection() - if attempt == max_rpc_attempts: - LOG.debug("Request failed. Giving up after %d attempts." % attempt) - raise - # This is the exact same block as the "except Exception" bellow. - # We need to do it here because the next except will match it - # otherwise and will not re-attempt. - # When we drop support of Python 2 and we will probably drop the - # next except, we might want to remove this except too. - except ssl_error_classes as e: - # Test whether the exception is due to the fact that this is an older version of - # Python that cannot validate certificates encrypted with SHA-2. If it is, then - # fall back on disabling the certificate validation and try again - unless the - # SHOTGUN_FORCE_CERTIFICATE_VALIDATION environment variable has been set by the - # user. In that case we simply raise the exception. Any other exceptions simply - # get raised as well. - # - # For more info see: - # https://www.shotgridsoftware.com/blog/important-ssl-certificate-renewal-and-sha-2/ - # - # SHA-2 errors look like this: - # [Errno 1] _ssl.c:480: error:0D0C50A1:asn1 encoding routines:ASN1_item_verify: - # unknown message digest algorithm - # - # Any other exceptions simply get raised. - if "unknown message digest algorithm" not in str(e) or \ - "SHOTGUN_FORCE_CERTIFICATE_VALIDATION" in os.environ: - raise - - if self.config.no_ssl_validation is False: - LOG.warning("SSL Error: this Python installation is incompatible with " - "certificates signed with SHA-2. Disabling certificate validation. " - "For more information, see https://www.shotgridsoftware.com/blog/" - "important-ssl-certificate-renewal-and-sha-2/") - self._turn_off_ssl_validation() - # reload user agent to reflect that we have turned off ssl validation - req_headers["user-agent"] = "; ".join(self._user_agents) - - self._close_connection() - if attempt == max_rpc_attempts: - LOG.debug("Request failed. Giving up after %d attempts." % attempt) - raise except Exception: self._close_connection() if attempt == max_rpc_attempts: diff --git a/tests/test_api.py b/tests/test_api.py index 45a06a21..6f1ff6f3 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -1935,77 +1935,6 @@ def my_side_effect2(*args, **kwargs): finally: self.sg.config.rpc_attempt_interval = bak_rpc_attempt_interval - @patch('shotgun_api3.shotgun.Http.request') - def test_sha2_error(self, mock_request): - # Simulate the exception raised with SHA-2 errors - mock_request.side_effect = ShotgunSSLError( - "[Errno 1] _ssl.c:480: error:0D0C50A1:asn1 " - "encoding routines:ASN1_item_verify: unknown message digest " - "algorithm" - ) - - # save the original state - original_env_val = os.environ.pop("SHOTGUN_FORCE_CERTIFICATE_VALIDATION", None) - - # ensure we're starting with the right values - self.sg.reset_user_agent() - - # ensure the initial settings are correct. These will be different depending on whether - # the ssl module imported successfully or not. - if "ssl" in sys.modules: - self.assertFalse(self.sg.config.no_ssl_validation) - self.assertFalse(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertTrue("(validate)" in " ".join(self.sg._user_agents)) - self.assertFalse("(no-validate)" in " ".join(self.sg._user_agents)) - else: - self.assertTrue(self.sg.config.no_ssl_validation) - self.assertTrue(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(no-validate)" in " ".join(self.sg._user_agents)) - - try: - self.sg.info() - except ShotgunSSLError: - # ensure the api has reset the values in the correct fallback behavior - self.assertTrue(self.sg.config.no_ssl_validation) - self.assertTrue(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(no-validate)" in " ".join(self.sg._user_agents)) - - if original_env_val is not None: - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = original_env_val - - @patch('shotgun_api3.shotgun.Http.request') - def test_sha2_error_with_strict(self, mock_request): - # Simulate the exception raised with SHA-2 errors - mock_request.side_effect = ShotgunSSLError( - "[Errno 1] _ssl.c:480: error:0D0C50A1:asn1 " - "encoding routines:ASN1_item_verify: unknown message digest " - "algorithm" - ) - - # save the original state - original_env_val = os.environ.pop("SHOTGUN_FORCE_CERTIFICATE_VALIDATION", None) - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = "1" - - # ensure we're starting with the right values - self.sg.config.no_ssl_validation = False - shotgun_api3.shotgun.NO_SSL_VALIDATION = False - self.sg.reset_user_agent() - - try: - self.sg.info() - except ShotgunSSLError: - # ensure the api has NOT reset the values in the fallback behavior because we have - # set the env variable to force validation - self.assertFalse(self.sg.config.no_ssl_validation) - self.assertFalse(shotgun_api3.shotgun.NO_SSL_VALIDATION) - self.assertFalse("(no-validate)" in " ".join(self.sg._user_agents)) - self.assertTrue("(validate)" in " ".join(self.sg._user_agents)) - - if original_env_val is not None: - os.environ["SHOTGUN_FORCE_CERTIFICATE_VALIDATION"] = original_env_val - @patch.object(urllib.request.OpenerDirector, 'open') def test_sanitized_auth_params(self, mock_open): # Simulate the server blowing up and giving us a 500 error