-
Notifications
You must be signed in to change notification settings - Fork 378
[MSI v2 - Feature] Add cert cache and Auth Operation to IMDS V2 #5509
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
base: rginsburg/msiv2_feature_branch
Are you sure you want to change the base?
Changes from all commits
77e22ca
a1776a0
f331cbe
7a61f5c
1a89431
8584d4e
9ebec69
40e4356
61a0b70
c7e0b75
186b98e
e5c11b6
22e1c30
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,11 +4,13 @@ | |
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Text; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using Microsoft.Identity.Client.Core; | ||
using Microsoft.Identity.Client.ManagedIdentity; | ||
using Microsoft.Identity.Client.ManagedIdentity.V2; | ||
|
||
namespace Microsoft.Identity.Client.ApiConfig.Parameters | ||
{ | ||
|
@@ -24,6 +26,13 @@ internal class AcquireTokenForManagedIdentityParameters : IAcquireTokenParameter | |
|
||
public bool IsMtlsPopRequested { get; set; } | ||
|
||
// When the MI source produced / resolved an mTLS binding certificate, we attach it here | ||
// so the request layer can apply a cache-correct IAuthenticationOperation. | ||
public X509Certificate2 MtlsCertificate { get; set; } | ||
|
||
// CSR response we get back when IMDSv2 minted the certificate. | ||
internal CertificateRequestResponse CertificateRequestResponse { get; set; } | ||
|
||
internal Func<AttestationTokenInput, CancellationToken, Task<AttestationTokenResponse>> AttestationTokenProvider { get; set; } | ||
|
||
public void LogParameters(ILoggerAdapter logger) | ||
|
@@ -38,6 +47,10 @@ public void LogParameters(ILoggerAdapter logger) | |
Claims: {!string.IsNullOrEmpty(Claims)} | ||
RevokedTokenHash: {!string.IsNullOrEmpty(RevokedTokenHash)} | ||
"""); | ||
|
||
logger.Info(() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: add it to the prev logger.Info, it's important info. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed, next commit will include it. |
||
$"[AcquireTokenForManagedIdentityParameters] IsMtlsPopRequested={IsMtlsPopRequested}, " + | ||
$"MtlsCert={(MtlsCertificate != null ? MtlsCertificate.Thumbprint : "null")}"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: MtlsCertificate?.Thumbprint ?? "null" There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed, next commit will include it. |
||
} | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -115,6 +115,13 @@ public AuthenticationRequestParameters( | |
|
||
public bool IsMtlsPopRequested => _commonParameters.IsMtlsPopRequested; | ||
|
||
// Request‑scoped override for the authentication operation. | ||
internal IAuthenticationOperation AuthenticationOperationOverride { get; set; } | ||
|
||
// Effective operation for this request: prefer the override, otherwise the default. | ||
public IAuthenticationOperation AuthenticationScheme => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe you want to just make this settable? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fixed, next commit will include it. |
||
AuthenticationOperationOverride ?? _commonParameters.AuthenticationOperation; | ||
|
||
/// <summary> | ||
/// Indicates if the user configured claims via .WithClaims. Not affected by Client Capabilities | ||
/// </summary> | ||
|
@@ -127,8 +134,6 @@ public string Claims | |
} | ||
} | ||
|
||
public IAuthenticationOperation AuthenticationScheme => _commonParameters.AuthenticationOperation; | ||
|
||
public IEnumerable<string> PersistedCacheParameters => _commonParameters.AdditionalCacheParameters; | ||
|
||
public SortedList<string, string> CacheKeyComponents {get; private set; } | ||
|
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// Copyright (c) Microsoft Corporation. All rights reserved. | ||
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Collections.Concurrent; | ||
using Microsoft.Identity.Client.ManagedIdentity.V2; | ||
|
||
namespace Microsoft.Identity.Client.ManagedIdentity | ||
{ | ||
/// <summary> | ||
/// Stores and manages certificate binding metadata for Azure managed identities using IMDSv2. | ||
/// This class caches certificate information and STS endpoints per identity (MSI client ID), | ||
/// maintaining separate mappings for different token types to ensure proper security isolation. | ||
/// </summary> | ||
/// <remarks> | ||
/// Each managed identity can have separate certificate bindings for different authentication methods: | ||
/// - Bearer tokens: Standard OAuth2 bearer tokens | ||
/// - PoP (Proof of Possession) tokens: Enhanced security tokens bound to a specific certificate | ||
/// | ||
/// The Subject is set once (first-wins pattern) while thumbprints can rotate during certificate renewal. | ||
/// This design allows proper certificate rotation while maintaining stable subject identities. | ||
/// </remarks> | ||
internal class ImdsV2BindingMetadata | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: you could have a key that includes the token type, so as to avoid storing {tokenType}Cert and {tokenType}Thumbprint |
||
{ | ||
/// <summary> | ||
/// The X.509 certificate subject distinguished name used for this identity. | ||
/// This value is set once (first-wins) and persists across certificate rotations. | ||
/// </summary> | ||
public string Subject { get; set; } | ||
|
||
/// <summary> | ||
/// Response data for Bearer token certificate authentication, including | ||
/// certificate data and STS endpoint information. | ||
/// </summary> | ||
public CertificateRequestResponse BearerResponse { get; set; } | ||
|
||
/// <summary> | ||
/// Thumbprint of the certificate used for Bearer token authentication. | ||
/// Updated during certificate rotation. | ||
/// </summary> | ||
public string BearerThumbprint { get; set; } | ||
|
||
/// <summary> | ||
/// Response data for PoP (Proof of Possession) token certificate authentication, | ||
/// including certificate data and STS endpoint information. | ||
/// </summary> | ||
public CertificateRequestResponse PopResponse { get; set; } | ||
|
||
/// <summary> | ||
/// Thumbprint of the certificate used for PoP token authentication. | ||
/// Updated during certificate rotation. | ||
/// </summary> | ||
public string PopThumbprint { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,14 +2,16 @@ | |
// Licensed under the MIT License. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using System.Collections.Concurrent; | ||
using System.IO; | ||
using System.Security.Cryptography.X509Certificates; | ||
using System.Threading; | ||
using Microsoft.Identity.Client.Internal; | ||
using System.Threading.Tasks; | ||
using Microsoft.Identity.Client.ApiConfig.Parameters; | ||
using Microsoft.Identity.Client.PlatformsCommon.Shared; | ||
using System.IO; | ||
using Microsoft.Identity.Client.Core; | ||
using Microsoft.Identity.Client.Internal; | ||
using Microsoft.Identity.Client.ManagedIdentity.V2; | ||
using Microsoft.Identity.Client.PlatformsCommon.Shared; | ||
|
||
namespace Microsoft.Identity.Client.ManagedIdentity | ||
{ | ||
|
@@ -22,9 +24,14 @@ internal class ManagedIdentityClient | |
private const string LinuxHimdsFilePath = "/opt/azcmagent/bin/himds"; | ||
internal static ManagedIdentitySource s_sourceName = ManagedIdentitySource.None; | ||
|
||
internal static void ResetSourceForTest() | ||
internal static readonly ConcurrentDictionary<string, ImdsV2BindingMetadata> s_identityToBindingMetadataMap | ||
= new ConcurrentDictionary<string, ImdsV2BindingMetadata>(StringComparer.Ordinal); | ||
|
||
internal static void ResetSourceAndBindingForTest() | ||
{ | ||
s_sourceName = ManagedIdentitySource.None; | ||
s_identityToBindingMetadataMap.Clear(); | ||
RemoveAllTestBindingCertsFromUserStoreForTest(); | ||
} | ||
|
||
internal async Task<ManagedIdentityResponse> SendTokenRequestForManagedIdentityAsync( | ||
|
@@ -68,7 +75,7 @@ internal async Task<ManagedIdentitySource> GetManagedIdentitySourceAsync(Request | |
return source; | ||
} | ||
|
||
// probe IMDSv2 | ||
// probe IMDSv2 | ||
var response = await ImdsV2ManagedIdentitySource.GetCsrMetadataAsync(requestContext, probeMode: true).ConfigureAwait(false); | ||
if (response != null) | ||
{ | ||
|
@@ -157,5 +164,11 @@ private static bool ValidateAzureArcEnvironment(string identityEndpoint, string | |
logger?.Verbose(() => "[Managed Identity] Azure Arc managed identity is not available."); | ||
return false; | ||
} | ||
|
||
// Test only method to remove all test binding certs from user store. | ||
internal static void RemoveAllTestBindingCertsFromUserStoreForTest() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this part of the product? Does eSTS send these certificates? |
||
{ | ||
MtlsBindingStore.RemoveAllBySubject("CN=Test"); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would avoid double caching. There is a component specialized in retrieving cached certificates, you should make use of that component instead of caching the cert in memory again here.