Skip to content

Commit fc5feed

Browse files
bsod90claude
andcommitted
feat(cubestore): support AWS Web Identity Token File in S3RemoteFs
When CUBESTORE_AWS_ACCESS_KEY_ID is not set and AWS_WEB_IDENTITY_TOKEN_FILE is present, the credential provider chain falls through to STS AssumeRoleWithWebIdentity — reading the JWT from the token file and exchanging it for temporary session credentials. The refresh loop now polls the token file mtime every 30 seconds in web identity mode (vs 3-hour default for static credentials). Credentials are only re-exchanged when the file actually changes, keeping STS calls minimal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 34a78f4 commit fc5feed

1 file changed

Lines changed: 43 additions & 3 deletions

File tree

  • rust/cubestore/cubestore/src/remotefs

rust/cubestore/cubestore/src/remotefs/s3.rs

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,40 @@ fn spawn_creds_refresh_loop(
9494
region: Region,
9595
fs: &Arc<S3RemoteFs>,
9696
) {
97-
// Refresh credentials. TODO: use expiration time.
98-
let refresh_every = refresh_interval_from_env();
97+
// When AWS_WEB_IDENTITY_TOKEN_FILE is set and no explicit access keys are
98+
// provided, the credential chain falls through to STS
99+
// AssumeRoleWithWebIdentity. STS session credentials expire (~1 hour), so
100+
// we poll the token file for changes frequently (default 30s) instead of
101+
// the default 3-hour static-credential refresh.
102+
// CUBESTORE_AWS_CREDS_REFRESH_EVERY_MINS is respected in both modes.
103+
let token_file = env::var("AWS_WEB_IDENTITY_TOKEN_FILE").ok();
104+
let is_web_identity = access_key.is_none() && token_file.is_some();
105+
106+
let refresh_every = {
107+
let configured = refresh_interval_from_env();
108+
if is_web_identity && configured == Duration::from_secs(60 * 180) {
109+
// Operator didn't set CUBESTORE_AWS_CREDS_REFRESH_EVERY_MINS —
110+
// use 30s default for web identity instead of the 3-hour static default.
111+
Duration::from_secs(30)
112+
} else {
113+
configured
114+
}
115+
};
116+
99117
if refresh_every.as_secs() == 0 {
100118
return;
101119
}
102120

103121
let fs = Arc::downgrade(fs);
122+
let mut last_modified = token_file
123+
.as_ref()
124+
.and_then(|f| std::fs::metadata(f).ok()?.modified().ok());
125+
104126
std::thread::spawn(move || {
105-
log::debug!("Started S3 credentials refresh loop");
127+
log::debug!(
128+
"Started S3 credentials refresh loop (web_identity={})",
129+
is_web_identity
130+
);
106131
loop {
107132
std::thread::sleep(refresh_every);
108133
let fs = match fs.upgrade() {
@@ -112,6 +137,21 @@ fn spawn_creds_refresh_loop(
112137
}
113138
Some(fs) => fs,
114139
};
140+
141+
// In web identity mode, only refresh when the token file changed.
142+
if is_web_identity {
143+
if let Some(ref file) = token_file {
144+
let current_modified = std::fs::metadata(file)
145+
.ok()
146+
.and_then(|m| m.modified().ok());
147+
if current_modified == last_modified {
148+
continue;
149+
}
150+
last_modified = current_modified;
151+
info!("Web identity token file changed, refreshing S3 credentials");
152+
}
153+
}
154+
115155
let c = match Credentials::new(
116156
access_key.as_deref(),
117157
secret_key.as_deref(),

0 commit comments

Comments
 (0)