Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding Credential Probe logic #5116

Closed
wants to merge 1 commit into from
Closed

Adding Credential Probe logic #5116

wants to merge 1 commit into from

Conversation

gladjohn
Copy link
Contributor

@gladjohn gladjohn commented Feb 6, 2025

Fixes #5115

Changes proposed in this request
This pull request introduces significant changes to the managed identity authentication process in the Microsoft Identity Client. The changes include adding new classes for handling credential-based managed identity, updating existing methods to support asynchronous operations, and improving logging and error handling.

Key Changes:

New Classes and Methods:

  • CredentialManagedIdentitySource: A new class to handle credential-based managed identity, including a factory method for creating instances and a method to create minimal valid requests.
  • ImdsCredentialProbeManager: A new class to probe the IMDS credential endpoint and evaluate the response to determine if the endpoint is supported.

Asynchronous Operations:

Logging and Error Handling:

  • Enhanced logging in ManagedIdentityClient and ImdsCredentialProbeManager to provide detailed information about the managed identity source selection and probe execution. [1] [2]

Public API Changes:

  • Added a new enum value Credential to ManagedIdentitySource to indicate the use of credential-based managed identity.
  • Introduced an asynchronous method GetManagedIdentitySourceAsync in ManagedIdentityApplication to detect the managed identity source in the environment.

Retry logic

  • Uses the same IMDS/MSI retry logic.

These changes aim to improve the flexibility and reliability of the managed identity authentication process by supporting new identity sources and enhancing the existing mechanisms.

Testing
unit tests been added

Performance impact
Before we fallback to IMDS we check for /credential endpoint so sources like App Service, Azure ARC that are determined by environment variables should not have any issues. However, for Resource providers that do not plan to use /credential endpoint and rely on the IMDS fallback logic will have one more source detection before the fallback.

Documentation

  • All relevant documentation is updated.

@gladjohn gladjohn self-assigned this Feb 7, 2025
@gladjohn gladjohn marked this pull request as ready for review February 7, 2025 00:53
@gladjohn gladjohn requested a review from a team as a code owner February 7, 2025 00:53
@@ -152,8 +152,9 @@ private async Task<AuthenticationResult> SendTokenRequestForManagedIdentityAsync

await ResolveAuthorityAsync().ConfigureAwait(false);

ManagedIdentityClient managedIdentityClient =
new ManagedIdentityClient(AuthenticationRequestParameters.RequestContext);
ManagedIdentityClient managedIdentityClient =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gladjohn - we discussed that you'd start a feature branch? The base branch for this PR is main.


namespace Microsoft.Identity.Client.ManagedIdentity
{
internal class CredentialManagedIdentitySource : AbstractManagedIdentity
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: maybe we just call it SLC or MSIv2 ? Credential is such an overloaded term.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, should it be ImdsV2Source? I would imagine that Arc, SF etc. will be sligthly different.

HttpContent httpContent = request.CreateHttpContent();

_logger.Info($"[Credential Probe] Sending request to {CredentialEndpoint}");
_logger.Verbose(() => $"[Credential Probe] Request Headers: {string.Join(", ", request.Headers)}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little dangerous to log headers and body. Maybe don't do it unless we really need it.

}
catch (Exception ex)
{
_logger.Error($"[Credential Probe] Exception during probe: {ex.Message}");
Copy link
Member

@bgavrilMS bgavrilMS Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not really an error is it? It is part of the normal flow. It will confuse ppl. We've did this before in regional and ppl complained that the logs are too noisy. Pls use info level.

I also recommend you do not print the full exception here, just the message should be suficient.

{
if (response == null)
{
_logger.Error("[Credential Probe] No response received from the server.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not an error ?


if (response.Body != null)
{
_logger.Verbose(() => $"[Credential Probe] Response Body: {response.Body}");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

avoid printing entire reponse body. it could have PII or worse, some secrets.


LogResponseDetails(response);

return EvaluateProbeResponse(response);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it worth recording in telemetry about this? I guess we will know MSAL version and MSI source already - will this be enough?


LogResponseDetails(response);

return EvaluateProbeResponse(response);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your spec states that a retry mechanism needs to be used. This doesn't seem implemented.

image

private readonly AbstractManagedIdentity _identitySource;

public ManagedIdentityClient(RequestContext requestContext)
internal static async Task<ManagedIdentityClient> CreateAsync(RequestContext requestContext, CancellationToken cancellationToken = default)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the requestContext has the cancellation token, but it's ok to move it as last param. But I recommend you do not use a default.

{
_identitySource = SelectManagedIdentitySource(requestContext);
throw new ArgumentNullException(nameof(requestContext), "RequestContext cannot be null.");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't be null. Don't throw.

{
ManagedIdentitySource.ServiceFabric => ServiceFabricManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.AppService => AppServiceManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.MachineLearning => MachineLearningManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.CloudShell => CloudShellManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.AzureArc => AzureArcManagedIdentitySource.Create(requestContext),
ManagedIdentitySource.Credential => CredentialManagedIdentitySource.Create(requestContext),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

naming: avoid using "credential". Maybe "ImdsV2" or smth

/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static async Task<ManagedIdentitySource> GetManagedIdentitySourceAsync(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Logic is too complex. Why do we have 2 public methods - one with the probe and one without the probe?

/// <param name="logger"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
private static async Task<ManagedIdentitySource> ComputeManagedIdentitySourceAsync(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please have a look at all these method names and consolidate. They are so many "select source", "compute source", etc.


if (s_cachedManagedIdentitySource.HasValue)
{
return s_cachedManagedIdentitySource.Value;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't Azure SDK need a way to reset this? For their tests?

Maybe it's time we expose a "ResetCachesForTest()" method in the extensibility namespace.

Copy link
Member

@bgavrilMS bgavrilMS left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. feature branch
  2. retry policy
  3. reset caches for test

@gladjohn gladjohn closed this Feb 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Engineering task] Add Credential Probe logic
3 participants