1
0
Fork 0
mirror of https://github.com/NixOS/nix synced 2025-06-30 15:48:00 +02:00

Merge pull request #11693 from NixOS/backport-11610-to-2.19-maintenance

Backport #11610 to 2.19 maintenance
This commit is contained in:
Robert Hensing 2024-10-21 22:25:40 +02:00 committed by GitHub
commit 52166fd12e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 105 additions and 34 deletions

View file

@ -1740,13 +1740,20 @@ void LocalDerivationGoal::runChild()
bool setUser = true; bool setUser = true;
/* Make the contents of netrc available to builtin:fetchurl /* Make the contents of netrc and the CA certificate bundle
(which may run under a different uid and/or in a sandbox). */ available to builtin:fetchurl (which may run under a
different uid and/or in a sandbox). */
std::string netrcData; std::string netrcData;
try { std::string caFileData;
if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") if (drv->isBuiltin() && drv->builder == "builtin:fetchurl") {
netrcData = readFile(settings.netrcFile); try {
} catch (SysError &) { } netrcData = readFile(settings.netrcFile);
} catch (SysError &) { }
try {
caFileData = readFile(settings.caFile);
} catch (SysError &) { }
}
#if __linux__ #if __linux__
if (useChroot) { if (useChroot) {
@ -1848,11 +1855,18 @@ void LocalDerivationGoal::runChild()
if (pathExists(path)) if (pathExists(path))
ss.push_back(path); ss.push_back(path);
if (settings.caFile != "") if (settings.caFile != "" && pathExists(settings.caFile)) {
pathsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", settings.caFile, true); Path caFile = settings.caFile;
pathsInChroot.try_emplace("/etc/ssl/certs/ca-certificates.crt", canonPath(caFile, true), true);
}
} }
for (auto & i : ss) pathsInChroot.emplace(i, i); for (auto & i : ss) {
// For backwards-compatibiliy, resolve all the symlinks in the
// chroot paths
auto canonicalPath = canonPath(i, true);
pathsInChroot.emplace(i, canonicalPath);
}
/* Bind-mount all the directories from the "host" /* Bind-mount all the directories from the "host"
filesystem that we want in the chroot filesystem that we want in the chroot
@ -2174,7 +2188,7 @@ void LocalDerivationGoal::runChild()
e.second = rewriteStrings(e.second, inputRewrites); e.second = rewriteStrings(e.second, inputRewrites);
if (drv->builder == "builtin:fetchurl") if (drv->builder == "builtin:fetchurl")
builtinFetchurl(drv2, netrcData); builtinFetchurl(drv2, netrcData, caFileData);
else if (drv->builder == "builtin:buildenv") else if (drv->builder == "builtin:buildenv")
builtinBuildenv(drv2); builtinBuildenv(drv2);
else if (drv->builder == "builtin:unpack-channel") else if (drv->builder == "builtin:unpack-channel")

View file

@ -6,7 +6,9 @@
namespace nix { namespace nix {
// TODO: make pluggable. // TODO: make pluggable.
void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData); void builtinFetchurl(const BasicDerivation & drv,
const std::string & netrcData,
const std::string & caFileData);
void builtinUnpackChannel(const BasicDerivation & drv); void builtinUnpackChannel(const BasicDerivation & drv);
} }

View file

@ -6,7 +6,10 @@
namespace nix { namespace nix {
void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData) void builtinFetchurl(
const BasicDerivation & drv,
const std::string & netrcData,
const std::string & caFileData)
{ {
/* Make the host's netrc data available. Too bad curl requires /* Make the host's netrc data available. Too bad curl requires
this to be stored in a file. It would be nice if we could just this to be stored in a file. It would be nice if we could just
@ -16,6 +19,9 @@ void builtinFetchurl(const BasicDerivation & drv, const std::string & netrcData)
writeFile(settings.netrcFile, netrcData, 0600); writeFile(settings.netrcFile, netrcData, 0600);
} }
settings.caFile = "ca-certificates.crt";
writeFile(settings.caFile, caFileData, 0600);
auto getAttr = [&](const std::string & name) { auto getAttr = [&](const std::string & name) {
auto i = drv.env.find(name); auto i = drv.env.find(name);
if (i == drv.env.end()) throw Error("attribute '%s' missing", name); if (i == drv.env.end()) throw Error("attribute '%s' missing", name);

View file

@ -50,6 +50,8 @@ struct curlFileTransfer : public FileTransfer
bool done = false; // whether either the success or failure function has been called bool done = false; // whether either the success or failure function has been called
Callback<FileTransferResult> callback; Callback<FileTransferResult> callback;
CURL * req = 0; CURL * req = 0;
// buffer to accompany the `req` above
char errbuf[CURL_ERROR_SIZE];
bool active = false; // whether the handle has been added to the multi object bool active = false; // whether the handle has been added to the multi object
std::string statusMsg; std::string statusMsg;
@ -352,6 +354,9 @@ struct curlFileTransfer : public FileTransfer
if (writtenToSink) if (writtenToSink)
curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink); curl_easy_setopt(req, CURLOPT_RESUME_FROM_LARGE, writtenToSink);
curl_easy_setopt(req, CURLOPT_ERRORBUFFER, errbuf);
errbuf[0] = 0;
result.data.clear(); result.data.clear();
result.bodySize = 0; result.bodySize = 0;
} }
@ -465,8 +470,8 @@ struct curlFileTransfer : public FileTransfer
code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code))) code == CURLE_OK ? "" : fmt(" (curl error: %s)", curl_easy_strerror(code)))
: FileTransferError(err, : FileTransferError(err,
std::move(response), std::move(response),
"unable to %s '%s': %s (%d)", "unable to %s '%s': %s (%d) %s",
request.verb(), request.uri, curl_easy_strerror(code), code); request.verb(), request.uri, curl_easy_strerror(code), code, errbuf);
/* If this is a transient error, then maybe retry the /* If this is a transient error, then maybe retry the
download after a while. If we're writing to a download after a while. If we're writing to a

View file

@ -60,7 +60,13 @@ testCert () {
nocert=$TEST_ROOT/no-cert-file.pem nocert=$TEST_ROOT/no-cert-file.pem
cert=$TEST_ROOT/some-cert-file.pem cert=$TEST_ROOT/some-cert-file.pem
symlinkcert=$TEST_ROOT/symlink-cert-file.pem
transitivesymlinkcert=$TEST_ROOT/transitive-symlink-cert-file.pem
symlinkDir=$TEST_ROOT/symlink-dir
echo -n "CERT_CONTENT" > $cert echo -n "CERT_CONTENT" > $cert
ln -s $cert $symlinkcert
ln -s $symlinkcert $transitivesymlinkcert
ln -s $TEST_ROOT $symlinkDir
# No cert in sandbox when not a fixed-output derivation # No cert in sandbox when not a fixed-output derivation
testCert missing normal "$cert" testCert missing normal "$cert"
@ -74,5 +80,14 @@ testCert missing fixed-output "$nocert"
# Cert in sandbox when ssl-cert-file is set to an existing file # Cert in sandbox when ssl-cert-file is set to an existing file
testCert present fixed-output "$cert" testCert present fixed-output "$cert"
# Cert in sandbox when ssl-cert-file is set to a (potentially transitive) symlink to an existing file
testCert present fixed-output "$symlinkcert"
testCert present fixed-output "$transitivesymlinkcert"
# Symlinks should be added in the sandbox directly and not followed # Symlinks should be added in the sandbox directly and not followed
nix-sandbox-build symlink-derivation.nix nix-sandbox-build symlink-derivation.nix -A depends_on_symlink
nix-sandbox-build symlink-derivation.nix -A test_sandbox_paths \
--option extra-sandbox-paths "/file=$cert" \
--option extra-sandbox-paths "/dir=$TEST_ROOT" \
--option extra-sandbox-paths "/symlinkDir=$symlinkDir" \
--option extra-sandbox-paths "/symlink=$symlinkcert"

View file

@ -15,22 +15,45 @@ let
''; '';
}; };
in in
mkDerivation { {
name = "depends-on-symlink"; depends_on_symlink = mkDerivation {
buildCommand = '' name = "depends-on-symlink";
( buildCommand = ''
set -x (
set -x
# `foo_symlink` should be a symlink pointing to `foo_in_store` # `foo_symlink` should be a symlink pointing to `foo_in_store`
[[ -L ${foo_symlink} ]] [[ -L ${foo_symlink} ]]
[[ $(readlink ${foo_symlink}) == ${foo_in_store} ]] [[ $(readlink ${foo_symlink}) == ${foo_in_store} ]]
# `symlink_to_not_in_store` should be a symlink pointing to `./.`, which # `symlink_to_not_in_store` should be a symlink pointing to `./.`, which
# is not available in the sandbox # is not available in the sandbox
[[ -L ${symlink_to_not_in_store} ]] [[ -L ${symlink_to_not_in_store} ]]
[[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]] [[ $(readlink ${symlink_to_not_in_store}) == ${builtins.toString ./.} ]]
(! ls ${symlink_to_not_in_store}/) (! ls ${symlink_to_not_in_store}/)
)
echo "Success!" > $out # Native paths
''; )
echo "Success!" > $out
'';
};
test_sandbox_paths = mkDerivation {
# Depends on the caller to set a bunch of `--sandbox-path` arguments
name = "test-sandbox-paths";
buildCommand = ''
(
set -x
[[ -f /file ]]
[[ -d /dir ]]
# /symlink and /symlinkDir should be available as raw symlinks
# (pointing to files outside of the sandbox)
[[ -L /symlink ]] && [[ ! -e $(readlink /symlink) ]]
[[ -L /symlinkDir ]] && [[ ! -e $(readlink /symlinkDir) ]]
)
touch $out
'';
};
} }

View file

@ -1,7 +1,7 @@
# Test whether builtin:fetchurl properly performs TLS certificate # Test whether builtin:fetchurl properly performs TLS certificate
# checks on HTTPS servers. # checks on HTTPS servers.
{ lib, config, pkgs, ... }: { pkgs, ... }:
let let
@ -25,7 +25,7 @@ in
name = "nss-preload"; name = "nss-preload";
nodes = { nodes = {
machine = { lib, pkgs, ... }: { machine = { pkgs, ... }: {
services.nginx = { services.nginx = {
enable = true; enable = true;
@ -60,13 +60,16 @@ in
}; };
}; };
testScript = { nodes, ... }: '' testScript = ''
machine.wait_for_unit("nginx") machine.wait_for_unit("nginx")
machine.wait_for_open_port(443) machine.wait_for_open_port(443)
out = machine.succeed("curl https://good/index.html") out = machine.succeed("curl https://good/index.html")
assert out == "hello world\n" assert out == "hello world\n"
out = machine.succeed("cat ${badCert}/cert.pem > /tmp/cafile.pem; curl --cacert /tmp/cafile.pem https://bad/index.html")
assert out == "foobar\n"
# Fetching from a server with a trusted cert should work. # Fetching from a server with a trusted cert should work.
machine.succeed("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://good/index.html\"; hash = \"sha256-qUiQTy8PR5uPgZdpSzAYSw0u0cHNKh7A+4XSmaGSpEc=\"; }'") machine.succeed("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://good/index.html\"; hash = \"sha256-qUiQTy8PR5uPgZdpSzAYSw0u0cHNKh7A+4XSmaGSpEc=\"; }'")
@ -74,5 +77,8 @@ in
err = machine.fail("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }' 2>&1") err = machine.fail("nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }' 2>&1")
print(err) print(err)
assert "SSL certificate problem: self-signed certificate" in err assert "SSL certificate problem: self-signed certificate" in err
# Fetching from a server with a trusted cert should work via environment variable override.
machine.succeed("NIX_SSL_CERT_FILE=/tmp/cafile.pem nix build --no-substitute --expr 'import <nix/fetchurl.nix> { url = \"https://bad/index.html\"; hash = \"sha256-rsBwZF/lPuOzdjBZN2E08FjMM3JHyXit0Xi2zN+wAZ8=\"; }'")
''; '';
} }