Skip to content

Commit e4ddb31

Browse files
committed
Fix loading of certificate bundle on Windows
1 parent 042a706 commit e4ddb31

File tree

4 files changed

+41
-7
lines changed

4 files changed

+41
-7
lines changed

newrelic/common/agent_http.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import os
16+
import ssl
1617
import sys
1718
import time
1819
import zlib
@@ -258,11 +259,28 @@ def __init__(
258259
if not ca_bundle_path:
259260
verify_path = get_default_verify_paths()
260261

261-
# If there is no resolved cafile, assume the bundled certs are
262-
# required and report this condition as a supportability metric.
263262
if not verify_path.cafile and not verify_path.capath:
264-
ca_bundle_path = certs.where()
265-
internal_metric("Supportability/Python/Certificate/BundleRequired", 1)
263+
if sys.platform != "win32":
264+
# If there is no resolved cafile on POSIX platforms, assume the bundled certs
265+
# are required and report this condition as a supportability metric.
266+
ca_bundle_path = certs.where()
267+
internal_metric("Supportability/Python/Certificate/BundleRequired", 1)
268+
else:
269+
# If there is no resolved cafile on Windows, attempt to load the default certs.
270+
try:
271+
_context = ssl.SSLContext()
272+
_context.load_default_certs()
273+
system_certs = _context.get_ca_certs()
274+
except Exception:
275+
system_certs = None
276+
277+
# If we still can't find any certs after loading the default ones,
278+
# then assume the bundled certs are required. If we do find them,
279+
# we don't have to do anything. We let urllib3 handle loading the
280+
# default certs from Windows.
281+
if not system_certs:
282+
ca_bundle_path = certs.where()
283+
internal_metric("Supportability/Python/Certificate/BundleRequired", 1)
266284

267285
if ca_bundle_path:
268286
if Path(ca_bundle_path).is_dir():

tests/agent_unittests/test_agent_protocol.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import logging
1616
import os
1717
import ssl
18+
import sys
1819
import tempfile
1920
from pathlib import Path
2021

@@ -540,7 +541,12 @@ class DefaultVerifyPaths:
540541
def __init__(self, *args, **kwargs):
541542
pass
542543

543-
monkeypatch.setattr(ssl, "DefaultVerifyPaths", DefaultVerifyPaths)
544+
def get_ca_certs(purpose=None):
545+
return []
546+
547+
monkeypatch.setattr(ssl, "DefaultVerifyPaths", DefaultVerifyPaths) # Bypass OpenSSL default certs
548+
if sys.platform == "win32":
549+
monkeypatch.setattr(ssl.SSLContext, "get_ca_certs", get_ca_certs) # Bypass Windows default certs
544550

545551
settings = finalize_application_settings({"ca_bundle_path": ca_bundle_path})
546552
protocol = AgentProtocol(settings)

tests/agent_unittests/test_http_client.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import base64
1616
import json
1717
import ssl
18+
import sys
1819
import zlib
1920
from http.server import BaseHTTPRequestHandler, HTTPServer
2021
from io import StringIO
@@ -319,14 +320,17 @@ def test_cert_path(server):
319320
status, data = client.send_request()
320321

321322

323+
@pytest.mark.skipif(sys.platform == "win32", reason="This test is not valid for Windows")
322324
@pytest.mark.parametrize("system_certs_available", (True, False))
323325
def test_default_cert_path(monkeypatch, system_certs_available):
324326
if system_certs_available:
325327
cert_file = "foo"
326328
ca_path = "/usr/certs"
329+
system_certs = [{"issuer": "Test CA"}] # Poorly faked certs
327330
else:
328331
cert_file = None
329332
ca_path = None
333+
system_certs = []
330334

331335
class DefaultVerifyPaths:
332336
cafile = cert_file
@@ -335,7 +339,13 @@ class DefaultVerifyPaths:
335339
def __init__(self, *args, **kwargs):
336340
pass
337341

338-
monkeypatch.setattr(ssl, "DefaultVerifyPaths", DefaultVerifyPaths)
342+
def get_ca_certs(purpose=None):
343+
return system_certs
344+
345+
monkeypatch.setattr(ssl, "DefaultVerifyPaths", DefaultVerifyPaths) # Bypass OpenSSL default certs
346+
if sys.platform == "win32":
347+
monkeypatch.setattr(ssl.SSLContext, "get_ca_certs", get_ca_certs) # Bypass Windows default certs
348+
339349
internal_metrics = CustomMetrics()
340350
with InternalTraceContext(internal_metrics):
341351
client = HttpClient("localhost", ca_bundle_path=None)

tests/cross_agent/conftest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@
3131
app_name="Python Agent Test (cross_agent_tests)", default_settings=_default_settings
3232
)
3333

34-
SKIP_ON_WINDOWS = pytest.mark.xfail(sys.platform == "win32", reason="This feature is not supported on Windows")
34+
SKIP_ON_WINDOWS = pytest.mark.skipif(sys.platform == "win32", reason="This feature is not supported on Windows")

0 commit comments

Comments
 (0)