Skip to content

Commit 3b749af

Browse files
authored
feat(google): support aws external account sources (#728)
Google external account credentials also support the AWS-specific `credential_source` shape from AIP-4117. This teaches `reqsign-google` to resolve `aws1` region and credentials from environment variables or IMDS, mint the signed `GetCallerIdentity` subject token, and exchange it through the existing STS flow. This follows the executable `external_account` support in #727 and keeps the change fully inside `services/google`.
1 parent bf20a01 commit 3b749af

File tree

3 files changed

+700
-2
lines changed

3 files changed

+700
-2
lines changed

services/google/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ http = { workspace = true }
3232
jsonwebtoken = { workspace = true }
3333
log = { workspace = true }
3434
percent-encoding = { workspace = true }
35+
reqsign-aws-v4 = { workspace = true }
3536
reqsign-core = { workspace = true }
3637
rsa = { workspace = true }
3738
serde = { workspace = true }

services/google/src/credential.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ pub mod external_account {
100100
#[derive(Clone, Deserialize, Debug)]
101101
#[serde(untagged)]
102102
pub enum Source {
103+
/// AWS provider-specific credential source.
104+
#[serde(rename_all = "snake_case")]
105+
Aws(AwsSource),
103106
/// URL-based credential source.
104107
#[serde(rename_all = "snake_case")]
105108
Url(UrlSource),
@@ -133,6 +136,22 @@ pub mod external_account {
133136
pub format: Format,
134137
}
135138

139+
/// Configuration for AWS provider-specific workload identity federation.
140+
#[derive(Clone, Deserialize, Debug)]
141+
#[serde(rename_all = "snake_case")]
142+
pub struct AwsSource {
143+
/// The environment identifier, currently `aws1`.
144+
pub environment_id: String,
145+
/// Metadata URL used to derive the region when env vars are absent.
146+
pub region_url: Option<String>,
147+
/// Metadata URL used to retrieve the role name and credentials.
148+
pub url: Option<String>,
149+
/// Regional GetCallerIdentity verification URL template.
150+
pub regional_cred_verification_url: String,
151+
/// Optional IMDSv2 token URL.
152+
pub imdsv2_session_token_url: Option<String>,
153+
}
154+
136155
/// Configuration for executing a command to load credentials.
137156
#[derive(Clone, Deserialize, Debug)]
138157
#[serde(rename_all = "snake_case")]
@@ -407,6 +426,48 @@ mod tests {
407426
let cred = CredentialFile::from_slice(ea_json.as_bytes()).unwrap();
408427
assert!(matches!(cred, CredentialFile::ExternalAccount(_)));
409428

429+
let aws_ea_json = r#"{
430+
"type": "external_account",
431+
"audience": "test_audience",
432+
"subject_token_type": "urn:ietf:params:aws:token-type:aws4_request",
433+
"token_url": "https://example.com/token",
434+
"credential_source": {
435+
"environment_id": "aws1",
436+
"region_url": "http://169.254.169.254/latest/meta-data/placement/availability-zone",
437+
"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials",
438+
"regional_cred_verification_url": "https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15",
439+
"imdsv2_session_token_url": "http://169.254.169.254/latest/api/token"
440+
}
441+
}"#;
442+
let cred = CredentialFile::from_slice(aws_ea_json.as_bytes()).unwrap();
443+
match cred {
444+
CredentialFile::ExternalAccount(external_account) => match external_account
445+
.credential_source
446+
{
447+
external_account::Source::Aws(source) => {
448+
assert_eq!(source.environment_id, "aws1");
449+
assert_eq!(
450+
source.region_url.as_deref(),
451+
Some("http://169.254.169.254/latest/meta-data/placement/availability-zone")
452+
);
453+
assert_eq!(
454+
source.url.as_deref(),
455+
Some("http://169.254.169.254/latest/meta-data/iam/security-credentials")
456+
);
457+
assert_eq!(
458+
source.regional_cred_verification_url,
459+
"https://sts.{region}.amazonaws.com?Action=GetCallerIdentity&Version=2011-06-15"
460+
);
461+
assert_eq!(
462+
source.imdsv2_session_token_url.as_deref(),
463+
Some("http://169.254.169.254/latest/api/token")
464+
);
465+
}
466+
_ => panic!("Expected Aws source"),
467+
},
468+
_ => panic!("Expected ExternalAccount"),
469+
}
470+
410471
let exec_ea_json = r#"{
411472
"type": "external_account",
412473
"audience": "test_audience",

0 commit comments

Comments
 (0)