Skip to content

Commit

Permalink
MI fixes (#44483)
Browse files Browse the repository at this point in the history
  • Loading branch information
christothes authored Jun 10, 2024
1 parent 3d4982c commit 9279a4f
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 7 deletions.
16 changes: 9 additions & 7 deletions sdk/identity/Azure.Identity/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
# Release History

## 1.12.0-beta.3 (Unreleased)
## 1.12.0-beta.3 (2024-06-11)

### Features Added
- `OnBehalfOfCredential` now supports client assertion callbacks for acquiring tokens on behalf of a user.
- All credentials now support setting RefreshOn value if received from MSAL.
- ManagedIdentityCredential sets RefreshOn value of half the token lifetime for AccessTokens with an ExpiresOn value greater than 2 hours in the future.

### Breaking Changes

### Bugs Fixed
- Managed identity bug fixes.

### Other Changes

## 1.11.3 (2024-05-07)
## 1.11.4 (2024-06-10)

### Bugs Fixed
- Fixed a regression in `DefaultAzureCredential` probe request behavior for IMDS managed identity environments. [#43796](https://github.com/Azure/azure-sdk-for-net/issues/43796)
- Managed identity bug fixes.

## 1.12.0-beta.2 (2024-05-07)

Expand All @@ -27,6 +24,11 @@
### Bugs Fixed
- Fixed a regression in `DefaultAzureCredential` probe request behavior for IMDS managed identity environments. [#43796](https://github.com/Azure/azure-sdk-for-net/issues/43796)

## 1.11.3 (2024-05-07)

### Bugs Fixed
- Fixed a regression in `DefaultAzureCredential` probe request behavior for IMDS managed identity environments. [#43796](https://github.com/Azure/azure-sdk-for-net/issues/43796)

## 1.12.0-beta.1 (2024-04-23)

### Bugs Fixed
Expand Down
37 changes: 37 additions & 0 deletions sdk/identity/Azure.Identity/src/AzureArcManagedIdentitySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,9 @@ protected override async ValueTask<AccessToken> HandleResponseAsync(bool async,
{
throw new AuthenticationFailedException(InvalidChallangeErrorMessage);
}
string filePath = splitChallenge[1];

ValidatePath(filePath);
var authHeaderValue = "Basic " + File.ReadAllText(splitChallenge[1]);

using Request request = CreateRequest(context.Scopes);
Expand All @@ -112,5 +114,40 @@ protected override async ValueTask<AccessToken> HandleResponseAsync(bool async,

return await base.HandleResponseAsync(async, context, message, cancellationToken).ConfigureAwait(false);
}

private void ValidatePath(string filePath)
{
// check that the file ends with '.key'
if (!filePath.EndsWith(".key"))
{
throw new AuthenticationFailedException("The secret key file failed validation. File name is invalid.");
}
// if the current platform is windows check that the file is in the path %ProgramData%\AzureConnectedMachineAgent\Tokens
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
var programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData);
var expectedPath = Path.Combine(programData, "AzureConnectedMachineAgent", "Tokens");
if (!filePath.StartsWith(expectedPath))
{
throw new AuthenticationFailedException("The secret key file failed validation. File path is invalid.");
}
}

// if the current platform is linux check that the file is in the path /var/opt/azcmagent/tokens
if (Environment.OSVersion.Platform == PlatformID.Unix)
{
var expectedPath = Path.Combine("/", "var", "opt", "azcmagent", "tokens");
if (!filePath.StartsWith(expectedPath))
{
throw new AuthenticationFailedException("The secret key file failed validation. File path is invalid.");
}
}

// Check that the file length is no larger than 4096 bytes
if (new FileInfo(filePath).Length > 4096)
{
throw new AuthenticationFailedException("The secret key file failed validation. File is too large.");
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public ManagedIdentityCredentialArcLiveTests(bool isAsync) : base(isAsync)

[NonParallelizable]
[Test]
[LiveOnly(Reason = "path validation fails in playback mode")]
public async Task ValidateSystemAssignedIdentity()
{
if (string.IsNullOrEmpty(TestEnvironment.ArcEnable))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,80 @@ public async Task VerifyAuthenticationFailedExceptionsAreDeferredToGetToken(Dict
await Task.CompletedTask;
}

[Test]
public void VerifyArcIdentitySourceFilePathValidation_DoesNotEndInDotKey()
{
using var environment = new TestEnvVar(
new()
{
{ "MSI_ENDPOINT", null },
{ "MSI_SECRET", null },
{ "IDENTITY_ENDPOINT", "https://identity.constoso.com" },
{ "IMDS_ENDPOINT", "https://imds.constoso.com" },
{ "IDENTITY_HEADER", null },
{ "AZURE_POD_IDENTITY_AUTHORITY_HOST", null }
});

var mockTransport = new MockTransport(request =>
{
var response = new MockResponse(401);
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
response.AddHeader("WWW-Authenticate", "file=c:\\ProgramData\\AzureConnectedMachineAgent\\Tokens\\secret.foo");
}
else
{
response.AddHeader("WWW-Authenticate", "file=/var/opt/azcmagent/tokens/secret.foo");
}
return response;
});
var options = new TokenCredentialOptions() { Transport = mockTransport };
options.Retry.MaxDelay = TimeSpan.Zero;
var pipeline = CredentialPipeline.GetInstance(options);

ManagedIdentityCredential credential = InstrumentClient(new ManagedIdentityCredential(null, pipeline));

var ex = Assert.ThrowsAsync<AuthenticationFailedException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
Assert.That(ex.Message, Does.Contain("File name is invalid."));
}

[Test]
public void VerifyArcIdentitySourceFilePathValidation_FilePathInvalid()
{
using var environment = new TestEnvVar(
new()
{
{ "MSI_ENDPOINT", null },
{ "MSI_SECRET", null },
{ "IDENTITY_ENDPOINT", "https://identity.constoso.com" },
{ "IMDS_ENDPOINT", "https://imds.constoso.com" },
{ "IDENTITY_HEADER", null },
{ "AZURE_POD_IDENTITY_AUTHORITY_HOST", null }
});

var mockTransport = new MockTransport(request =>
{
var response = new MockResponse(401);
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
{
response.AddHeader("WWW-Authenticate", "file=c:\\ProgramData\\bugus\\AzureConnectedMachineAgent\\Tokens\\secret.key");
}
else
{
response.AddHeader("WWW-Authenticate", "file=/var/opt/bogus/azcmagent/tokens/secret.key");
}
return response;
});
var options = new TokenCredentialOptions() { Transport = mockTransport };
options.Retry.MaxDelay = TimeSpan.Zero;
var pipeline = CredentialPipeline.GetInstance(options);

ManagedIdentityCredential credential = InstrumentClient(new ManagedIdentityCredential(null, pipeline));

var ex = Assert.ThrowsAsync<AuthenticationFailedException>(async () => await credential.GetTokenAsync(new TokenRequestContext(MockScopes.Default)));
Assert.That(ex.Message, Does.Contain("File path is invalid."));
}

private static IEnumerable<TestCaseData> ResourceAndClientIds()
{
yield return new TestCaseData(new object[] { null, false });
Expand Down

0 comments on commit 9279a4f

Please sign in to comment.