Skip to content

Commit 34fc0e2

Browse files
authored
Pass CA and client TLS certificates to build subprocesses (#13063)
The _PIP_STANDALONE_CERT environment variable hack is no longer required as pip doesn't run a zip archive of itself to provision build dependencies these days (which due to a CPython bug would leave behind temporary certifi files). Some people do depend on this private envvar in the wild, so the removal has been called out in the news entry.
1 parent 8936fee commit 34fc0e2

File tree

6 files changed

+53
-30
lines changed

6 files changed

+53
-30
lines changed

news/5502.bugfix.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Configured TLS server and client certificates are now used while installing build dependencies.
2+
Consequently, the private ``_PIP_STANDALONE_CERT`` environment variable is no longer used.

src/pip/_internal/build_env.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,8 @@ def _install_requirements(
246246
# target from config file or env var should be ignored
247247
"--target",
248248
"",
249+
"--cert",
250+
finder.custom_cert or where(),
249251
]
250252
if logger.getEffectiveLevel() <= logging.DEBUG:
251253
args.append("-vv")
@@ -272,19 +274,19 @@ def _install_requirements(
272274

273275
for host in finder.trusted_hosts:
274276
args.extend(["--trusted-host", host])
277+
if finder.client_cert:
278+
args.extend(["--client-cert", finder.client_cert])
275279
if finder.allow_all_prereleases:
276280
args.append("--pre")
277281
if finder.prefer_binary:
278282
args.append("--prefer-binary")
279283
args.append("--")
280284
args.extend(requirements)
281-
extra_environ = {"_PIP_STANDALONE_CERT": where()}
282285
with open_spinner(f"Installing {kind}") as spinner:
283286
call_subprocess(
284287
args,
285288
command_desc=f"pip subprocess to install {kind}",
286289
spinner=spinner,
287-
extra_environ=extra_environ,
288290
)
289291

290292

src/pip/_internal/index/package_finder.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,20 @@ def trusted_hosts(self) -> Iterable[str]:
666666
for host_port in self._link_collector.session.pip_trusted_origins:
667667
yield build_netloc(*host_port)
668668

669+
@property
670+
def custom_cert(self) -> Optional[str]:
671+
# session.verify is either a boolean (use default bundle/no SSL
672+
# verification) or a string path to a custom CA bundle to use. We only
673+
# care about the latter.
674+
verify = self._link_collector.session.verify
675+
return verify if isinstance(verify, str) else None
676+
677+
@property
678+
def client_cert(self) -> Optional[str]:
679+
cert = self._link_collector.session.cert
680+
assert not isinstance(cert, tuple), "pip only supports PEM client certs"
681+
return cert
682+
669683
@property
670684
def allow_all_prereleases(self) -> bool:
671685
return self._candidate_prefs.allow_all_prereleases

src/pip/_vendor/requests/certs.py

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,7 @@
1111
environment, you can change the definition of where() to return a separately
1212
packaged CA bundle.
1313
"""
14-
15-
import os
16-
17-
if "_PIP_STANDALONE_CERT" not in os.environ:
18-
from pip._vendor.certifi import where
19-
else:
20-
def where():
21-
return os.environ["_PIP_STANDALONE_CERT"]
14+
from pip._vendor.certifi import where
2215

2316
if __name__ == "__main__":
2417
print(where())

tests/functional/test_install.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2361,6 +2361,38 @@ def test_install_sends_client_cert(
23612361
assert environ["SSL_CLIENT_CERT"]
23622362

23632363

2364+
def test_install_sends_certs_for_pep518_deps(
2365+
script: PipTestEnvironment,
2366+
cert_factory: CertFactory,
2367+
data: TestData,
2368+
common_wheels: Path,
2369+
) -> None:
2370+
cert_path = cert_factory()
2371+
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
2372+
ctx.load_cert_chain(cert_path, cert_path)
2373+
ctx.load_verify_locations(cafile=cert_path)
2374+
ctx.verify_mode = ssl.CERT_REQUIRED
2375+
2376+
setuptools_pkg = next(common_wheels.glob("setuptools*")).name
2377+
server = make_mock_server(ssl_context=ctx)
2378+
server.mock.side_effect = [
2379+
package_page({setuptools_pkg: f"/files/{setuptools_pkg}"}),
2380+
file_response(common_wheels / setuptools_pkg),
2381+
]
2382+
url = f"https://{server.host}:{server.port}/simple"
2383+
2384+
args = ["install", str(data.packages / "pep517_setup_and_pyproject")]
2385+
args.extend(["--index-url", url])
2386+
args.extend(["--cert", cert_path, "--client-cert", cert_path])
2387+
2388+
with server_running(server):
2389+
script.pip(*args)
2390+
2391+
for call_args in server.mock.call_args_list:
2392+
environ, _ = call_args.args
2393+
assert environ.get("SSL_CLIENT_CERT", "")
2394+
2395+
23642396
def test_install_skip_work_dir_pkg(script: PipTestEnvironment, data: TestData) -> None:
23652397
"""
23662398
Test that install of a package in working directory

tools/vendoring/patches/requests.patch

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,26 +84,6 @@ index 8fbcd656..094e2046 100644
8484

8585
try:
8686
from urllib3.contrib import pyopenssl
87-
diff --git a/src/pip/_vendor/requests/certs.py b/src/pip/_vendor/requests/certs.py
88-
index be422c3e..3daf06f6 100644
89-
--- a/src/pip/_vendor/requests/certs.py
90-
+++ b/src/pip/_vendor/requests/certs.py
91-
@@ -11,7 +11,14 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed
92-
environment, you can change the definition of where() to return a separately
93-
packaged CA bundle.
94-
"""
95-
-from certifi import where
96-
+
97-
+import os
98-
+
99-
+if "_PIP_STANDALONE_CERT" not in os.environ:
100-
+ from certifi import where
101-
+else:
102-
+ def where():
103-
+ return os.environ["_PIP_STANDALONE_CERT"]
104-
105-
if __name__ == "__main__":
106-
print(where())
10787
diff --git a/src/pip/_vendor/requests/__init__.py b/src/pip/_vendor/requests/__init__.py
10888
index 9d4e72c60..04230fc8d 100644
10989
--- a/src/pip/_vendor/requests/__init__.py

0 commit comments

Comments
 (0)