Skip to content

Commit 519e052

Browse files
committed
ensure we only bundle certifi on linux builds
1 parent 44bbe63 commit 519e052

7 files changed

Lines changed: 53 additions & 27 deletions

File tree

cpython-unix/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ PYTHON_DEPENDS_$(1) := \
254254
$$(if$$(NEED_AUTOCONF),$$(OUTDIR)/autoconf-$$(AUTOCONF_VERSION)-$$(PACKAGE_SUFFIX).tar) \
255255
$$(if $$(NEED_BDB),$$(OUTDIR)/bdb-$$(BDB_VERSION)-$$(PACKAGE_SUFFIX).tar) \
256256
$$(if $$(NEED_BZIP2),$$(OUTDIR)/bzip2-$$(BZIP2_VERSION)-$$(PACKAGE_SUFFIX).tar) \
257-
$$(OUTDIR)/certifi-$$(CERTIFI_VERSION)-$$(PACKAGE_SUFFIX).tar \
257+
$$(if $$(NEED_CERTIFI),$$(OUTDIR)/certifi-$$(CERTIFI_VERSION)-$$(PACKAGE_SUFFIX).tar) \
258258
$$(if $$(NEED_EXPAT),$$(OUTDIR)/expat-$$(EXPAT_VERSION)-$$(PACKAGE_SUFFIX).tar) \
259259
$$(if $$(NEED_LIBEDIT),$$(OUTDIR)/libedit-$$(LIBEDIT_VERSION)-$$(PACKAGE_SUFFIX).tar) \
260260
$$(if $$(NEED_LIBFFI_3_3),$$(OUTDIR)/libffi-3.3-$$(LIBFFI_3.3_VERSION)-$$(PACKAGE_SUFFIX).tar) \

cpython-unix/build-cpython.sh

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,9 @@ fi
316316
# RHEL 8 (supported until 2029) and below, including Fedora 33 and below, do not
317317
# ship an /etc/ssl/cert.pem or a hashed /etc/ssl/cert/ directory. Patch to look at
318318
# /etc/pki/tls/cert.pem instead, if that file exists and /etc/ssl/cert.pem does not.
319-
patch -p1 -i ${ROOT}/patch-cpython-redhat-cert-file.patch
319+
if [[ "${TARGET_TRIPLE}" =~ linux ]]; then
320+
patch -p1 -i "${ROOT}/patch-cpython-redhat-cert-file.patch"
321+
fi
320322

321323
# Cherry-pick an upstream change in Python 3.15 to build _asyncio as
322324
# static (which we do anyway in our own fashion) and more importantly to
@@ -1361,10 +1363,12 @@ if [ -d "${TOOLS_PATH}/deps/usr/share/terminfo" ]; then
13611363
cp -av "${TOOLS_PATH}/deps/usr/share/terminfo" "${ROOT}/out/python/install/share/"
13621364
fi
13631365

1364-
# Copy a fallback CA bundle. Python will prefer the host trust store when one
1365-
# exists and use this only for minimal environments without root certificates.
1366-
mkdir -p "${ROOT}/out/python/install/etc/ssl"
1367-
cp -av "${TOOLS_PATH}/deps/share/certifi/cacert.pem" "${ROOT}/out/python/install/etc/ssl/cert.pem"
1366+
# Copy a Linux fallback CA bundle. Python will prefer the host trust store when
1367+
# one exists and use this only for minimal environments without root certificates.
1368+
if [[ "${TARGET_TRIPLE}" =~ linux ]]; then
1369+
mkdir -p "${ROOT}/out/python/install/etc/ssl"
1370+
cp -av "${TOOLS_PATH}/deps/share/certifi/cacert.pem" "${ROOT}/out/python/install/etc/ssl/cert.pem"
1371+
fi
13681372

13691373
# config.c defines _PyImport_Inittab and extern references to modules, which
13701374
# downstream consumers may want to strip. We bundle config.c and config.c.in so
@@ -1391,5 +1395,7 @@ find "${ROOT}/out/python/install/lib/pkgconfig" -name \*.pc -type f -exec \
13911395
sed "${sed_args[@]}" 's|^prefix=/install|prefix=${pcfiledir}/../..|' {} +
13921396

13931397
mkdir "${ROOT}/out/python/licenses"
1394-
cp "${ROOT}/LICENSE" "${ROOT}/out/python/licenses/"
1398+
if [[ "${TARGET_TRIPLE}" =~ linux ]]; then
1399+
cp "${ROOT}/LICENSE" "${ROOT}/out/python/licenses/"
1400+
fi
13951401
cp "${ROOT}"/LICENSE.*.txt "${ROOT}/out/python/licenses/"

cpython-unix/build.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,12 +775,14 @@ def build_cpython(
775775
python_archive,
776776
setuptools_archive,
777777
pip_archive,
778-
ROOT / "LICENSE",
779778
SUPPORT / "build-cpython.sh",
780779
SUPPORT / "run_tests-13.py",
781780
):
782781
build_env.copy_file(p)
783782

783+
if "-linux-" in target_triple:
784+
build_env.copy_file(ROOT / "LICENSE")
785+
784786
for f in sorted(os.listdir(ROOT)):
785787
if f.startswith("LICENSE.") and f.endswith(".txt"):
786788
build_env.copy_file(ROOT / f)

cpython-unix/patch-cpython-redhat-cert-file.patch

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,24 @@ index 42ebb8ed384..c15c0ec940f 100644
1313

1414
sslsocket_class = None # SSLSocket is assigned later.
1515
sslobject_class = None # SSLObject is assigned later.
16-
@@ -531,6 +534,34 @@ def load_default_certs(self, purpose=Purpose.SERVER_AUTH):
16+
@@ -531,6 +535,41 @@ def load_default_certs(self, purpose=Purpose.SERVER_AUTH):
1717
if sys.platform == "win32":
1818
for storename in self._windows_cert_stores:
1919
self._load_windows_store_certs(storename, purpose)
20-
+ else:
21-
+ def _cert_dir_has_entries(path):
20+
+ elif sys.platform == "linux":
21+
+ def _has_hashed_certs(path):
2222
+ if not os.path.isdir(path):
2323
+ return False
2424
+ try:
25-
+ return next(os.scandir(path), None) is not None
25+
+ for entry in os.scandir(path):
26+
+ name, dot, suffix = entry.name.partition(".")
27+
+ if dot and len(name) == 8 and suffix.isdigit():
28+
+ try:
29+
+ int(name, 16)
30+
+ except ValueError:
31+
+ continue
32+
+ return True
33+
+ return False
2634
+ except OSError:
2735
+ return False
2836
+
@@ -31,15 +39,14 @@ index 42ebb8ed384..c15c0ec940f 100644
3139
+ _def_paths[0] in os.environ or os.path.isfile(_def_paths[1])
3240
+ )
3341
+ _has_cert_dir = (
34-
+ _def_paths[2] in os.environ or _cert_dir_has_entries(_def_paths[3])
42+
+ _def_paths[2] in os.environ or _has_hashed_certs(_def_paths[3])
3543
+ )
36-
+ if sys.platform == "linux":
37-
+ if not _has_cert_file and os.path.isfile(self._FALLBACK_CERT_FILE):
38-
+ self.load_verify_locations(cafile=self._FALLBACK_CERT_FILE)
39-
+ _has_cert_file = True
40-
+ if not _has_cert_dir and _cert_dir_has_entries(self._FALLBACK_CERT_DIR):
41-
+ self.load_verify_locations(capath=self._FALLBACK_CERT_DIR)
42-
+ _has_cert_dir = True
44+
+ if not _has_cert_file and os.path.isfile(self._FALLBACK_CERT_FILE):
45+
+ self.load_verify_locations(cafile=self._FALLBACK_CERT_FILE)
46+
+ _has_cert_file = True
47+
+ if not _has_cert_dir and _has_hashed_certs(self._FALLBACK_CERT_DIR):
48+
+ self.load_verify_locations(capath=self._FALLBACK_CERT_DIR)
49+
+ _has_cert_dir = True
4350
+ if (self._STANDALONE_CERT_FALLBACK_ENV not in os.environ and
4451
+ not _has_cert_file and
4552
+ not _has_cert_dir and

docs/quirks.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ ncurses/libedit/readline are loaded.
7474
Fallback CA Certificate Bundle
7575
==============================
7676

77-
On Unix platforms, Python prefers the trust store provided by the host
78-
operating system. When none of the usual host certificate locations exist,
79-
these distributions fall back to a bundled CA certificate file at
77+
On Linux, Python prefers the trust store provided by the host operating
78+
system. When none of the usual host certificate locations exist, these
79+
distributions fall back to a bundled CA certificate file at
8080
``etc/ssl/cert.pem`` inside the Python installation.
8181

8282
The bundled file is a safety net for minimal environments such as scratch-like

pythonbuild/disttests/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ def test_ssl(self):
217217

218218
ssl.create_default_context()
219219

220-
if os.name != "nt":
220+
if sys.platform == "linux":
221221
bundled_ca_file = os.path.join(INSTALL_ROOT, "etc", "ssl", "cert.pem")
222222
self.assertTrue(os.path.isfile(bundled_ca_file))
223223

pythonbuild/utils.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,15 +100,26 @@ def target_needs(yaml_path: pathlib.Path, target: str):
100100
settings = get_targets(yaml_path)[target]
101101

102102
needs = set(settings["needs"])
103-
# Every Unix distribution ships a fallback CA bundle.
104-
needs.add("certifi")
103+
# Linux distributions ship a fallback CA bundle for minimal images.
104+
if "-linux-" in target:
105+
needs.add("certifi")
105106

106107
# Ship libedit linked readline extension to avoid a GPL dependency.
107108
needs.discard("readline")
108109

109110
return needs
110111

111112

113+
def target_makefile_needs(target: str, settings: dict) -> set[str]:
114+
"""Obtain dependency names that should be emitted into target Makefiles."""
115+
needs = set(settings.get("needs", []))
116+
117+
if "-linux-" in target:
118+
needs.add("certifi")
119+
120+
return needs
121+
122+
112123
def release_tag_from_git():
113124
return (
114125
subprocess.check_output(
@@ -192,7 +203,7 @@ def write_triples_makefiles(
192203
makefile_path = dest_dir / ("Makefile.%s.%s" % (host_platform, triple))
193204

194205
lines = []
195-
for need in settings.get("needs", []):
206+
for need in sorted(target_makefile_needs(triple, settings)):
196207
lines.append(
197208
"NEED_%s := 1\n" % need.upper().replace("-", "_").replace(".", "_")
198209
)

0 commit comments

Comments
 (0)