Skip to content

Commit 689a79e

Browse files
feat: add support for external_id in AWS assume role (vectordotdev#17743)
aws assume role did not have support for specifying external id which is quintessential for security concerned consumers. P.S. [contributing guidelines](https://github.com/vectordotdev/vector/blob/44be37843c0599abb64073fe737ce146e30b3aa5/CONTRIBUTING.md) is empty, help me if I'm missing anything. Closes: vectordotdev#17739 --------- Co-authored-by: Spencer Gilbert <[email protected]>
1 parent 983a92a commit 689a79e

11 files changed

+162
-1
lines changed

src/aws/auth.rs

+70-1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ pub enum AwsAuthentication {
7979
#[configurable(metadata(docs::examples = "arn:aws:iam::123456789098:role/my_role"))]
8080
assume_role: Option<String>,
8181

82+
/// The optional unique external ID in conjunction with role to assume.
83+
///
84+
/// [external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
85+
#[configurable(metadata(docs::examples = "randomEXAMPLEidString"))]
86+
external_id: Option<String>,
87+
8288
/// The [AWS region][aws_region] to send STS requests to.
8389
///
8490
/// If not set, this will default to the configured region
@@ -115,6 +121,12 @@ pub enum AwsAuthentication {
115121
#[configurable(metadata(docs::examples = "arn:aws:iam::123456789098:role/my_role"))]
116122
assume_role: String,
117123

124+
/// The optional unique external ID in conjunction with role to assume.
125+
///
126+
/// [external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
127+
#[configurable(metadata(docs::examples = "randomEXAMPLEidString"))]
128+
external_id: Option<String>,
129+
118130
/// Timeout for assuming the role, in seconds.
119131
///
120132
/// Relevant when the default credentials chain or `assume_role` is used.
@@ -199,6 +211,7 @@ impl AwsAuthentication {
199211
access_key_id,
200212
secret_access_key,
201213
assume_role,
214+
external_id,
202215
region,
203216
} => {
204217
let provider = SharedCredentialsProvider::new(Credentials::from_keys(
@@ -208,8 +221,10 @@ impl AwsAuthentication {
208221
));
209222
if let Some(assume_role) = assume_role {
210223
let auth_region = region.clone().map(Region::new).unwrap_or(service_region);
224+
let auth_external_id = external_id.clone().unwrap();
211225
let provider = AssumeRoleProviderBuilder::new(assume_role)
212226
.region(auth_region)
227+
.external_id(auth_external_id)
213228
.build(provider);
214229
return Ok(SharedCredentialsProvider::new(provider));
215230
}
@@ -232,15 +247,17 @@ impl AwsAuthentication {
232247
}
233248
AwsAuthentication::Role {
234249
assume_role,
250+
external_id,
235251
imds,
236252
region,
237253
..
238254
} => {
239255
let auth_region = region.clone().map(Region::new).unwrap_or(service_region);
256+
let auth_external_id = external_id.clone().unwrap();
240257
let provider = AssumeRoleProviderBuilder::new(assume_role)
241258
.region(auth_region.clone())
259+
.external_id(auth_external_id)
242260
.build(default_credentials_provider(auth_region, *imds).await?);
243-
244261
Ok(SharedCredentialsProvider::new(provider))
245262
}
246263
AwsAuthentication::Default { imds, region, .. } => Ok(SharedCredentialsProvider::new(
@@ -259,6 +276,7 @@ impl AwsAuthentication {
259276
access_key_id: "dummy".to_string().into(),
260277
secret_access_key: "dummy".to_string().into(),
261278
assume_role: None,
279+
external_id: None,
262280
region: None,
263281
}
264282
}
@@ -295,6 +313,7 @@ mod tests {
295313
#[derive(Serialize, Deserialize, Clone, Debug)]
296314
struct ComponentConfig {
297315
assume_role: Option<String>,
316+
external_id: Option<String>,
298317
#[serde(default)]
299318
auth: AwsAuthentication,
300319
}
@@ -396,6 +415,20 @@ mod tests {
396415
assert!(matches!(config.auth, AwsAuthentication::Role { .. }));
397416
}
398417

418+
#[test]
419+
fn parsing_external_id_with_assume_role() {
420+
let config = toml::from_str::<ComponentConfig>(
421+
r#"
422+
auth.assume_role = "root"
423+
auth.external_id = "id"
424+
auth.load_timeout_secs = 10
425+
"#,
426+
)
427+
.unwrap();
428+
429+
assert!(matches!(config.auth, AwsAuthentication::Role { .. }));
430+
}
431+
399432
#[test]
400433
fn parsing_assume_role_with_imds_client() {
401434
let config = toml::from_str::<ComponentConfig>(
@@ -411,11 +444,13 @@ mod tests {
411444
match config.auth {
412445
AwsAuthentication::Role {
413446
assume_role,
447+
external_id,
414448
load_timeout_secs,
415449
imds,
416450
region,
417451
} => {
418452
assert_eq!(&assume_role, "root");
453+
assert_eq!(external_id, None);
419454
assert_eq!(load_timeout_secs, None);
420455
assert!(matches!(
421456
imds,
@@ -446,11 +481,13 @@ mod tests {
446481
match config.auth {
447482
AwsAuthentication::Role {
448483
assume_role,
484+
external_id,
449485
load_timeout_secs,
450486
imds,
451487
region,
452488
} => {
453489
assert_eq!(&assume_role, "auth.root");
490+
assert_eq!(external_id, None);
454491
assert_eq!(load_timeout_secs, Some(10));
455492
assert!(matches!(imds, ImdsAuthentication { .. }));
456493
assert_eq!(region.unwrap(), "us-west-2");
@@ -501,6 +538,38 @@ mod tests {
501538
}
502539
}
503540

541+
#[test]
542+
fn parsing_static_with_assume_role_and_external_id() {
543+
let config = toml::from_str::<ComponentConfig>(
544+
r#"
545+
auth.access_key_id = "key"
546+
auth.secret_access_key = "other"
547+
auth.assume_role = "root"
548+
auth.external_id = "id"
549+
"#,
550+
)
551+
.unwrap();
552+
553+
match config.auth {
554+
AwsAuthentication::AccessKey {
555+
access_key_id,
556+
secret_access_key,
557+
assume_role,
558+
external_id,
559+
..
560+
} => {
561+
assert_eq!(&access_key_id, &SensitiveString::from("key".to_string()));
562+
assert_eq!(
563+
&secret_access_key,
564+
&SensitiveString::from("other".to_string())
565+
);
566+
assert_eq!(&assume_role, &Some("root".to_string()));
567+
assert_eq!(&external_id, &Some("id".to_string()));
568+
}
569+
_ => panic!(),
570+
}
571+
}
572+
504573
#[test]
505574
fn parsing_file() {
506575
let config = toml::from_str::<ComponentConfig>(

website/cue/reference/components/sinks/base/aws_cloudwatch_logs.cue

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ base: components: sinks: aws_cloudwatch_logs: configuration: {
5050
required: true
5151
type: string: examples: ["/my/aws/credentials"]
5252
}
53+
external_id: {
54+
description: """
55+
The optional unique external ID in conjunction with role to assume.
56+
57+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
58+
"""
59+
required: false
60+
type: string: examples: ["randomEXAMPLEidString"]
61+
}
5362
imds: {
5463
description: "Configuration for authenticating with AWS through IMDS."
5564
required: false

website/cue/reference/components/sinks/base/aws_cloudwatch_metrics.cue

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ base: components: sinks: aws_cloudwatch_metrics: configuration: {
5050
required: true
5151
type: string: examples: ["/my/aws/credentials"]
5252
}
53+
external_id: {
54+
description: """
55+
The optional unique external ID in conjunction with role to assume.
56+
57+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
58+
"""
59+
required: false
60+
type: string: examples: ["randomEXAMPLEidString"]
61+
}
5362
imds: {
5463
description: "Configuration for authenticating with AWS through IMDS."
5564
required: false

website/cue/reference/components/sinks/base/aws_kinesis_firehose.cue

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ base: components: sinks: aws_kinesis_firehose: configuration: {
5050
required: true
5151
type: string: examples: ["/my/aws/credentials"]
5252
}
53+
external_id: {
54+
description: """
55+
The optional unique external ID in conjunction with role to assume.
56+
57+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
58+
"""
59+
required: false
60+
type: string: examples: ["randomEXAMPLEidString"]
61+
}
5362
imds: {
5463
description: "Configuration for authenticating with AWS through IMDS."
5564
required: false

website/cue/reference/components/sinks/base/aws_kinesis_streams.cue

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ base: components: sinks: aws_kinesis_streams: configuration: {
5050
required: true
5151
type: string: examples: ["/my/aws/credentials"]
5252
}
53+
external_id: {
54+
description: """
55+
The optional unique external ID in conjunction with role to assume.
56+
57+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
58+
"""
59+
required: false
60+
type: string: examples: ["randomEXAMPLEidString"]
61+
}
5362
imds: {
5463
description: "Configuration for authenticating with AWS through IMDS."
5564
required: false

website/cue/reference/components/sinks/base/aws_s3.cue

+9
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,15 @@ base: components: sinks: aws_s3: configuration: {
125125
required: true
126126
type: string: examples: ["/my/aws/credentials"]
127127
}
128+
external_id: {
129+
description: """
130+
The optional unique external ID in conjunction with role to assume.
131+
132+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
133+
"""
134+
required: false
135+
type: string: examples: ["randomEXAMPLEidString"]
136+
}
128137
imds: {
129138
description: "Configuration for authenticating with AWS through IMDS."
130139
required: false

website/cue/reference/components/sinks/base/aws_sqs.cue

+9
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,15 @@ base: components: sinks: aws_sqs: configuration: {
5050
required: true
5151
type: string: examples: ["/my/aws/credentials"]
5252
}
53+
external_id: {
54+
description: """
55+
The optional unique external ID in conjunction with role to assume.
56+
57+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
58+
"""
59+
required: false
60+
type: string: examples: ["randomEXAMPLEidString"]
61+
}
5362
imds: {
5463
description: "Configuration for authenticating with AWS through IMDS."
5564
required: false

website/cue/reference/components/sinks/base/elasticsearch.cue

+10
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,16 @@ base: components: sinks: elasticsearch: configuration: {
7676
required: true
7777
type: string: examples: ["/my/aws/credentials"]
7878
}
79+
external_id: {
80+
description: """
81+
The optional unique external ID in conjunction with role to assume.
82+
83+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
84+
"""
85+
relevant_when: "strategy = \"aws\""
86+
required: false
87+
type: string: examples: ["randomEXAMPLEidString"]
88+
}
7989
imds: {
8090
description: "Configuration for authenticating with AWS through IMDS."
8191
relevant_when: "strategy = \"aws\""

website/cue/reference/components/sinks/base/prometheus_remote_write.cue

+10
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ base: components: sinks: prometheus_remote_write: configuration: {
5353
required: true
5454
type: string: examples: ["/my/aws/credentials"]
5555
}
56+
external_id: {
57+
description: """
58+
The optional unique external ID in conjunction with role to assume.
59+
60+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
61+
"""
62+
relevant_when: "strategy = \"aws\""
63+
required: false
64+
type: string: examples: ["randomEXAMPLEidString"]
65+
}
5666
imds: {
5767
description: "Configuration for authenticating with AWS through IMDS."
5868
relevant_when: "strategy = \"aws\""

website/cue/reference/components/sources/base/aws_s3.cue

+9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ base: components: sources: aws_s3: configuration: {
4545
required: true
4646
type: string: examples: ["/my/aws/credentials"]
4747
}
48+
external_id: {
49+
description: """
50+
The optional unique external ID in conjunction with role to assume.
51+
52+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
53+
"""
54+
required: false
55+
type: string: examples: ["randomEXAMPLEidString"]
56+
}
4857
imds: {
4958
description: "Configuration for authenticating with AWS through IMDS."
5059
required: false

website/cue/reference/components/sources/base/aws_sqs.cue

+9
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,15 @@ base: components: sources: aws_sqs: configuration: {
4545
required: true
4646
type: string: examples: ["/my/aws/credentials"]
4747
}
48+
external_id: {
49+
description: """
50+
The optional unique external ID in conjunction with role to assume.
51+
52+
[external_id]: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_create_for-user_externalid.html
53+
"""
54+
required: false
55+
type: string: examples: ["randomEXAMPLEidString"]
56+
}
4857
imds: {
4958
description: "Configuration for authenticating with AWS through IMDS."
5059
required: false

0 commit comments

Comments
 (0)