Skip to content

Commit 000e6f6

Browse files
committed
feat(libstore): add builtin fetchurl S3 credential pre-resolution
Add support for pre-resolving AWS credentials in the parent process before forking for builtin:fetchurl. This avoids recreating credential providers in the forked child process.
1 parent f770947 commit 000e6f6

File tree

4 files changed

+97
-5
lines changed

4 files changed

+97
-5
lines changed

src/libstore/builtins/fetchurl.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,26 @@ static void builtinFetchurl(const BuiltinBuilderContext & ctx)
3333

3434
/* Note: have to use a fresh fileTransfer here because we're in
3535
a forked process. */
36+
debug("[pid=%d] builtin:fetchurl creating fresh FileTransfer instance", getpid());
3637
auto fileTransfer = makeFileTransfer();
3738

3839
auto fetch = [&](const std::string & url) {
3940
auto source = sinkToSource([&](Sink & sink) {
4041
FileTransferRequest request(ValidURL{url});
4142
request.decompress = false;
4243

44+
#if NIX_WITH_CURL_S3
45+
// Use pre-resolved credentials if available
46+
if (ctx.awsCredentials && request.uri.scheme() == "s3") {
47+
debug("[pid=%d] Using pre-resolved AWS credentials from parent process", getpid());
48+
request.usernameAuth = UsernameAuth{
49+
.username = ctx.awsCredentials->accessKeyId,
50+
.password = ctx.awsCredentials->secretAccessKey,
51+
};
52+
request.preResolvedAwsSessionToken = ctx.awsCredentials->sessionToken;
53+
}
54+
#endif
55+
4356
auto decompressor = makeDecompressionSink(unpack && hasSuffix(mainUrl, ".xz") ? "xz" : "none", sink);
4457
fileTransfer->download(std::move(request), *decompressor);
4558
decompressor->finish();

src/libstore/include/nix/store/builtins.hh

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
///@file
33

44
#include "nix/store/derivations.hh"
5+
#include "nix/store/config.hh"
6+
7+
#if NIX_WITH_CURL_S3
8+
# include "nix/store/aws-creds.hh"
9+
#endif
510

611
namespace nix {
712

@@ -12,6 +17,14 @@ struct BuiltinBuilderContext
1217
std::string netrcData;
1318
std::string caFileData;
1419
Path tmpDirInSandbox;
20+
21+
#if NIX_WITH_CURL_S3
22+
/**
23+
* Pre-resolved AWS credentials for S3 URLs in builtin:fetchurl.
24+
* When present, these should be used instead of creating new credential providers.
25+
*/
26+
std::optional<AwsCredentials> awsCredentials;
27+
#endif
1528
};
1629

1730
using BuiltinBuilder = std::function<void(const BuiltinBuilderContext &)>;

src/libstore/unix/build/derivation-builder.cc

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,12 @@
4646
#include "store-config-private.hh"
4747
#include "build/derivation-check.hh"
4848

49+
#if NIX_WITH_CURL_S3
50+
# include "nix/store/aws-creds.hh"
51+
# include "nix/store/s3-url.hh"
52+
# include "nix/util/url.hh"
53+
#endif
54+
4955
namespace nix {
5056

5157
struct NotDeterministic : BuildError
@@ -290,6 +296,15 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
290296
*/
291297
virtual void startChild();
292298

299+
#if NIX_WITH_CURL_S3
300+
/**
301+
* Pre-resolve AWS credentials for S3 URLs in builtin:fetchurl.
302+
* This should be called before forking to ensure credentials are available in child.
303+
* Returns the credentials if successfully resolved, or std::nullopt otherwise.
304+
*/
305+
std::optional<AwsCredentials> preResolveAwsCredentials();
306+
#endif
307+
293308
private:
294309

295310
/**
@@ -339,10 +354,20 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder
339354
*/
340355
void writeBuilderFile(const std::string & name, std::string_view contents);
341356

357+
/**
358+
* Arguments passed to runChild().
359+
*/
360+
struct RunChildArgs
361+
{
362+
#if NIX_WITH_CURL_S3
363+
std::optional<AwsCredentials> awsCredentials;
364+
#endif
365+
};
366+
342367
/**
343368
* Run the builder's process.
344369
*/
345-
void runChild();
370+
void runChild(RunChildArgs args);
346371

347372
/**
348373
* Move the current process into the chroot, if any. Called early
@@ -920,11 +945,43 @@ void DerivationBuilderImpl::openSlave()
920945
throw SysError("cannot pipe standard error into log file");
921946
}
922947

948+
#if NIX_WITH_CURL_S3
949+
std::optional<AwsCredentials> DerivationBuilderImpl::preResolveAwsCredentials()
950+
{
951+
if (drv.isBuiltin() && drv.builder == "builtin:fetchurl") {
952+
auto url = drv.env.find("url");
953+
if (url != drv.env.end()) {
954+
try {
955+
auto parsedUrl = parseURL(url->second);
956+
if (parsedUrl.scheme == "s3") {
957+
debug("Pre-resolving AWS credentials for S3 URL in builtin:fetchurl");
958+
auto s3Url = ParsedS3URL::parse(parsedUrl);
959+
960+
// Use the preResolveAwsCredentials from aws-creds
961+
auto credentials = nix::preResolveAwsCredentials(s3Url);
962+
debug("Successfully pre-resolved AWS credentials in parent process");
963+
return credentials;
964+
}
965+
} catch (const std::exception & e) {
966+
debug("Error pre-resolving S3 credentials: %s", e.what());
967+
}
968+
}
969+
}
970+
return std::nullopt;
971+
}
972+
#endif
973+
923974
void DerivationBuilderImpl::startChild()
924975
{
925-
pid = startProcess([&]() {
976+
RunChildArgs args{
977+
#if NIX_WITH_CURL_S3
978+
.awsCredentials = preResolveAwsCredentials(),
979+
#endif
980+
};
981+
982+
pid = startProcess([this, args = std::move(args)]() {
926983
openSlave();
927-
runChild();
984+
runChild(std::move(args));
928985
});
929986
}
930987

@@ -1181,7 +1238,7 @@ void DerivationBuilderImpl::writeBuilderFile(const std::string & name, std::stri
11811238
chownToBuilder(fd.get(), path);
11821239
}
11831240

1184-
void DerivationBuilderImpl::runChild()
1241+
void DerivationBuilderImpl::runChild(RunChildArgs args)
11851242
{
11861243
/* Warning: in the child we should absolutely not make any SQLite
11871244
calls! */
@@ -1198,6 +1255,9 @@ void DerivationBuilderImpl::runChild()
11981255
BuiltinBuilderContext ctx{
11991256
.drv = drv,
12001257
.tmpDirInSandbox = tmpDirInSandbox(),
1258+
#if NIX_WITH_CURL_S3
1259+
.awsCredentials = args.awsCredentials,
1260+
#endif
12011261
};
12021262

12031263
if (drv.isBuiltin() && drv.builder == "builtin:fetchurl") {

src/libstore/unix/build/linux-derivation-builder.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,12 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu
276276

277277
void startChild() override
278278
{
279+
RunChildArgs args{
280+
# if NIX_WITH_CURL_S3
281+
.awsCredentials = preResolveAwsCredentials(),
282+
# endif
283+
};
284+
279285
/* Set up private namespaces for the build:
280286
281287
- The PID namespace causes the build to start as PID 1.
@@ -343,7 +349,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu
343349
if (usingUserNamespace)
344350
options.cloneFlags |= CLONE_NEWUSER;
345351

346-
pid_t child = startProcess([&]() { runChild(); }, options);
352+
pid_t child = startProcess([this, args = std::move(args)]() { runChild(std::move(args)); }, options);
347353

348354
writeFull(sendPid.writeSide.get(), fmt("%d\n", child));
349355
_exit(0);

0 commit comments

Comments
 (0)