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
|
namespace detail
|
||||||
{
|
{
|
||||||
|
auto get_root_prefix() -> fs::u8path;
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
bool ConfigurableImpl<T>::cli_configured() const
|
bool ConfigurableImpl<T>::cli_configured() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -713,22 +713,52 @@ namespace mamba
|
||||||
return { fs::weakly_canonical(std::move(prefix)) };
|
return { fs::weakly_canonical(std::move(prefix)) };
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
auto get_default_root_prefix(fs::u8path& prefix) -> void
|
||||||
* 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";
|
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
|
||||||
|
{
|
||||||
|
prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value();
|
||||||
|
LOG_WARNING << unindent(R"(
|
||||||
|
'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose.
|
||||||
|
Consider using 'MAMBA_ROOT_PREFIX' instead)");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE
|
||||||
|
// mamba case
|
||||||
|
// set the root prefix as the mamba installation path
|
||||||
|
get_root_prefix_from_mamba_bin(util::which("mamba"))
|
||||||
|
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
|
||||||
|
.or_else([](mamba_error&& error) { throw std::move(error); });
|
||||||
|
#else
|
||||||
|
// micromamba case
|
||||||
|
|
||||||
|
// 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
|
||||||
* 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";
|
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)
|
void root_prefix_hook(Configuration& config, fs::u8path& prefix)
|
||||||
|
@ -737,30 +767,7 @@ namespace mamba
|
||||||
|
|
||||||
if (prefix.empty())
|
if (prefix.empty())
|
||||||
{
|
{
|
||||||
if (util::get_env("MAMBA_DEFAULT_ROOT_PREFIX"))
|
prefix = get_root_prefix();
|
||||||
{
|
|
||||||
prefix = util::get_env("MAMBA_DEFAULT_ROOT_PREFIX").value();
|
|
||||||
LOG_WARNING << unindent(R"(
|
|
||||||
'MAMBA_DEFAULT_ROOT_PREFIX' is meant for testing purpose.
|
|
||||||
Consider using 'MAMBA_ROOT_PREFIX' instead)");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#ifdef MAMBA_USE_INSTALL_PREFIX_AS_BASE
|
|
||||||
// mamba case
|
|
||||||
// set the root prefix as the mamba installation path
|
|
||||||
get_root_prefix_from_mamba_bin(util::which("mamba"))
|
|
||||||
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
|
|
||||||
.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()); })
|
|
||||||
.transform([&](fs::u8path&& p) { prefix = std::move(p); })
|
|
||||||
.or_else([](mamba_error&& error) { throw std::move(error); });
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
if (env_name.configured())
|
if (env_name.configured())
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <spdlog/sinks/stdout_color_sinks.h>
|
#include <spdlog/sinks/stdout_color_sinks.h>
|
||||||
#include <spdlog/spdlog.h>
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#include "mamba/api/configuration.hpp"
|
||||||
#include "mamba/core/context.hpp"
|
#include "mamba/core/context.hpp"
|
||||||
#include "mamba/core/execution.hpp"
|
#include "mamba/core/execution.hpp"
|
||||||
#include "mamba/core/output.hpp"
|
#include "mamba/core/output.hpp"
|
||||||
|
@ -177,7 +178,7 @@ namespace mamba
|
||||||
Context::Context(const ContextOptions& options)
|
Context::Context(const ContextOptions& options)
|
||||||
{
|
{
|
||||||
on_ci = static_cast<bool>(util::get_env("CI"));
|
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;
|
prefix_params.conda_prefix = prefix_params.root_prefix;
|
||||||
|
|
||||||
envs_dirs = { prefix_params.root_prefix / "envs" };
|
envs_dirs = { prefix_params.root_prefix / "envs" };
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
//
|
//
|
||||||
// The full license is in the file LICENSE, distributed with this software.
|
// 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/invoke.hpp"
|
||||||
#include "mamba/core/thread_utils.hpp"
|
#include "mamba/core/thread_utils.hpp"
|
||||||
#include "mamba/core/util.hpp"
|
#include "mamba/core/util.hpp"
|
||||||
|
@ -24,13 +25,18 @@ namespace mamba::download
|
||||||
namespace
|
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/ssl/certs/ca-certificates.crt", // Debian/Ubuntu/Gentoo etc.
|
||||||
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
"/etc/pki/tls/certs/ca-bundle.crt", // Fedora/RHEL 6
|
||||||
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
"/etc/ssl/ca-bundle.pem", // OpenSUSE
|
||||||
"/etc/pki/tls/cacert.pem", // OpenELEC
|
"/etc/pki/tls/cacert.pem", // OpenELEC
|
||||||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", // CentOS/RHEL 7
|
||||||
"/etc/ssl/cert.pem", // Alpine Linux
|
"/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)
|
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;
|
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;
|
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)
|
for (const auto& loc : cert_locations)
|
||||||
{
|
{
|
||||||
if (fs::exists(loc))
|
if (fs::exists(loc))
|
||||||
{
|
{
|
||||||
|
LOG_INFO << "Using system CA certificates at: " << loc;
|
||||||
remote_fetch_params.ssl_verify = loc;
|
remote_fetch_params.ssl_verify = loc;
|
||||||
found = true;
|
found = true;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include <catch2/catch_all.hpp>
|
#include <catch2/catch_all.hpp>
|
||||||
|
|
||||||
|
#include "mamba/api/configuration.hpp"
|
||||||
#include "mamba/download/downloader.hpp"
|
#include "mamba/download/downloader.hpp"
|
||||||
|
|
||||||
#include "mambatests.hpp"
|
#include "mambatests.hpp"
|
||||||
|
@ -54,5 +55,46 @@ namespace mamba
|
||||||
std::runtime_error
|
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")
|
out = helpers.create("-p", env_prefix, "-f", env_spec_file, "--dry-run")
|
||||||
|
|
||||||
assert update_specs_list in out.replace("\r", "")
|
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