Skip to content

Commit f48b412

Browse files
sicoyleyaron2
andauthored
feat(kafka): iam roles anywhere + assume role auth profiles (#3606)
Signed-off-by: Samantha Coyle <[email protected]> Co-authored-by: Yaron Schneider <[email protected]>
1 parent 8c02ff3 commit f48b412

File tree

21 files changed

+803
-285
lines changed

21 files changed

+803
-285
lines changed

.build-tools/builtin-authentication-profiles.yaml

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ aws:
33
description: |
44
Authenticate using an Access Key ID and Secret Access Key included in the metadata
55
metadata:
6-
- name: awsRegion
6+
- name: region
77
type: string
88
required: true
99
description: |
@@ -25,8 +25,29 @@ aws:
2525
description: |
2626
AWS session token to use. A session token is only required if you are using
2727
temporary security credentials.
28-
example: '"TOKEN"'
28+
- title: "AWS: Assume IAM Role"
29+
description: |
30+
Assume a specific IAM role. Note: This is only supported for Kafka and PostgreSQL.
31+
metadata:
32+
- name: region
33+
type: string
34+
required: true
35+
description: |
36+
The AWS Region where the AWS resource is deployed to.
37+
example: '"us-east-1"'
38+
- name: assumeRoleArn
39+
type: string
40+
required: false
41+
description: |
42+
IAM role that has access to AWS resource.
43+
This is another option to authenticate with MSK and RDS Aurora aside from the AWS Credentials.
44+
example: '"arn:aws:iam::123456789:role/mskRole"'
45+
- name: sessionName
2946
type: string
47+
description: |
48+
The session name for assuming a role.
49+
example: '"MyAppSession"'
50+
default: '"DaprDefaultSession"'
3051
- title: "AWS: Credentials from Environment Variables"
3152
description: Use AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY from the environment
3253
- title: "AWS: IAM Roles Anywhere"

.build-tools/pkg/metadataschema/builtin-authentication-profiles.go

Lines changed: 73 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,46 @@ func ParseBuiltinAuthenticationProfile(bi BuiltinAuthenticationProfile, componen
3232
for i, profile := range profiles {
3333
res[i] = profile
3434

35-
res[i].Metadata = mergedMetadata(bi.Metadata, res[i].Metadata...)
35+
// convert slice to a slice of pointers to update in place for required -> non-required fields
36+
metadataPtr := make([]*Metadata, len(profile.Metadata))
37+
for j := range profile.Metadata {
38+
metadataPtr[j] = &profile.Metadata[j]
39+
}
40+
41+
if componentTitle == "Apache Kafka" {
42+
removeRequiredOnSomeAWSFields(&metadataPtr)
43+
}
44+
45+
// convert back to value slices for merging
46+
updatedMetadata := make([]Metadata, 0, len(metadataPtr))
47+
for _, ptr := range metadataPtr {
48+
if ptr != nil {
49+
updatedMetadata = append(updatedMetadata, *ptr)
50+
}
51+
}
52+
53+
merged := mergedMetadata(bi.Metadata, updatedMetadata...)
54+
55+
// Note: We must apply the removal of deprecated fields after the merge!!
3656

37-
// If component is PostgreSQL, filter out duplicated aws profile fields
38-
if strings.ToLower(componentTitle) == "postgresql" && bi.Name == "aws" {
39-
res[i].Metadata = filterOutDuplicateFields(res[i].Metadata)
57+
// Here, we remove some deprecated fields as we support the transition to a new auth profile
58+
if profile.Title == "AWS: Assume specific IAM Role" && componentTitle == "Apache Kafka" {
59+
merged = removeSomeDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
4060
}
4161

62+
// Here, there are no metadata fields that need deprecating
63+
if profile.Title == "AWS: Credentials from Environment Variables" && componentTitle == "Apache Kafka" {
64+
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
65+
}
66+
67+
// Here, this is a new auth profile, so rm all deprecating fields as unrelated.
68+
if profile.Title == "AWS: IAM Roles Anywhere" && componentTitle == "Apache Kafka" {
69+
merged = removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(merged)
70+
}
71+
72+
res[i].Metadata = merged
4273
}
74+
4375
return res, nil
4476
}
4577

@@ -54,26 +86,49 @@ func mergedMetadata(base []Metadata, add ...Metadata) []Metadata {
5486
return res
5587
}
5688

57-
// filterOutDuplicateFields removes specific duplicated fields from the metadata
58-
func filterOutDuplicateFields(metadata []Metadata) []Metadata {
59-
duplicateFields := map[string]int{
60-
"awsRegion": 0,
61-
"accessKey": 0,
62-
"secretKey": 0,
89+
// removeRequiredOnSomeAWSFields needs to be removed in Dapr 1.17 as duplicated AWS IAM fields get removed,
90+
// and we standardize on these fields.
91+
// Currently, there are: awsAccessKey, accessKey and awsSecretKey, secretKey, and awsRegion and region fields.
92+
// We normally have accessKey, secretKey, and region fields marked required as it is part of the builtin AWS auth profile fields.
93+
// However, as we rm the aws prefixed ones, we need to then mark the normally required ones as not required only for postgres and kafka.
94+
// This way we do not break existing users, and transition them to the standardized fields.
95+
func removeRequiredOnSomeAWSFields(metadata *[]*Metadata) {
96+
if metadata == nil {
97+
return
6398
}
6499

65-
filteredMetadata := []Metadata{}
100+
for _, field := range *metadata {
101+
if field == nil {
102+
continue
103+
}
104+
105+
if field.Name == "accessKey" || field.Name == "secretKey" || field.Name == "region" {
106+
field.Required = false
107+
}
108+
}
109+
}
66110

111+
func removeAllDeprecatedFieldsOnUnrelatedAuthProfiles(metadata []Metadata) []Metadata {
112+
filteredMetadata := []Metadata{}
67113
for _, field := range metadata {
68-
if _, exists := duplicateFields[field.Name]; !exists {
114+
if strings.HasPrefix(field.Name, "aws") {
115+
continue
116+
} else {
69117
filteredMetadata = append(filteredMetadata, field)
118+
}
119+
}
120+
121+
return filteredMetadata
122+
}
123+
124+
func removeSomeDeprecatedFieldsOnUnrelatedAuthProfiles(metadata []Metadata) []Metadata {
125+
filteredMetadata := []Metadata{}
126+
127+
for _, field := range metadata {
128+
if field.Name == "awsAccessKey" || field.Name == "awsSecretKey" || field.Name == "awsSessionToken" {
129+
continue
70130
} else {
71-
if field.Name == "awsRegion" && duplicateFields["awsRegion"] == 0 {
72-
filteredMetadata = append(filteredMetadata, field)
73-
duplicateFields["awsRegion"]++
74-
} else if field.Name != "awsRegion" {
75-
continue
76-
}
131+
filteredMetadata = append(filteredMetadata, field)
77132
}
78133
}
79134

bindings/aws/sns/sns.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ type snsMetadata struct {
4343
SessionToken string `json:"sessionToken" mapstructure:"sessionToken" mdignore:"true"`
4444

4545
TopicArn string `json:"topicArn"`
46+
// TODO: in Dapr 1.17 rm the alias on region as we remove the aws prefix on these fields
4647
Region string `json:"region" mapstructure:"region" mapstructurealiases:"awsRegion" mdignore:"true"`
4748
Endpoint string `json:"endpoint"`
4849
}

bindings/kafka/metadata.yaml

Lines changed: 69 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,75 @@ binding:
1414
operations:
1515
- name: create
1616
description: "Publish a new message in the topic."
17+
# This auth profile has duplicate fields intentionally as we maintain backwards compatibility,
18+
# but also move Kafka to utilize the noramlized AWS fields in the builtin auth profiles.
19+
# TODO: rm the duplicate aws prefixed fields in Dapr 1.17.
20+
builtinAuthenticationProfiles:
21+
- name: "aws"
22+
metadata:
23+
- name: authType
24+
type: string
25+
required: true
26+
description: |
27+
Authentication type.
28+
This must be set to "awsiam" for this authentication profile.
29+
example: '"awsiam"'
30+
allowedValues:
31+
- "awsiam"
32+
- name: awsRegion
33+
type: string
34+
required: false
35+
description: |
36+
This maintains backwards compatibility with existing fields.
37+
It will be deprecated as of Dapr 1.17. Use 'region' instead.
38+
The AWS Region where the AWS Relational Database Service is deployed to.
39+
example: '"us-east-1"'
40+
- name: awsAccessKey
41+
type: string
42+
required: false
43+
description: |
44+
This maintains backwards compatibility with existing fields.
45+
It will be deprecated as of Dapr 1.17. Use 'accessKey' instead.
46+
If both fields are set, then 'accessKey' value will be used.
47+
AWS access key associated with an IAM account.
48+
example: '"AKIAIOSFODNN7EXAMPLE"'
49+
- name: awsSecretKey
50+
type: string
51+
required: false
52+
sensitive: true
53+
description: |
54+
This maintains backwards compatibility with existing fields.
55+
It will be deprecated as of Dapr 1.17. Use 'secretKey' instead.
56+
If both fields are set, then 'secretKey' value will be used.
57+
The secret key associated with the access key.
58+
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
59+
- name: awsSessionToken
60+
type: string
61+
sensitive: true
62+
description: |
63+
This maintains backwards compatibility with existing fields.
64+
It will be deprecated as of Dapr 1.17. Use 'sessionToken' instead.
65+
If both fields are set, then 'sessionToken' value will be used.
66+
AWS session token to use. A session token is only required if you are using temporary security credentials.
67+
example: '"TOKEN"'
68+
- name: awsIamRoleArn
69+
type: string
70+
required: false
71+
description: |
72+
This maintains backwards compatibility with existing fields.
73+
It will be deprecated as of Dapr 1.17. Use 'assumeRoleArn' instead.
74+
If both fields are set, then 'assumeRoleArn' value will be used.
75+
IAM role that has access to MSK. This is another option to authenticate with MSK aside from the AWS Credentials.
76+
example: '"arn:aws:iam::123456789:role/mskRole"'
77+
- name: awsStsSessionName
78+
type: string
79+
description: |
80+
This maintains backwards compatibility with existing fields.
81+
It will be deprecated as of Dapr 1.17. Use 'sessionName' instead.
82+
If both fields are set, then 'sessionName' value will be used.
83+
Represents the session name for assuming a role.
84+
example: '"MyAppSession"'
85+
default: '"MSKSASLDefaultSession"'
1786
authenticationProfiles:
1887
- title: "OIDC Authentication"
1988
description: |
@@ -139,55 +208,6 @@ authenticationProfiles:
139208
example: '"none"'
140209
allowedValues:
141210
- "none"
142-
- title: "AWS IAM"
143-
description: "Authenticate using AWS IAM credentials or role for AWS MSK"
144-
metadata:
145-
- name: authType
146-
type: string
147-
required: true
148-
description: |
149-
Authentication type.
150-
This must be set to "awsiam" for this authentication profile.
151-
example: '"awsiam"'
152-
allowedValues:
153-
- "awsiam"
154-
- name: awsRegion
155-
type: string
156-
required: true
157-
description: |
158-
The AWS Region where the MSK Kafka broker is deployed to.
159-
example: '"us-east-1"'
160-
- name: awsAccessKey
161-
type: string
162-
required: true
163-
description: |
164-
AWS access key associated with an IAM account.
165-
example: '"AKIAIOSFODNN7EXAMPLE"'
166-
- name: awsSecretKey
167-
type: string
168-
required: true
169-
sensitive: true
170-
description: |
171-
The secret key associated with the access key.
172-
example: '"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY"'
173-
- name: awsSessionToken
174-
type: string
175-
sensitive: true
176-
description: |
177-
AWS session token to use. A session token is only required if you are using temporary security credentials.
178-
example: '"TOKEN"'
179-
- name: awsIamRoleArn
180-
type: string
181-
required: true
182-
description: |
183-
IAM role that has access to MSK. This is another option to authenticate with MSK aside from the AWS Credentials.
184-
example: '"arn:aws:iam::123456789:role/mskRole"'
185-
- name: awsStsSessionName
186-
type: string
187-
description: |
188-
Represents the session name for assuming a role.
189-
example: '"MyAppSession"'
190-
default: '"MSKSASLDefaultSession"'
191211
metadata:
192212
- name: topics
193213
type: string

common/authentication/aws/aws.go

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,17 @@ type AWSIAM struct {
4444
AWSRegion string `json:"awsRegion" mapstructure:"awsRegion"`
4545
}
4646

47+
// TODO: Delete in Dapr 1.17 so we can move all IAM fields to use the defaults of:
48+
// accessKey and secretKey and region as noted in the docs, and Options struct above.
49+
type DeprecatedKafkaIAM struct {
50+
Region string `json:"awsRegion" mapstructure:"awsRegion"`
51+
AccessKey string `json:"awsAccessKey" mapstructure:"awsAccessKey"`
52+
SecretKey string `json:"awsSecretKey" mapstructure:"awsSecretKey"`
53+
SessionToken string `json:"awsSessionToken" mapstructure:"awsSessionToken"`
54+
IamRoleArn string `json:"awsIamRoleArn" mapstructure:"awsIamRoleArn"`
55+
StsSessionName string `json:"awsStsSessionName" mapstructure:"awsStsSessionName"`
56+
}
57+
4758
type AWSIAMAuthOptions struct {
4859
PoolConfig *pgxpool.Config `json:"poolConfig" mapstructure:"poolConfig"`
4960
ConnectionString string `json:"connectionString" mapstructure:"connectionString"`
@@ -59,9 +70,13 @@ type Options struct {
5970
PoolConfig *pgxpool.Config `json:"poolConfig" mapstructure:"poolConfig"`
6071
ConnectionString string `json:"connectionString" mapstructure:"connectionString"`
6172

62-
Region string `json:"region" mapstructure:"region"`
63-
AccessKey string `json:"accessKey" mapstructure:"accessKey"`
64-
SecretKey string `json:"secretKey" mapstructure:"secretKey"`
73+
// TODO: in Dapr 1.17 rm the alias on regions as we rm the aws prefixed one.
74+
// Docs have it just as region, but most metadata fields show the aws prefix...
75+
Region string `json:"region" mapstructure:"region" mapstructurealiases:"awsRegion"`
76+
AccessKey string `json:"accessKey" mapstructure:"accessKey"`
77+
SecretKey string `json:"secretKey" mapstructure:"secretKey"`
78+
SessionName string `mapstructure:"sessionName"`
79+
AssumeRoleARN string `mapstructure:"assumeRoleArn"`
6580

6681
Endpoint string
6782
SessionToken string
@@ -80,6 +95,7 @@ func GetConfig(opts Options) *aws.Config {
8095
return cfg
8196
}
8297

98+
//nolint:interfacebloat
8399
type Provider interface {
84100
S3() *S3Clients
85101
DynamoDB() *DynamoDBClients
@@ -91,6 +107,8 @@ type Provider interface {
91107
Kinesis() *KinesisClients
92108
Ses() *SesClients
93109

110+
Kafka(KafkaOptions) (*KafkaClients, error)
111+
94112
Close() error
95113
}
96114

@@ -179,3 +197,14 @@ func (opts *Options) InitiateAWSIAMAuth() error {
179197

180198
return nil
181199
}
200+
201+
// Coalesce is a helper function to return the first non-empty string from the inputs
202+
// This helps us to migrate away from the deprecated duplicate aws auth profile metadata fields in Dapr 1.17.
203+
func Coalesce(values ...string) string {
204+
for _, v := range values {
205+
if v != "" {
206+
return v
207+
}
208+
}
209+
return ""
210+
}

0 commit comments

Comments
 (0)