Skip to content

Commit cbd6851

Browse files
authored
[CloudConnectors] Implement AWS OIDC chain (#3675)
The OIDC role chaining should go through the "Elastic Global Role" and then assume the remote role. To do that I extended `AWSRoleChainingStep` to support `NewWebIdentityRoleProvider`.
1 parent c63a6aa commit cbd6851

File tree

6 files changed

+274
-57
lines changed

6 files changed

+274
-57
lines changed

internal/config/config.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ const (
254254
CloudConnectorsGlobalRoleEnvVar = "CLOUD_CONNECTORS_GLOBAL_ROLE"
255255
CloudResourceIDEnvVar = "CLOUD_RESOURCE_ID"
256256
CloudConnectorsJWTPathEnvVar = "CLOUD_CONNECTORS_ID_TOKEN_FILE"
257+
CloudConnectorsAWSTokenEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE"
257258
)
258259

259260
type CloudConnectorsConfig struct {

internal/flavors/assetinventory/strategy_test.go

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -39,37 +39,42 @@ func TestStrategyPicks(t *testing.T) {
3939
testCases := []struct {
4040
name string
4141
cfg *config.Config
42+
env map[string]string
4243
expectedErr string
4344
}{
4445
{
45-
"expected error: asset_inventory_provider not set",
46-
&config.Config{},
47-
"missing config.v1.asset_inventory_provider",
46+
name: "expected error: asset_inventory_provider not set",
47+
cfg: &config.Config{},
48+
env: nil,
49+
expectedErr: "missing config.v1.asset_inventory_provider",
4850
},
4951
{
50-
"expected error: unsupported provider",
51-
&config.Config{
52+
name: "expected error: unsupported provider",
53+
cfg: &config.Config{
5254
AssetInventoryProvider: "NOPE",
5355
},
54-
"unsupported Asset Inventory provider \"NOPE\"",
56+
env: nil,
57+
expectedErr: "unsupported Asset Inventory provider \"NOPE\"",
5558
},
5659
{
57-
"expected success: Azure",
58-
&config.Config{
60+
name: "expected success: Azure",
61+
cfg: &config.Config{
5962
AssetInventoryProvider: config.ProviderAzure,
6063
},
61-
"",
64+
env: nil,
65+
expectedErr: "",
6266
},
6367
{
64-
"expected error: GCP missing account type",
65-
&config.Config{
68+
name: "expected error: GCP missing account type",
69+
cfg: &config.Config{
6670
AssetInventoryProvider: config.ProviderGCP,
6771
},
68-
"invalid gcp account type",
72+
env: nil,
73+
expectedErr: "invalid gcp account type",
6974
},
7075
{
71-
"expected success: GCP",
72-
&config.Config{
76+
name: "expected success: GCP",
77+
cfg: &config.Config{
7378
AssetInventoryProvider: config.ProviderGCP,
7479
CloudConfig: config.CloudConfig{
7580
Gcp: config.GcpConfig{
@@ -82,23 +87,25 @@ func TestStrategyPicks(t *testing.T) {
8287
},
8388
},
8489
},
85-
"could not parse key",
90+
env: nil,
91+
expectedErr: "could not parse key",
8692
},
8793
{
88-
"expected error: AWS unsupported account type",
89-
&config.Config{
94+
name: "expected error: AWS unsupported account type",
95+
cfg: &config.Config{
9096
AssetInventoryProvider: config.ProviderAWS,
9197
CloudConfig: config.CloudConfig{
9298
Aws: config.AwsConfig{
9399
AccountType: "NOPE",
94100
},
95101
},
96102
},
97-
"unsupported account_type: \"NOPE\"",
103+
env: nil,
104+
expectedErr: "unsupported account_type: \"NOPE\"",
98105
},
99106
{
100-
"expected success: AWS",
101-
&config.Config{
107+
name: "expected success: AWS",
108+
cfg: &config.Config{
102109
AssetInventoryProvider: config.ProviderAWS,
103110
CloudConfig: config.CloudConfig{
104111
Aws: config.AwsConfig{
@@ -110,11 +117,12 @@ func TestStrategyPicks(t *testing.T) {
110117
},
111118
},
112119
},
113-
"STS: GetCallerIdentity",
120+
env: nil,
121+
expectedErr: "STS: GetCallerIdentity",
114122
},
115123
{
116-
"expected success: AWS with cloud connectors",
117-
&config.Config{
124+
name: "expected success: AWS with cloud connectors",
125+
cfg: &config.Config{
118126
AssetInventoryProvider: config.ProviderAWS,
119127
CloudConfig: config.CloudConfig{
120128
Aws: config.AwsConfig{
@@ -132,12 +140,19 @@ func TestStrategyPicks(t *testing.T) {
132140
},
133141
},
134142
},
135-
"STS: GetCallerIdentity",
143+
env: map[string]string{
144+
"AWS_WEB_IDENTITY_TOKEN_FILE": "/tmp/fake-token-file",
145+
"AWS_ROLE_ARN": "arn:aws:iam::123456789012:role/test-local-role",
146+
},
147+
expectedErr: "STS: GetCallerIdentity",
136148
},
137149
}
138150

139151
for _, tc := range testCases {
140152
t.Run(tc.name, func(t *testing.T) {
153+
for k, v := range tc.env {
154+
t.Setenv(k, v)
155+
}
141156
s := strategy{
142157
logger: testhelper.NewLogger(t),
143158
cfg: tc.cfg,

internal/flavors/benchmark/aws_test.go

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ func TestAWS_Initialize(t *testing.T) {
4040

4141
tests := []struct {
4242
name string
43+
envsVars map[string]string
4344
identityProvider awslib.IdentityProviderGetter
4445
cfg config.Config
4546
want []string
@@ -69,7 +70,12 @@ func TestAWS_Initialize(t *testing.T) {
6970
},
7071
},
7172
{
72-
name: "cloud connectors",
73+
name: "cloud connectors irsa",
74+
envsVars: map[string]string{
75+
config.CloudConnectorsAWSTokenEnvVar: "/abc",
76+
"AWS_REGION": "eu-west-1",
77+
"AWS_ROLE_ARN": "arn:aws:iam::111111111111:role/localrole",
78+
},
7379
cfg: config.Config{
7480
Benchmark: "cis_aws",
7581
CloudConfig: config.CloudConfig{
@@ -113,6 +119,51 @@ func TestAWS_Initialize(t *testing.T) {
113119
fetching.S3Type,
114120
},
115121
},
122+
{
123+
name: "cloud connectors id token",
124+
envsVars: map[string]string{config.CloudConnectorsJWTPathEnvVar: "/abc"},
125+
cfg: config.Config{
126+
Benchmark: "cis_aws",
127+
CloudConfig: config.CloudConfig{
128+
Aws: config.AwsConfig{
129+
AccountType: config.SingleAccount,
130+
Cred: libbeataws.ConfigAWS{},
131+
CloudConnectors: true,
132+
CloudConnectorsConfig: config.CloudConnectorsConfig{
133+
GlobalRoleARN: "abc456",
134+
ResourceID: "abc789",
135+
},
136+
},
137+
},
138+
},
139+
identityProvider: func() awslib.IdentityProviderGetter {
140+
cfgMatcher := mock.MatchedBy(func(cfg aws.Config) bool {
141+
c, is := cfg.Credentials.(*aws.CredentialsCache)
142+
if !is {
143+
return false
144+
}
145+
return c.IsCredentialsProvider(&stscreds.AssumeRoleProvider{})
146+
})
147+
identityProvider := &awslib.MockIdentityProviderGetter{}
148+
identityProvider.EXPECT().GetIdentity(mock.Anything, cfgMatcher).Return(
149+
&cloud.Identity{
150+
Account: "test-account",
151+
},
152+
nil,
153+
)
154+
155+
return identityProvider
156+
}(),
157+
want: []string{
158+
fetching.IAMType,
159+
fetching.KmsType,
160+
fetching.TrailType,
161+
fetching.AwsMonitoringType,
162+
fetching.EC2NetworkingType,
163+
fetching.RdsType,
164+
fetching.S3Type,
165+
},
166+
},
116167
{
117168
name: "no credential cache in non cloud connectors setup",
118169
cfg: config.Config{
@@ -156,7 +207,10 @@ func TestAWS_Initialize(t *testing.T) {
156207
}
157208
for _, tt := range tests {
158209
t.Run(tt.name, func(t *testing.T) {
159-
t.Parallel()
210+
// t.Parallel() // cannot be used with t.Setenv
211+
for k, v := range tt.envsVars {
212+
t.Setenv(k, v)
213+
}
160214

161215
testInitialize(t, &AWS{
162216
IdentityProvider: tt.identityProvider,

internal/flavors/benchmark/strategy_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ func testInitialize(t *testing.T, s benchInit, cfg *config.Config, wantErr strin
115115
require.ErrorContains(t, err, wantErr)
116116
return
117117
}
118+
118119
reg.Update(t.Context())
119120
defer reg.Stop()
120121

0 commit comments

Comments
 (0)