Skip to content

Commit 635d2ed

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 635d2ed

1 file changed

Lines changed: 48 additions & 3 deletions

File tree

  • rust/cubestore/cubestore/src/remotefs

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

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,15 +94,45 @@ 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 is_web_identity = access_key.is_none()
104+
&& env::var("AWS_WEB_IDENTITY_TOKEN_FILE").is_ok();
105+
let token_file = if is_web_identity {
106+
env::var("AWS_WEB_IDENTITY_TOKEN_FILE").ok()
107+
} else {
108+
None
109+
};
110+
111+
let refresh_every = {
112+
let configured = refresh_interval_from_env();
113+
if is_web_identity && configured == Duration::from_secs(60 * 180) {
114+
// Operator didn't set CUBESTORE_AWS_CREDS_REFRESH_EVERY_MINS —
115+
// use 30s default for web identity instead of the 3-hour static default.
116+
Duration::from_secs(30)
117+
} else {
118+
configured
119+
}
120+
};
121+
99122
if refresh_every.as_secs() == 0 {
100123
return;
101124
}
102125

103126
let fs = Arc::downgrade(fs);
127+
let mut last_modified = token_file
128+
.as_ref()
129+
.and_then(|f| std::fs::metadata(f).ok()?.modified().ok());
130+
104131
std::thread::spawn(move || {
105-
log::debug!("Started S3 credentials refresh loop");
132+
log::debug!(
133+
"Started S3 credentials refresh loop (web_identity={})",
134+
is_web_identity
135+
);
106136
loop {
107137
std::thread::sleep(refresh_every);
108138
let fs = match fs.upgrade() {
@@ -112,6 +142,21 @@ fn spawn_creds_refresh_loop(
112142
}
113143
Some(fs) => fs,
114144
};
145+
146+
// In web identity mode, only refresh when the token file changed.
147+
// token_file is None when not in web identity mode, so this block
148+
// never activates for static credentials.
149+
if let Some(ref file) = token_file {
150+
let current_modified = std::fs::metadata(file)
151+
.ok()
152+
.and_then(|m| m.modified().ok());
153+
if current_modified == last_modified {
154+
continue;
155+
}
156+
last_modified = current_modified;
157+
info!("Web identity token file changed, refreshing S3 credentials");
158+
}
159+
115160
let c = match Credentials::new(
116161
access_key.as_deref(),
117162
secret_key.as_deref(),

0 commit comments

Comments
 (0)