Skip to content

Commit 4a43769

Browse files
authored
fix: Resolve ca-certificates installed in the local environment (#4101)
Signed-off-by: Julien Jerphanion <[email protected]>
1 parent 806e590 commit 4a43769

File tree

2 files changed

+89
-8
lines changed

2 files changed

+89
-8
lines changed

libmamba/src/download/downloader.cpp

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "mamba/core/invoke.hpp"
99
#include "mamba/core/thread_utils.hpp"
1010
#include "mamba/core/util.hpp"
11+
#include "mamba/core/util_os.hpp"
1112
#include "mamba/core/util_scope.hpp"
1213
#include "mamba/download/downloader.hpp"
1314
#include "mamba/util/build.hpp"
@@ -84,23 +85,60 @@ namespace mamba::download
8485
// from `conda-forge::ca-certificates` and the system CA certificates.
8586
else if (remote_fetch_params.ssl_verify == "<system>")
8687
{
87-
// Use the CA certificates from `conda-forge::ca-certificates` installed in the
88-
// root prefix or the system CA certificates if the certificate is not present.
89-
fs::u8path root_prefix = detail::get_root_prefix();
90-
fs::u8path env_prefix_conda_cert = root_prefix / "ssl" / "cacert.pem";
91-
92-
LOG_INFO << "Checking for CA certificates at the root prefix: "
88+
// See the location of the CA certificates as distributed by
89+
// `conda-forge::ca-certificates`:
90+
// https://github.com/conda-forge/ca-certificates-feedstock/blob/main/recipe/meta.yaml#L25-L29
91+
const fs::u8path prefix_relative_conda_cert_path = (util::on_win ? fs::u8path("Library") / "ssl" / "cacert.pem" : fs::u8path("ssl") / "cert.pem");
92+
93+
const fs::u8path executable_path = get_self_exe_path();
94+
95+
// Find the environment prefix using the path of the `mamba` or `micromamba`
96+
// executable (we cannot assume the existence of an environment variable or
97+
// etc.).
98+
//
99+
// `mamba` or `micromamba` is installed at:
100+
//
101+
// - `${PREFIX}/bin/{mamba,micromamba}` on Unix
102+
// - `${PREFIX}/Library/bin/{mamba,micromamba}.exe` on Windows
103+
//
104+
const fs::u8path env_prefix
105+
= (util::on_win ? executable_path.parent_path().parent_path().parent_path()
106+
: executable_path.parent_path().parent_path());
107+
108+
const fs::u8path env_prefix_conda_cert = env_prefix
109+
/ prefix_relative_conda_cert_path;
110+
111+
LOG_INFO << "Checking for CA certificates in the same prefix as the executable installation: "
93112
<< env_prefix_conda_cert;
94113

95114
if (fs::exists(env_prefix_conda_cert))
96115
{
97-
LOG_INFO << "Using CA certificates from `conda-forge::ca-certificates` installed in the root prefix "
98-
<< "(i.e " << env_prefix_conda_cert << ")";
116+
LOG_INFO << "Using CA certificates from `conda-forge::ca-certificates` installed in the same prefix "
117+
<< "as the executable installation (i.e " << env_prefix_conda_cert
118+
<< ")";
99119
remote_fetch_params.ssl_verify = env_prefix_conda_cert;
100120
remote_fetch_params.curl_initialized = true;
101121
return;
102122
}
103123

124+
// Try to use the CA certificates from `conda-forge::ca-certificates` installed
125+
// in the root prefix.
126+
const fs::u8path root_prefix = detail::get_root_prefix();
127+
const fs::u8path root_prefix_conda_cert = root_prefix
128+
/ prefix_relative_conda_cert_path;
129+
130+
LOG_INFO << "Checking for CA certificates at the root prefix: "
131+
<< root_prefix_conda_cert;
132+
133+
if (fs::exists(root_prefix_conda_cert))
134+
{
135+
LOG_INFO << "Using CA certificates from `conda-forge::ca-certificates` installed in the root prefix "
136+
<< "(i.e " << root_prefix_conda_cert << ")";
137+
remote_fetch_params.ssl_verify = root_prefix_conda_cert;
138+
remote_fetch_params.curl_initialized = true;
139+
return;
140+
}
141+
104142
// Fallback on system CA certificates.
105143
bool found = false;
106144

micromamba/tests/test_env.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import os
22
import re
33
import shutil
4+
import subprocess
5+
import platform
46

57
from packaging.version import Version
68
from pathlib import Path
@@ -624,3 +626,44 @@ def test_env_export_with_uv_flag(tmp_home, tmp_root_prefix, tmp_path, json_flag)
624626
# Check that `requests` and `urllib3` (pulled dependency) are exported
625627
assert "requests==2.32.3" in pip_section_vals
626628
assert any(pkg.startswith("urllib3==") for pkg in pip_section_vals)
629+
630+
631+
def test_env_export_with_ca_certificates(tmp_path):
632+
# CA certificates in the same environment as `mamba` or `micromamba`
633+
# executable installation are used by default.
634+
tmp_env_prefix = tmp_path / "env-export-with-ca-certificates"
635+
636+
helpers.create("-p", tmp_env_prefix, "ca-certificates", no_dry_run=True)
637+
638+
# Copy the `mamba` or `micromamba` executable in this prefix `bin` subdirectory
639+
built_executable = helpers.get_umamba()
640+
executable_basename = os.path.basename(built_executable)
641+
642+
if platform.system() == "Windows":
643+
tmp_env_bin_dir = tmp_env_prefix / "Library" / "bin"
644+
tmp_env_executable = tmp_env_bin_dir / executable_basename
645+
(tmp_env_bin_dir).mkdir(parents=True, exist_ok=True)
646+
# Copy all the `Library/bin/` subdirectory to have the executable in
647+
# the environment and the DLLs in the environment.
648+
shutil.copytree(Path(built_executable).parent, tmp_env_bin_dir, dirs_exist_ok=True)
649+
else:
650+
tmp_env_bin_dir = tmp_env_prefix / "bin"
651+
tmp_env_executable = tmp_env_bin_dir / executable_basename
652+
(tmp_env_bin_dir).mkdir(parents=True, exist_ok=True)
653+
shutil.copy(built_executable, tmp_env_executable)
654+
655+
# Run a command using mamba in verbose mode and check that the ca-certificates file
656+
# from the same prefix as the executable is used by default.
657+
p = subprocess.run(
658+
[tmp_env_executable, "search", "xtensor", "-v"],
659+
capture_output=True,
660+
check=False,
661+
)
662+
stderr = p.stderr.decode()
663+
assert (
664+
"Checking for CA certificates in the same prefix as the executable installation" in stderr
665+
)
666+
assert (
667+
"Using CA certificates from `conda-forge::ca-certificates` installed in the same prefix"
668+
in stderr
669+
)

0 commit comments

Comments
 (0)