mirror of https://github.com/mamba-org/mamba.git
fix: Use CA certificates from `conda-forge::ca-certificates` (#3765)
Signed-off-by: Julien Jerphanion <git@jjerphan.xyz>
This commit is contained in:
parent
617be25ef1
commit
7fa748f53e
|
@ -483,6 +483,8 @@ namespace mamba
|
|||
|
||||
namespace detail
|
||||
{
|
||||
auto get_root_prefix() -> fs::u8path;
|
||||
|
||||
template <class T>
|
||||
bool ConfigurableImpl<T>::cli_configured() const
|
||||
{
|
||||
|
|
|
@ -713,29 +713,7 @@ namespace mamba
|
|||
return { fs::weakly_canonical(std::move(prefix)) };
|
||||
}
|
||||
|
||||
/**
|
||||
* In mamba 1.0, only micromamba was using this location.
|
||||
*/
|
||||
auto default_root_prefix_v1() -> fs::u8path
|
||||
{
|
||||
return fs::u8path(util::user_home_dir()) / "micromamba";
|
||||
}
|
||||
|
||||
/**
|
||||
* In mamba 2.0, we change the default location.
|
||||
* We unconditionally name the subfolder "mamba" for compatibility between ``mamba``
|
||||
* and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables.
|
||||
*/
|
||||
auto default_root_prefix_v2() -> fs::u8path
|
||||
{
|
||||
return fs::u8path(util::user_data_dir()) / "mamba";
|
||||
}
|
||||
|
||||
void root_prefix_hook(Configuration& config, fs::u8path& prefix)
|
||||
{
|
||||
auto& env_name = config.at("env_name");
|
||||
|
||||
if (prefix.empty())
|
||||
auto get_default_root_prefix(fs::u8path& prefix) -> void
|
||||
{
|
||||
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
|
||||
{
|
||||
|
@ -754,13 +732,42 @@ namespace mamba
|
|||
.or_else([](mamba_error&& error) { throw std::move(error); });
|
||||
#else
|
||||
// micromamba case
|
||||
validate_existing_root_prefix(default_root_prefix_v1())
|
||||
.or_else([](const auto& /* error */)
|
||||
{ return validate_root_prefix(default_root_prefix_v2()); })
|
||||
|
||||
// In 1.0, only micromamba was using this location.
|
||||
const fs::u8path default_root_prefix_v1 = fs::u8path(util::user_home_dir())
|
||||
/ "micromamba";
|
||||
|
||||
// In 2.0, we change the default location.
|
||||
// We unconditionally name the subfolder "mamba" for compatibility between ``mamba``
|
||||
// and ``micromamba``, as well as consistency with ``MAMBA_`` environment variables.
|
||||
const fs::u8path default_root_prefix_v2 = fs::u8path(util::user_data_dir()) / "mamba";
|
||||
|
||||
validate_existing_root_prefix(default_root_prefix_v1)
|
||||
.or_else([&default_root_prefix_v2](const auto& /* error */)
|
||||
{ return validate_root_prefix(default_root_prefix_v2); })
|
||||
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
|
||||
.or_else([](mamba_error&& error) { throw std::move(error); });
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
auto get_root_prefix() -> fs::u8path
|
||||
{
|
||||
fs::u8path root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or("");
|
||||
if (root_prefix.empty())
|
||||
{
|
||||
get_default_root_prefix(root_prefix);
|
||||
}
|
||||
return root_prefix;
|
||||
}
|
||||
|
||||
void root_prefix_hook(Configuration& config, fs::u8path& prefix)
|
||||
{
|
||||
auto& env_name = config.at("env_name");
|
||||
|
||||
if (prefix.empty())
|
||||
{
|
||||
prefix = get_root_prefix();
|
||||
|
||||
if (env_name.configured())
|
||||
{
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/core/context.hpp"
|
||||
#include "mamba/core/execution.hpp"
|
||||
#include "mamba/core/output.hpp"
|
||||
|
@ -177,7 +178,7 @@ namespace mamba
|
|||
Context::Context(const ContextOptions& options)
|
||||
{
|
||||
on_ci = static_cast<bool>(util::get_env("CI"));
|
||||
prefix_params.root_prefix = util::get_env("MAMBA_ROOT_PREFIX").value_or("");
|
||||
prefix_params.root_prefix = detail::get_root_prefix();
|
||||
prefix_params.conda_prefix = prefix_params.root_prefix;
|
||||
|
||||
envs_dirs = { prefix_params.root_prefix / "envs" };
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// The full license is in the file LICENSE, distributed with this software.
|
||||
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/core/invoke.hpp"
|
||||
#include "mamba/core/thread_utils.hpp"
|
||||
#include "mamba/core/util.hpp"
|
||||
|
@ -24,13 +25,18 @@ namespace mamba::download
|
|||
namespace
|
||||
{
|
||||
|
||||
constexpr std::array<const char*, 6> cert_locations{
|
||||
constexpr std::array<const char*, 10> cert_locations{
|
||||
"/etc/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||
"/etc/ssl/cert.pem", // Alpine Linux
|
||||
// MacOS
|
||||
"/System/Library/OpenSSL/certs/cert.pem",
|
||||
"/usr/local/etc/openssl/cert.pem",
|
||||
"/usr/local/share/certs/ca-root-nss.crt",
|
||||
"/usr/local/share/certs/ca-root.crt",
|
||||
};
|
||||
|
||||
void init_remote_fetch_params(Context::RemoteFetchParams& remote_fetch_params)
|
||||
|
@ -74,16 +80,52 @@ namespace mamba::download
|
|||
LOG_INFO << "Using REQUESTS_CA_BUNDLE " << remote_fetch_params.ssl_verify;
|
||||
}
|
||||
}
|
||||
else if (remote_fetch_params.ssl_verify == "<system>" && util::on_linux)
|
||||
// TODO: Adapt the semantic of `<system>` to decouple the use of CA certificates
|
||||
// from `conda-forge::ca-certificates` and the system CA certificates.
|
||||
else if (remote_fetch_params.ssl_verify == "<system>")
|
||||
{
|
||||
// Use the CA certificates from `conda-forge::ca-certificates` installed in the
|
||||
// root prefix or the system CA certificates if the certificate is not present.
|
||||
fs::u8path libmamba_library_path;
|
||||
|
||||
fs::u8path root_prefix = detail::get_root_prefix();
|
||||
fs::u8path env_prefix_conda_cert = root_prefix / "ssl" / "cacert.pem";
|
||||
|
||||
LOG_INFO << "Checking for CA certificates at the root prefix: "
|
||||
<< env_prefix_conda_cert;
|
||||
|
||||
if (fs::exists(env_prefix_conda_cert))
|
||||
{
|
||||
LOG_INFO << "Using CA certificates from `conda-forge::ca-certificates` installed in the root prefix "
|
||||
<< "(i.e " << env_prefix_conda_cert << ")";
|
||||
remote_fetch_params.ssl_verify = env_prefix_conda_cert;
|
||||
remote_fetch_params.curl_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Fallback on system CA certificates.
|
||||
bool found = false;
|
||||
|
||||
// TODO: find if one needs to specify a CA certificate on Windows or not
|
||||
// given that the location of system's CA certificates is not clear on Windows.
|
||||
// For now, just use `libcurl` and the SSL libraries' default.
|
||||
if (util::on_win)
|
||||
{
|
||||
LOG_INFO << "Using libcurl/the SSL library's default CA certification";
|
||||
remote_fetch_params.ssl_verify = "";
|
||||
found = true;
|
||||
remote_fetch_params.curl_initialized = true;
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& loc : cert_locations)
|
||||
{
|
||||
if (fs::exists(loc))
|
||||
{
|
||||
LOG_INFO << "Using system CA certificates at: " << loc;
|
||||
remote_fetch_params.ssl_verify = loc;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <catch2/catch_all.hpp>
|
||||
|
||||
#include "mamba/api/configuration.hpp"
|
||||
#include "mamba/download/downloader.hpp"
|
||||
|
||||
#include "mambatests.hpp"
|
||||
|
@ -54,5 +55,46 @@ namespace mamba
|
|||
std::runtime_error
|
||||
);
|
||||
}
|
||||
|
||||
TEST_CASE("Use CA certificate from the root prefix")
|
||||
{
|
||||
// Create a context, make a request and check that ssl_verify is set to the correct path
|
||||
auto& context = mambatests::singletons().context;
|
||||
|
||||
// Set the context values to the default ones
|
||||
context.remote_fetch_params.curl_initialized = false;
|
||||
context.remote_fetch_params.ssl_verify = "<system>";
|
||||
|
||||
download::Request request(
|
||||
"test",
|
||||
download::MirrorName(""),
|
||||
"https://conda.anaconda.org/conda-forge/linux-64/repodata.json",
|
||||
"test_download_repodata.json"
|
||||
);
|
||||
download::MultiRequest dl_request{ std::vector{ std::move(request) } };
|
||||
|
||||
// Downloading must initialize curl and set `ssl_verify` to the path of the CA
|
||||
// certificate
|
||||
REQUIRE(!context.remote_fetch_params.curl_initialized);
|
||||
download::MultiResult res = download::download(dl_request, context.mirrors, context);
|
||||
REQUIRE(context.remote_fetch_params.curl_initialized);
|
||||
|
||||
auto certificates = context.remote_fetch_params.ssl_verify;
|
||||
const fs::u8path root_prefix = detail::get_root_prefix();
|
||||
const fs::u8path expected_certificates = root_prefix / "ssl" / "cert.pem";
|
||||
|
||||
// TODO: is libmamba tested without a root prefix or a base installation?
|
||||
bool reach_fallback_certificates;
|
||||
if (util::on_win)
|
||||
{
|
||||
// Default certificates from libcurl/libssl are used on Windows
|
||||
reach_fallback_certificates = certificates == "";
|
||||
}
|
||||
else
|
||||
{
|
||||
reach_fallback_certificates = (mamba::util::ends_with(certificates, "cert.pem") || mamba::util::ends_with(certificates, "ca-certificates.crt"));
|
||||
}
|
||||
REQUIRE((certificates == expected_certificates || reach_fallback_certificates));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1608,3 +1608,33 @@ https://conda.anaconda.org/conda-forge/noarch/pip-24.3.1-pyh145f28c_2.conda#7660
|
|||
out = helpers.create("-p", env_prefix, "-f", env_spec_file, "--dry-run")
|
||||
|
||||
assert update_specs_list in out.replace("\r", "")
|
||||
|
||||
|
||||
def test_ca_certificates(tmp_path):
|
||||
# Check that CA certificates from conda-forge or that the fall back is used by micromamba.
|
||||
env_prefix = tmp_path / "env-ca-certificates"
|
||||
|
||||
umamba = helpers.get_umamba()
|
||||
args = [umamba, "create", "-p", env_prefix, "numpy", "--dry-run", "-vvv"]
|
||||
p = subprocess.run(args, capture_output=True, check=True)
|
||||
verbose_logs = p.stderr.decode()
|
||||
|
||||
root_prefix_ca_certificates_used = (
|
||||
"Using CA certificates from `conda-forge::ca-certificates` installed in the root prefix"
|
||||
in verbose_logs
|
||||
)
|
||||
|
||||
system_ca_certificates_used = "Using system CA certificates at" in verbose_logs
|
||||
|
||||
default_libcurl_certificates_used = (
|
||||
"Using libcurl/the SSL library's default CA certification" in verbose_logs
|
||||
)
|
||||
|
||||
# On Windows default
|
||||
fall_back_certificates_used = (
|
||||
default_libcurl_certificates_used
|
||||
if platform.system() == "Windows"
|
||||
else system_ca_certificates_used
|
||||
)
|
||||
|
||||
assert root_prefix_ca_certificates_used or fall_back_certificates_used
|
||||
|
|
Loading…
Reference in New Issue