Skip to content

Commit 2d49440

Browse files
committed
accept ACI's non-JSON response to IMDS probe
1 parent beafa24 commit 2d49440

File tree

3 files changed

+29
-4
lines changed

3 files changed

+29
-4
lines changed

sdk/azidentity/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
### Bugs Fixed
1010
* User credential types inconsistently log access token scopes
11+
* `DefaultAzureCredential` skips managed identity in Azure Container Instances
1112

1213
### Other Changes
1314

sdk/azidentity/default_azure_credential_test.go

+21
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,27 @@ func TestDefaultAzureCredential_IMDS(t *testing.T) {
357357
require.NoError(t, err, "DefaultAzureCredential should continue after receiving a non-JSON response from IMDS")
358358
}
359359
})
360+
361+
t.Run("Azure Container Instances", func(t *testing.T) {
362+
// ensure GetToken returns an error if DefaultAzureCredential skips managed identity
363+
before := defaultAzTokenProvider
364+
defer func() { defaultAzTokenProvider = before }()
365+
defaultAzTokenProvider = mockAzTokenProviderFailure
366+
367+
srv, close := mock.NewTLSServer(mock.WithTransformAllRequestsToTestServerUrl())
368+
defer close()
369+
srv.AppendResponse(mock.WithBody([]byte("Required metadata header not specified or not correct")), mock.WithStatusCode(http.StatusBadRequest))
370+
srv.AppendResponse(mock.WithBody(accessTokenRespSuccess), mock.WithStatusCode(http.StatusOK))
371+
cred, err := NewDefaultAzureCredential(&DefaultAzureCredentialOptions{
372+
ClientOptions: policy.ClientOptions{
373+
Transport: srv,
374+
},
375+
})
376+
require.NoError(t, err)
377+
tk, err := cred.GetToken(ctx, testTRO)
378+
require.NoError(t, err, "DefaultAzureCredential should accept ACI's response to the probe request")
379+
require.Equal(t, tokenValue, tk.Token)
380+
})
360381
})
361382

362383
t.Run("timeout", func(t *testing.T) {

sdk/azidentity/managed_identity_client.go

+7-4
Original file line numberDiff line numberDiff line change
@@ -230,16 +230,19 @@ func (c *managedIdentityClient) authenticate(ctx context.Context, id ManagedIDKi
230230
}
231231
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, msg)
232232
}
233-
// because IMDS always responds with JSON, assume a non-JSON response is from something else, such
234-
// as a proxy, and return credentialUnavailableError so DefaultAzureCredential continues iterating
233+
// Determine whether the response is from IMDS, a service whose managed identity API imitates IMDS such as Azure
234+
// Container Instances (ACI), or something other than a managed identity API. Assume a JSON response is from IMDS.
235+
// If the response is not JSON, check for a known ACI error message. If the response is neither JSON nor an ACI
236+
// error, assume it's from something like a proxy and return credentialUnavailableError so DefaultAzureCredential
237+
// continues to its next credential.
235238
b, err := azruntime.Payload(res)
236239
if err != nil {
237240
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, fmt.Sprintf("failed to read IMDS probe response: %s", err))
238241
}
239-
if !json.Valid(b) {
242+
if !json.Valid(b) && !strings.HasPrefix(string(b), "Required metadata header") {
240243
return azcore.AccessToken{}, newCredentialUnavailableError(credNameManagedIdentity, "unexpected response to IMDS probe")
241244
}
242-
// send normal token requests from now on because IMDS responded
245+
// send normal token requests from now on because IMDS, or something imitating it, responded
243246
c.probeIMDS = false
244247
}
245248

0 commit comments

Comments
 (0)