7
7
#include " nix/util/finally.hh"
8
8
#include " nix/util/callback.hh"
9
9
#include " nix/util/signals.hh"
10
+ #include " nix/store/store-reference.hh"
10
11
11
12
#include " store-config-private.hh"
12
13
#if NIX_WITH_S3_SUPPORT
13
14
# include < aws/core/client/ClientConfiguration.h>
14
15
#endif
16
+ #if NIX_WITH_AWS_CRT_SUPPORT
17
+ # include " nix/store/aws-auth.hh"
18
+ #endif
15
19
16
20
#ifdef __linux__
17
21
# include " nix/util/linux-namespaces.hh"
@@ -77,6 +81,13 @@ struct curlFileTransfer : public FileTransfer
77
81
78
82
std::chrono::steady_clock::time_point startTime = std::chrono::steady_clock::now();
79
83
84
+ #if NIX_WITH_AWS_CRT_SUPPORT
85
+ // AWS SigV4 authentication data
86
+ bool isS3Request = false ;
87
+ std::string awsCredentials; // "access_key:secret_key" for CURLOPT_USERPWD
88
+ std::string awsSigV4Provider; // Provider string for CURLOPT_AWS_SIGV4
89
+ #endif
90
+
80
91
inline static const std::set<long > successfulStatuses{200 , 201 , 204 , 206 , 304 , 0 /* other protocol */ };
81
92
82
93
/* Get the HTTP status code, or 0 for other protocols. */
@@ -131,6 +142,52 @@ struct curlFileTransfer : public FileTransfer
131
142
for (auto it = request.headers .begin (); it != request.headers .end (); ++it) {
132
143
requestHeaders = curl_slist_append (requestHeaders, fmt (" %s: %s" , it->first , it->second ).c_str ());
133
144
}
145
+
146
+ #if NIX_WITH_AWS_CRT_SUPPORT && NIX_WITH_S3_SUPPORT
147
+ // Handle S3 URLs with curl-based AWS SigV4 authentication
148
+ if (hasPrefix (request.uri , " s3://" )) {
149
+ try {
150
+ auto [httpsUri, params] = fileTransfer.convertS3ToHttpsUri (request.uri );
151
+
152
+ // Update the request URI to use HTTPS
153
+ const_cast <FileTransferRequest &>(request).uri = httpsUri;
154
+ result.urls .clear ();
155
+ result.urls .push_back (httpsUri);
156
+
157
+ isS3Request = true ;
158
+
159
+ // Get credentials
160
+ std::string profile = getOr (params, " profile" , " " );
161
+ auto credProvider = profile.empty () ? AwsCredentialProvider::createDefault ()
162
+ : AwsCredentialProvider::createProfile (profile);
163
+
164
+ if (credProvider) {
165
+ auto creds = credProvider->getCredentials ();
166
+ if (creds) {
167
+ awsCredentials = creds->accessKeyId + " :" + creds->secretAccessKey ;
168
+
169
+ std::string region = getOr (params, " region" , " us-east-1" );
170
+ std::string service = " s3" ;
171
+ awsSigV4Provider = " aws:amz:" + region + " :" + service;
172
+
173
+ // Add session token header if present
174
+ if (creds->sessionToken ) {
175
+ requestHeaders = curl_slist_append (
176
+ requestHeaders, (" x-amz-security-token: " + *creds->sessionToken ).c_str ());
177
+ }
178
+
179
+ debug (" Using AWS SigV4 authentication for S3 request to %s" , httpsUri);
180
+ } else {
181
+ warn (" Failed to obtain AWS credentials for S3 request %s" , request.uri );
182
+ }
183
+ } else {
184
+ warn (" Failed to create AWS credential provider for S3 request %s" , request.uri );
185
+ }
186
+ } catch (std::exception & e) {
187
+ warn (" Failed to set up AWS SigV4 authentication for S3 request %s: %s" , request.uri , e.what ());
188
+ }
189
+ }
190
+ #endif
134
191
}
135
192
136
193
~TransferItem ()
@@ -426,6 +483,15 @@ struct curlFileTransfer : public FileTransfer
426
483
curl_easy_setopt (req, CURLOPT_ERRORBUFFER, errbuf);
427
484
errbuf[0 ] = 0 ;
428
485
486
+ #if NIX_WITH_AWS_CRT_SUPPORT && LIBCURL_VERSION_NUM >= 0x074b00 // curl 7.75.0
487
+ // Set up AWS SigV4 authentication if this is an S3 request
488
+ if (isS3Request && !awsCredentials.empty () && !awsSigV4Provider.empty ()) {
489
+ curl_easy_setopt (req, CURLOPT_USERPWD, awsCredentials.c_str ());
490
+ curl_easy_setopt (req, CURLOPT_AWS_SIGV4, awsSigV4Provider.c_str ());
491
+ debug (" Configured curl with AWS SigV4 authentication: provider=%s" , awsSigV4Provider);
492
+ }
493
+ #endif
494
+
429
495
result.data .clear ();
430
496
result.bodySize = 0 ;
431
497
}
@@ -812,15 +878,42 @@ struct curlFileTransfer : public FileTransfer
812
878
813
879
return {bucketName, key, params};
814
880
}
881
+
882
+ /* *
883
+ * Convert S3 URI to HTTPS URI for use with curl's AWS SigV4 authentication
884
+ */
885
+ std::pair<std::string, Store::Config::Params> convertS3ToHttpsUri (const std::string & s3Uri)
886
+ {
887
+ auto [bucketName, key, params] = parseS3Uri (s3Uri);
888
+
889
+ std::string region = getOr (params, " region" , " us-east-1" );
890
+ std::string endpoint = getOr (params, " endpoint" , " " );
891
+ std::string scheme = getOr (params, " scheme" , " https" );
892
+
893
+ std::string httpsUri;
894
+ if (!endpoint.empty ()) {
895
+ // Custom endpoint (e.g., MinIO, custom S3-compatible service)
896
+ httpsUri = scheme + " ://" + endpoint + " /" + bucketName + " /" + key;
897
+ } else {
898
+ // Standard AWS S3 endpoint
899
+ httpsUri = scheme + " ://s3." + region + " .amazonaws.com/" + bucketName + " /" + key;
900
+ }
901
+
902
+ return {httpsUri, params};
903
+ }
815
904
#endif
816
905
817
906
void enqueueFileTransfer (const FileTransferRequest & request, Callback<FileTransferResult> callback) override
818
907
{
819
- /* Ugly hack to support s3:// URIs. */
908
+ /* Handle s3:// URIs with curl-based AWS SigV4 authentication or fall back to legacy S3Helper */
820
909
if (hasPrefix (request.uri , " s3://" )) {
910
+ #if NIX_WITH_AWS_CRT_SUPPORT && LIBCURL_VERSION_NUM >= 0x074b00
911
+ // Use new curl-based approach with AWS SigV4 authentication
912
+ enqueueItem (std::make_shared<TransferItem>(*this , request, std::move (callback)));
913
+ #elif NIX_WITH_S3_SUPPORT
914
+ // Fall back to legacy S3Helper approach
821
915
// FIXME: do this on a worker thread
822
916
try {
823
- #if NIX_WITH_S3_SUPPORT
824
917
auto [bucketName, key, params] = parseS3Uri (request.uri );
825
918
826
919
std::string profile = getOr (params, " profile" , " " );
@@ -838,12 +931,12 @@ struct curlFileTransfer : public FileTransfer
838
931
res.data = std::move (*s3Res.data );
839
932
res.urls .push_back (request.uri );
840
933
callback (std::move (res));
841
- #else
842
- throw nix::Error (" cannot download '%s' because Nix is not built with S3 support" , request.uri );
843
- #endif
844
934
} catch (...) {
845
935
callback.rethrow ();
846
936
}
937
+ #else
938
+ throw nix::Error (" cannot download '%s' because Nix is not built with S3 support" , request.uri );
939
+ #endif
847
940
return ;
848
941
}
849
942
0 commit comments