Skip to content

Commit 6a4351a

Browse files
committed
Merge tag '2.33.4' into sync-2.33.4
Tagging release 2.33.4
2 parents 7ab838d + e4491cf commit 6a4351a

9 files changed

Lines changed: 141 additions & 27 deletions

File tree

.version

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.33.3
1+
2.33.4
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
synopsis: "S3: restore STS WebIdentity and ECS container credential providers"
3+
prs: [15507]
4+
---
5+
6+
Nix 2.33 replaced the S3 backend's `aws-sdk-cpp` credential chain with a
7+
custom chain built on `aws-c-auth`. That chain omitted two providers,
8+
breaking S3 binary cache access in container workloads:
9+
10+
- **STS WebIdentity** (`AWS_WEB_IDENTITY_TOKEN_FILE`, `AWS_ROLE_ARN`,
11+
`AWS_ROLE_SESSION_NAME`) — used by EKS IRSA, GitHub Actions OIDC, and
12+
any `sts:AssumeRoleWithWebIdentity` federation.
13+
- **ECS container metadata** (`AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`,
14+
`AWS_CONTAINER_CREDENTIALS_FULL_URI`) — used by ECS tasks and EKS Pod
15+
Identity.
16+
17+
The typical symptom was a misleading IMDS error
18+
(`Valid credentials could not be sourced by the IMDS provider`), because
19+
IMDS is the last provider tried after the correct one was skipped.
20+
21+
Both providers are now part of the chain, ordered to match the
22+
pre-2.33 `DefaultAWSCredentialsProviderChain`:
23+
`Environment → SSO → Profile → STS WebIdentity → (ECS | IMDS)`.
24+
As in both the old and new AWS SDK default chains, ECS and IMDS are
25+
mutually exclusive: when container credential environment variables are
26+
set, IMDS is skipped.

packaging/binary-tarball.nix

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ runCommand "nix-binary-tarball-${version}" env ''
7474
fn=$out/$dir.tar.xz
7575
mkdir -p $out/nix-support
7676
echo "file binary-dist $fn" >> $out/nix-support/hydra-build-products
77-
tar cfJ $fn \
77+
tar cf - \
78+
--sort=name \
7879
--owner=0 --group=0 --mode=u+rw,uga+r \
7980
--mtime='1970-01-01' \
8081
--absolute-names \
@@ -90,5 +91,5 @@ runCommand "nix-binary-tarball-${version}" env ''
9091
$TMPDIR/install-freebsd-multi-user.sh \
9192
$TMPDIR/install-multi-user \
9293
$TMPDIR/reginfo \
93-
$(cat ${installerClosureInfo}/store-paths)
94+
$(cat ${installerClosureInfo}/store-paths) | xz --threads=1 > $fn
9495
''

src/libcmd/markdown.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ static std::string doRenderMarkdownToTerminal(std::string_view markdown)
3838
# endif
3939
.feat = LOWDOWN_COMMONMARK | LOWDOWN_FENCED | LOWDOWN_DEFLIST | LOWDOWN_TABLES,
4040
.oflags =
41-
# if HAVE_LOWDOWN_1_4
41+
# if HAVE_LOWDOWN_3
42+
LOWDOWN_NORELLINK
43+
# elif HAVE_LOWDOWN_1_4
4244
LOWDOWN_TERM_NORELLINK // To render full links while skipping relative ones
4345
# else
4446
LOWDOWN_TERM_NOLINK

src/libcmd/meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ configdata.set(
4444
'HAVE_LOWDOWN_1_4',
4545
lowdown.version().version_compare('>= 1.4.0').to_int(),
4646
)
47+
configdata.set(
48+
'HAVE_LOWDOWN_3',
49+
lowdown.version().version_compare('>= 3.0.0').to_int(),
50+
)
4751

4852
readline_flavor = get_option('readline-flavor')
4953
if readline_flavor == 'editline'

src/libstore/aws-creds.cc

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
# include <aws/crt/auth/Credentials.h>
1111
# include <aws/crt/io/Bootstrap.h>
1212

13-
// C library headers for SSO provider support
13+
// C library headers for SSO, STS WebIdentity, and ECS credential providers
1414
# include <aws/auth/credentials.h>
1515

1616
// C library headers for custom logging
@@ -170,6 +170,53 @@ static std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createSSOProvider(
170170
return createWrappedProvider(aws_credentials_provider_new_sso(allocator, &options), allocator);
171171
}
172172

173+
/**
174+
* Create an STS WebIdentity credentials provider using the C library directly.
175+
* This reads AWS_WEB_IDENTITY_TOKEN_FILE, AWS_ROLE_ARN, AWS_ROLE_SESSION_NAME,
176+
* and AWS_REGION from the environment (falling back to the profile config).
177+
* Used by EKS IRSA, GitHub Actions OIDC, and other sts:AssumeRoleWithWebIdentity flows.
178+
* Returns nullptr if the required parameters can't be resolved.
179+
*/
180+
static std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createSTSWebIdentityProvider(
181+
const std::string & profileName,
182+
Aws::Crt::Io::ClientBootstrap * bootstrap,
183+
Aws::Crt::Io::TlsContext * tlsContext,
184+
Aws::Crt::Allocator * allocator = Aws::Crt::ApiAllocator())
185+
{
186+
aws_credentials_provider_sts_web_identity_options options;
187+
AWS_ZERO_STRUCT(options);
188+
189+
options.bootstrap = bootstrap->GetUnderlyingHandle();
190+
options.tls_ctx = tlsContext ? tlsContext->GetUnderlyingHandle() : nullptr;
191+
if (!profileName.empty()) {
192+
options.profile_name_override = aws_byte_cursor_from_c_str(profileName.c_str());
193+
}
194+
195+
return createWrappedProvider(aws_credentials_provider_new_sts_web_identity(allocator, &options), allocator);
196+
}
197+
198+
/**
199+
* Create an ECS container credentials provider using the C library directly.
200+
* This reads AWS_CONTAINER_CREDENTIALS_RELATIVE_URI or
201+
* AWS_CONTAINER_CREDENTIALS_FULL_URI (plus the optional
202+
* AWS_CONTAINER_AUTHORIZATION_TOKEN / _TOKEN_FILE) from the environment.
203+
* Used by ECS tasks and EKS Pod Identity.
204+
* Returns nullptr if neither URI env var is set.
205+
*/
206+
static std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> createECSProvider(
207+
Aws::Crt::Io::ClientBootstrap * bootstrap,
208+
Aws::Crt::Io::TlsContext * tlsContext,
209+
Aws::Crt::Allocator * allocator = Aws::Crt::ApiAllocator())
210+
{
211+
aws_credentials_provider_ecs_environment_options options;
212+
AWS_ZERO_STRUCT(options);
213+
214+
options.bootstrap = bootstrap->GetUnderlyingHandle();
215+
options.tls_ctx = tlsContext ? tlsContext->GetUnderlyingHandle() : nullptr;
216+
217+
return createWrappedProvider(aws_credentials_provider_new_ecs_from_environment(allocator, &options), allocator);
218+
}
219+
173220
static AwsCredentials getCredentialsFromProvider(std::shared_ptr<Aws::Crt::Auth::ICredentialsProvider> provider)
174221
{
175222
if (!provider || !provider->IsValid()) {
@@ -223,13 +270,14 @@ class AwsCredentialProviderImpl : public AwsCredentialProvider
223270
// This ensures AWS logs respect Nix's verbosity settings and are formatted consistently.
224271
initialiseAwsLogger();
225272

226-
// Create a shared TLS context for SSO (required for HTTPS connections)
273+
// Create a shared TLS context for SSO, STS WebIdentity, and ECS providers (required for HTTPS)
227274
auto allocator = Aws::Crt::ApiAllocator();
228275
auto tlsCtxOptions = Aws::Crt::Io::TlsContextOptions::InitDefaultClient(allocator);
229276
tlsContext =
230277
std::make_shared<Aws::Crt::Io::TlsContext>(tlsCtxOptions, Aws::Crt::Io::TlsMode::CLIENT, allocator);
231278
if (!tlsContext || !*tlsContext) {
232-
warn("failed to create TLS context for AWS SSO; SSO authentication will be unavailable");
279+
warn(
280+
"failed to create TLS context for AWS credential providers; SSO, STS WebIdentity, and ECS container authentication will be unavailable");
233281
tlsContext = nullptr;
234282
}
235283

@@ -273,19 +321,20 @@ AwsCredentialProviderImpl::createProviderForProfile(const std::string & profile)
273321

274322
debug("[pid=%d] creating new AWS credential provider for profile '%s'", getpid(), profileDisplayName);
275323

276-
// Build a custom credential chain: Environment → SSO → Profile → IMDS
324+
// Build a custom credential chain: Environment → SSO → Profile → STS WebIdentity → ECS → IMDS
277325
// This works for both default and named profiles, ensuring consistent behavior
278326
// including SSO support and proper TLS context for STS-based role assumption.
279327
Aws::Crt::Auth::CredentialsProviderChainConfig chainConfig;
280328
auto allocator = Aws::Crt::ApiAllocator();
281329

282-
auto addProviderToChain = [&](std::string_view name, auto createProvider) {
330+
auto addProviderToChain = [&](std::string_view name, auto createProvider) -> bool {
283331
if (auto provider = createProvider()) {
284332
chainConfig.Providers.push_back(provider);
285333
debug("Added AWS %s Credential Provider to chain for profile '%s'", name, profileDisplayName);
286-
} else {
287-
debug("Skipped AWS %s Credential Provider for profile '%s'", name, profileDisplayName);
334+
return true;
288335
}
336+
debug("Skipped AWS %s Credential Provider for profile '%s'", name, profileDisplayName);
337+
return false;
289338
};
290339

291340
// 1. Environment variables (highest priority)
@@ -311,12 +360,37 @@ AwsCredentialProviderImpl::createProviderForProfile(const std::string & profile)
311360
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderProfile(profileConfig, allocator);
312361
});
313362

314-
// 4. IMDS provider (for EC2 instances, lowest priority)
315-
addProviderToChain("IMDS", [&]() {
316-
Aws::Crt::Auth::CredentialsProviderImdsConfig imdsConfig;
317-
imdsConfig.Bootstrap = bootstrap;
318-
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderImds(imdsConfig, allocator);
319-
});
363+
// 4. STS WebIdentity (AWS_WEB_IDENTITY_TOKEN_FILE + AWS_ROLE_ARN — EKS IRSA, GitHub Actions OIDC)
364+
// 5. ECS container metadata (AWS_CONTAINER_CREDENTIALS_RELATIVE_URI — ECS tasks, EKS Pod Identity)
365+
// ECS and IMDS are mutually exclusive per both the aws-c-auth default chain and the
366+
// pre-2.33 aws-sdk-cpp DefaultAWSCredentialsProviderChain: when container credential
367+
// env vars are set, IMDS is skipped so a transient ECS endpoint failure can't silently
368+
// fall through to the (typically broader) EC2 instance profile.
369+
bool ecsAdded = false;
370+
if (tlsContext) {
371+
addProviderToChain("STS WebIdentity", [&]() {
372+
return createSTSWebIdentityProvider(profile, bootstrap, tlsContext.get(), allocator);
373+
});
374+
ecsAdded =
375+
addProviderToChain("ECS", [&]() { return createECSProvider(bootstrap, tlsContext.get(), allocator); });
376+
} else {
377+
debug(
378+
"Skipped AWS STS WebIdentity and ECS Credential Providers for profile '%s': TLS context unavailable",
379+
profileDisplayName);
380+
}
381+
382+
// 6. IMDS provider (for EC2 instances, lowest priority) — only if ECS didn't claim the slot
383+
if (!ecsAdded) {
384+
addProviderToChain("IMDS", [&]() {
385+
Aws::Crt::Auth::CredentialsProviderImdsConfig imdsConfig;
386+
imdsConfig.Bootstrap = bootstrap;
387+
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderImds(imdsConfig, allocator);
388+
});
389+
} else {
390+
debug(
391+
"Skipped AWS IMDS Credential Provider for profile '%s': ECS provider is active (mutually exclusive)",
392+
profileDisplayName);
393+
}
320394

321395
return Aws::Crt::Auth::CredentialsProvider::CreateCredentialsProviderChain(chainConfig, allocator);
322396
}

src/libstore/remote-fs-accessor.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ std::shared_ptr<SourceAccessor> RemoteFSAccessor::accessObject(const StorePath &
9494

9595
std::optional<SourceAccessor::Stat> RemoteFSAccessor::maybeLstat(const CanonPath & path)
9696
{
97+
if (path.isRoot())
98+
return Stat{.type = tDirectory};
9799
auto res = fetch(path);
98100
return res.first->maybeLstat(res.second);
99101
}

src/libutil/logging.cc

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -150,18 +150,23 @@ class SimpleLogger : public Logger
150150

151151
Verbosity verbosity = lvlInfo;
152152

153-
void writeToStderr(std::string_view s)
153+
static void writeFullLogging(Descriptor fd, std::string_view s)
154154
{
155155
try {
156-
writeFull(getStandardError(), s, false);
156+
writeFull(fd, s, false);
157157
} catch (SystemError & e) {
158-
/* Ignore failing writes to stderr. We need to ignore write
159-
errors to ensure that cleanup code that logs to stderr runs
160-
to completion if the other side of stderr has been closed
161-
unexpectedly. */
158+
/* Ignore failing logging writes. We need to ignore write
159+
errors to ensure that cleanup code that writes logs runs
160+
to completion if the other side of the logging fd has
161+
been closed unexpectedly. */
162162
}
163163
}
164164

165+
void writeToStderr(std::string_view s)
166+
{
167+
writeFullLogging(getStandardError(), s);
168+
}
169+
165170
std::unique_ptr<Logger> makeSimpleLogger(bool printBuildLogs)
166171
{
167172
return std::make_unique<SimpleLogger>(printBuildLogs);
@@ -245,15 +250,15 @@ struct JSONLogger : Logger
245250

246251
void write(const nlohmann::json & json)
247252
{
248-
auto line =
249-
(includeNixPrefix ? "@nix " : "") + json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace);
253+
auto line = (includeNixPrefix ? "@nix " : "")
254+
+ json.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace) + "\n";
250255

251256
/* Acquire a lock to prevent log messages from clobbering each
252257
other. */
253258
try {
254259
auto state(_state.lock());
255260
if (state->enabled)
256-
writeLine(fd, line);
261+
writeFullLogging(fd, line);
257262
} catch (...) {
258263
bool enabled = false;
259264
std::swap(_state.lock()->enabled, enabled);

src/libutil/terminal.cc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ std::string filterANSIEscapes(std::string_view s, bool filterAll, unsigned int w
163163

164164
// Note: this object intentionally leaks to avoid a destructor ordering issue (specifically, ~ProgressBar() calling
165165
// getWindowSize() after windowSize has been destroyed).
166-
static auto * const windowSize = new Sync<std::pair<unsigned short, unsigned short>>({0, 0});
166+
static auto * const windowSize = new Sync<std::pair<unsigned short, unsigned short>>{{0, 0}};
167167

168168
void updateWindowSize()
169169
{

0 commit comments

Comments
 (0)