|
5 | 5 | :caption: Contents |
6 | 6 | ``` |
7 | 7 |
|
8 | | -Verify certificates using OS trust stores. Supports macOS, Windows, and Linux (with OpenSSL). |
| 8 | +Verify certificates using OS trust stores. This is useful when your system contains |
| 9 | +custom certificate authorities such as when using a corporate proxy or using test certificates. |
| 10 | +Supports macOS, Windows, and Linux (with OpenSSL). |
9 | 11 |
|
10 | 12 | ```{warning} |
11 | 13 | This project should be considered experimental so shouldn't be used in production. |
12 | 14 | ``` |
13 | 15 |
|
14 | | -## Platforms |
| 16 | +## Installation |
15 | 17 |
|
16 | | -- Requires Python 3.10 or later |
17 | | -- Supports macOS 10.8+ via [Security framework](https://developer.apple.com/documentation/security) |
18 | | -- Supports Windows via [CryptoAPI](https://docs.microsoft.com/en-us/windows/win32/seccrypto/cryptography-functions#certificate-verification-functions) |
19 | | -- Supports Linux via OpenSSL |
| 18 | +Truststore can be installed from [PyPI](https://pypi.org/project/truststore) with pip: |
20 | 19 |
|
21 | | -## Usage |
| 20 | +```{code-block} shell |
| 21 | +$ python -m pip install truststore |
| 22 | +``` |
22 | 23 |
|
23 | | -The `truststore` module has a single API: `truststore.SSLContext` |
| 24 | +Truststore **requires Python 3.10 or later** and supports the following platforms: |
| 25 | +- macOS 10.8+ via [Security framework](https://developer.apple.com/documentation/security) |
| 26 | +- Windows via [CryptoAPI](https://docs.microsoft.com/en-us/windows/win32/seccrypto/cryptography-functions#certificate-verification-functions) |
| 27 | +- Linux via OpenSSL |
24 | 28 |
|
25 | | -```{code-block} python |
26 | | - import truststore |
| 29 | +## User Guide |
| 30 | + |
| 31 | +You can inject `truststore` into the standard library `ssl` module so the functionality is used |
| 32 | +by every library by default. To do so use the `truststore.inject_into_ssl()` function. |
| 33 | + |
| 34 | +The call to `truststore.inject_into_ssl()` should be called as early as possible in |
| 35 | +your program as modules that have already imported `ssl.SSLContext` won't be affected. |
| 36 | + |
| 37 | +```python |
| 38 | +import truststore |
| 39 | +truststore.inject_into_ssl() |
| 40 | + |
| 41 | +# Automatically works with urllib3, requests, aiohttp, and more: |
| 42 | +import urllib3 |
| 43 | +http = urllib3.PoolManager() |
| 44 | +resp = http.request("GET", "https://example.com") |
| 45 | + |
| 46 | +import aiohttp |
| 47 | +http = aiohttp.ClientSession() |
| 48 | +resp = await http.request("GET", "https://example.com") |
| 49 | + |
| 50 | +import requests |
| 51 | +resp = requests.get("https://example.com") |
| 52 | +``` |
| 53 | + |
| 54 | +If you'd like finer-grained control you can create your own `truststore.SSLContext` instance |
| 55 | +and use it anywhere you'd use an `ssl.SSLContext`: |
| 56 | + |
| 57 | +```python |
| 58 | +import ssl |
| 59 | +import truststore |
| 60 | + |
| 61 | +ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
27 | 62 |
|
28 | | - ctx = truststore.SSLContext() |
| 63 | +import urllib3 |
| 64 | +http = urllib3.PoolManager(ssl_context=ctx) |
| 65 | +resp = http.request("GET", "https://example.com") |
29 | 66 | ``` |
30 | 67 |
|
31 | 68 | ### Using truststore with pip |
32 | 69 |
|
33 | | -Pip v22.2 includes experimental support for verifying certificates with system trust stores using `truststore`. To enable the feature, use the flag `--use-feature=truststore` when installing a package like so: |
| 70 | +[Pip v22.2](https://discuss.python.org/t/announcement-pip-22-2-release/17543) includes experimental support for verifying certificates with system trust stores using `truststore`. To enable the feature, use the flag `--use-feature=truststore` when installing a package like so: |
34 | 71 |
|
35 | 72 | ```{code-block} bash |
36 | | - # Install Django using system trust stores |
37 | | - $ python -m pip install --use-feature=truststore Django |
| 73 | +# Install Django using system trust stores |
| 74 | +$ python -m pip install --use-feature=truststore Django |
38 | 75 | ``` |
39 | 76 |
|
40 | 77 | This requires `truststore` to be installed in the same environment as the one running pip and to be running Python 3.10 or later. For more information you can [read the pip documentation about the feature](https://pip.pypa.io/en/stable/user_guide/#using-system-trust-stores-for-verifying-https). |
41 | 78 |
|
42 | 79 | ### Using truststore with urllib3 |
43 | 80 |
|
44 | | -This `SSLContext` works the same as an {py:class}`ssl.SSLContext`. |
45 | | -You can use it anywhere you would use an {py:class}`ssl.SSLContext` and |
46 | | -system trust stores are automatically used to verify peer certificates: |
| 81 | +```{code-block} python |
| 82 | +import urllib3 |
| 83 | +import truststore |
| 84 | +
|
| 85 | +truststore.inject_into_ssl() |
| 86 | +
|
| 87 | +http = urllib3.PoolManager() |
| 88 | +resp = http.request("GET", "https://example.com") |
| 89 | +``` |
| 90 | + |
| 91 | +If you'd like to use the `truststore.SSLContext` directly you can pass |
| 92 | +the instance via the `ssl_context` parameter: |
47 | 93 |
|
48 | 94 | ```{code-block} python |
49 | | - import ssl |
50 | | - import urllib3 |
51 | | - import truststore |
| 95 | +import ssl |
| 96 | +import urllib3 |
| 97 | +import truststore |
52 | 98 |
|
53 | | - ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 99 | +ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
54 | 100 |
|
55 | | - http = urllib3.PoolManager(ssl_context=ctx) |
56 | | - resp = http.request("GET", "https://example.com") |
| 101 | +http = urllib3.PoolManager(ssl_context=ctx) |
| 102 | +resp = http.request("GET", "https://example.com") |
57 | 103 | ``` |
58 | 104 |
|
59 | 105 | ### Using truststore with aiohttp |
60 | 106 |
|
61 | 107 | Truststore supports wrapping either {py:class}`socket.socket` or {py:class}`ssl.MemoryBIO` which means both synchronous and asynchronous I/O can be used: |
62 | 108 |
|
63 | 109 | ```{code-block} python |
64 | | - import ssl |
65 | | - import aiohttp |
66 | | - import truststore |
| 110 | +import aiohttp |
| 111 | +import truststore |
67 | 112 |
|
68 | | - ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 113 | +truststore.inject_into_ssl() |
69 | 114 |
|
70 | | - http = aiohttp.ClientSession(ssl=ctx) |
71 | | - resp = await http.request("GET", "https://example.com") |
| 115 | +http = aiohttp.ClientSession() |
| 116 | +resp = await http.request("GET", "https://example.com") |
72 | 117 | ``` |
73 | 118 |
|
74 | | -### Using truststore with Requests |
75 | | - |
76 | | -Requests doesn't support passing an {py:class}`ssl.SSLContext` object to a `requests.Session` object directly so there's an additional class you need to inject the `truststore.SSLContext` instance to the lower-level {py:class}`urllib3.PoolManager` instance: |
| 119 | +If you'd like to use the `truststore.SSLContext` directly you can pass |
| 120 | +the instance via the `ssl` parameter: |
77 | 121 |
|
78 | 122 | ```{code-block} python |
79 | | - import ssl |
80 | | - import requests |
81 | | - import requests.adapters |
82 | | - import truststore |
| 123 | +import ssl |
| 124 | +import aiohttp |
| 125 | +import truststore |
| 126 | +
|
| 127 | +ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 128 | +
|
| 129 | +http = aiohttp.ClientSession(ssl=ctx) |
| 130 | +resp = await http.request("GET", "https://example.com") |
| 131 | +``` |
83 | 132 |
|
84 | | - class SSLContextAdapter(requests.adapters.HTTPAdapter): |
85 | | - def __init__(self, *, ssl_context=None, **kwargs): |
86 | | - self._ssl_context = ssl_context |
87 | | - super().__init__(**kwargs) |
| 133 | +### Using truststore with Requests |
88 | 134 |
|
89 | | - def init_poolmanager(self, *args, **kwargs): |
90 | | - if self._ssl_context is not None: |
91 | | - kwargs.setdefault("ssl_context", self._ssl_context) |
92 | | - return super().init_poolmanager(*args, **kwargs) |
| 135 | +Just like with `urllib3` using `truststore.inject_into_ssl()` is the easiest method for using Truststore with Requests: |
93 | 136 |
|
94 | | - ctx = truststore.SSLContext(ssl.PROTOCOL_TLS_CLIENT) |
| 137 | +```{code-block} python |
| 138 | +import requests |
| 139 | +import truststore |
95 | 140 |
|
96 | | - http = requests.Session() |
97 | | - adapter = SSLContextAdapter(ssl_context=ctx) |
98 | | - http.mount("https://", adapter) |
| 141 | +truststore.inject_into_ssl() |
99 | 142 |
|
100 | | - resp = http.request("GET", "https://example.com") |
| 143 | +resp = requests.request("GET", "https://example.com") |
101 | 144 | ``` |
102 | 145 |
|
103 | 146 | ## Prior art |
104 | 147 |
|
| 148 | +* [pip v22.2 with support for `--use-feature=truststore`](https://discuss.python.org/t/announcement-pip-22-2-release/17543) |
105 | 149 | * [The future of trust stores in Python (PyCon US 2022 lightning talk)](https://youtu.be/1IiL31tUEVk?t=698) ([slides](https://speakerdeck.com/sethmlarson/the-future-of-trust-stores-in-python)) |
106 | 150 | * [Experimental APIs in Python 3.10 and the future of trust stores](https://sethmlarson.dev/blog/2021-11-27/experimental-python-3.10-apis-and-trust-stores) |
107 | 151 | * [PEP 543: A Unified TLS API for Python](https://www.python.org/dev/peps/pep-0543) |
|
0 commit comments