Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass CA and client TLS certificates to build subprocesses #13063

Merged
merged 2 commits into from
Dec 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/5502.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Configured TLS server and client certificates are now used while installing build dependencies.
Consequently, the private ``_PIP_STANDALONE_CERT`` environment variable is no longer used.
6 changes: 4 additions & 2 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,8 @@ def _install_requirements(
# target from config file or env var should be ignored
"--target",
"",
"--cert",
finder.custom_cert or where(),
]
if logger.getEffectiveLevel() <= logging.DEBUG:
args.append("-vv")
Expand All @@ -272,19 +274,19 @@ def _install_requirements(

for host in finder.trusted_hosts:
args.extend(["--trusted-host", host])
if finder.client_cert:
args.extend(["--client-cert", finder.client_cert])
if finder.allow_all_prereleases:
args.append("--pre")
if finder.prefer_binary:
args.append("--prefer-binary")
args.append("--")
args.extend(requirements)
extra_environ = {"_PIP_STANDALONE_CERT": where()}
with open_spinner(f"Installing {kind}") as spinner:
call_subprocess(
args,
command_desc=f"pip subprocess to install {kind}",
spinner=spinner,
extra_environ=extra_environ,
)


Expand Down
14 changes: 14 additions & 0 deletions src/pip/_internal/index/package_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -680,6 +680,20 @@ def trusted_hosts(self) -> Iterable[str]:
for host_port in self._link_collector.session.pip_trusted_origins:
yield build_netloc(*host_port)

@property
def custom_cert(self) -> Optional[str]:
# session.verify is either a boolean (use default bundle/no SSL
# verification) or a string path to a custom CA bundle to use. We only
# care about the latter.
verify = self._link_collector.session.verify
return verify if isinstance(verify, str) else None

@property
def client_cert(self) -> Optional[str]:
cert = self._link_collector.session.cert
assert not isinstance(cert, tuple), "pip only supports PEM client certs"
return cert

@property
def allow_all_prereleases(self) -> bool:
return self._candidate_prefs.allow_all_prereleases
Expand Down
9 changes: 1 addition & 8 deletions src/pip/_vendor/requests/certs.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,7 @@
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""

import os

if "_PIP_STANDALONE_CERT" not in os.environ:
from pip._vendor.certifi import where
else:
def where():
return os.environ["_PIP_STANDALONE_CERT"]
from pip._vendor.certifi import where

if __name__ == "__main__":
print(where())
32 changes: 32 additions & 0 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -2361,6 +2361,38 @@ def test_install_sends_client_cert(
assert environ["SSL_CLIENT_CERT"]


def test_install_sends_certs_for_pep518_deps(
script: PipTestEnvironment,
cert_factory: CertFactory,
data: TestData,
common_wheels: Path,
) -> None:
cert_path = cert_factory()
ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
ctx.load_cert_chain(cert_path, cert_path)
ctx.load_verify_locations(cafile=cert_path)
ctx.verify_mode = ssl.CERT_REQUIRED

setuptools_pkg = next(common_wheels.glob("setuptools*")).name
server = make_mock_server(ssl_context=ctx)
server.mock.side_effect = [
package_page({setuptools_pkg: f"/files/{setuptools_pkg}"}),
file_response(common_wheels / setuptools_pkg),
]
url = f"https://{server.host}:{server.port}/simple"

args = ["install", str(data.packages / "pep517_setup_and_pyproject")]
args.extend(["--index-url", url])
args.extend(["--cert", cert_path, "--client-cert", cert_path])

with server_running(server):
script.pip(*args)

for call_args in server.mock.call_args_list:
environ, _ = call_args.args
assert environ.get("SSL_CLIENT_CERT", "")


def test_install_skip_work_dir_pkg(script: PipTestEnvironment, data: TestData) -> None:
"""
Test that install of a package in working directory
Expand Down
20 changes: 0 additions & 20 deletions tools/vendoring/patches/requests.patch
Original file line number Diff line number Diff line change
Expand Up @@ -84,26 +84,6 @@ index 8fbcd656..094e2046 100644

try:
from urllib3.contrib import pyopenssl
diff --git a/src/pip/_vendor/requests/certs.py b/src/pip/_vendor/requests/certs.py
index be422c3e..3daf06f6 100644
--- a/src/pip/_vendor/requests/certs.py
+++ b/src/pip/_vendor/requests/certs.py
@@ -11,7 +11,14 @@ If you are packaging Requests, e.g., for a Linux distribution or a managed
environment, you can change the definition of where() to return a separately
packaged CA bundle.
"""
-from certifi import where
+
+import os
+
+if "_PIP_STANDALONE_CERT" not in os.environ:
+ from certifi import where
+else:
+ def where():
+ return os.environ["_PIP_STANDALONE_CERT"]

if __name__ == "__main__":
print(where())
diff --git a/src/pip/_vendor/requests/__init__.py b/src/pip/_vendor/requests/__init__.py
index 9d4e72c60..04230fc8d 100644
--- a/src/pip/_vendor/requests/__init__.py
Expand Down
Loading