Feature/support env var sslkeylogfile
MODIFY
CONTRIBUTORS.txt
CONTRIBUTORS.txt
@@ -300,5 +300,8 @@ In chronological order:
* Chris Olufson <tycarac@gmail.com>
* Fix for connection not being released on HTTP redirect and response not preloaded
+* [Bastiaan Bakker] <https://github.com/bastiaanb>
+ * Support for logging session keys via environment variable ``SSLKEYLOGFILE`` (Python 3.8+)
+
* [Your name or handle] <[email or website]>
* [Brief summary of your changes]

MODIFY
docs/advanced-usage.rst
docs/advanced-usage.rst
@@ -303,3 +303,16 @@ Here's an example using brotli encoding via the ``Accept-Encoding`` header::
>>> from urllib3 import PoolManager
>>> http = PoolManager()
>>> http.request('GET', 'https://www.google.com/', headers={'Accept-Encoding': 'br'})
+
+Decrypting captured TLS sessions with Wireshark
+-----------------------------------------------
+Python 3.8 and higher support logging of TLS pre-master secrets.
+With these secrets tools like `Wireshark <https://wireshark.org>`_ can decrypt captured
+network traffic.
+
+To enable this simply define environment variable `SSLKEYLOGFILE`:
+
+ export SSLKEYLOGFILE=/path/to/keylogfile.txt
+
+Then configure the key logfile in `Wireshark <https://wireshark.org>`_, see
+`Wireshark TLS Decryption <https://wiki.wireshark.org/TLS#TLS_Decryption>`_ for instructions.

MODIFY
src/urllib3/util/ssl_.py
src/urllib3/util/ssl_.py
@@ -2,6 +2,7 @@ from __future__ import absolute_import
import errno
import warnings
import hmac
+import os
import sys
from binascii import hexlify, unhexlify
@@ -293,6 +294,12 @@ def create_urllib3_context(
# We do our own verification, including fingerprints and alternative
# hostnames. So disable it here
context.check_hostname = False
+
+ # Enable logging of TLS session keys via defacto standard environment variable
+ # 'SSLKEYLOGFILE', if the feature is available (Python 3.8+).
+ if hasattr(context, "keylog_filename"):
+ context.keylog_filename = os.environ.get("SSLKEYLOGFILE")
+
return context

MODIFY
test/with_dummyserver/test_https.py
test/with_dummyserver/test_https.py
@@ -698,6 +698,31 @@ class TestHTTPS(HTTPSDummyServerTestCase):
finally:
conn.close()
+ @pytest.mark.skipif(
+ not hasattr(ssl.SSLContext, "keylog_filename"),
+ reason="requires OpenSSL 1.1.1+",
+ )
+ @pytest.mark.skipif(sys.version_info < (3, 8), reason="requires python 3.8+")
+ @pytest.mark.skipif(
+ sys.platform == "win32",
+ reason="does not work reliably in Appveyor test enviroment for not yet known reasons",
+ )
+ def test_sslkeylogfile(self, tmpdir, monkeypatch):
+ keylog_file = tmpdir.join("keylogfile.txt")
+ monkeypatch.setenv("SSLKEYLOGFILE", str(keylog_file))
+ with HTTPSConnectionPool(
+ self.host, self.port, ca_certs=DEFAULT_CA
+ ) as https_pool:
+ r = https_pool.request("GET", "/")
+ assert r.status == 200, r.data
+ assert keylog_file.check(file=1), "keylogfile '%s' should exist" % str(
+ keylog_file
+ )
+ assert keylog_file.read().startswith("# TLS secrets log file"), (
+ "keylogfile '%s' should start with '# TLS secrets log file'"
+ % str(keylog_file)
+ )
+
@requiresTLSv1()
class TestHTTPS_TLSv1(TestHTTPS):

Feature/support env var sslkeylogfile (#1867)