[POC] Linux Managed Identity key provider (KMPP/OP-TEE non-exportable RSA-PSS) - #6150
[POC] Linux Managed Identity key provider (KMPP/OP-TEE non-exportable RSA-PSS)#6150Gladwin Johnson (gladjohn) wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a proof-of-concept Linux Managed Identity mTLS-PoP key provider backed by KMPP/OP-TEE (non-exportable RSA-PSS), enables MI mTLS-PoP on Linux for net8.0+, and changes KeyGuard/Credential Guard attestation to fail closed (surface MsalServiceException("attestation_failed")) while bridging native MAA logs into MSAL logging.
Changes:
- Introduces Linux KMPP-backed key provider (
KmppNativeMethods,KmppRsa,LinuxManagedIdentityKeyProvider) and selects it on Linux fornet8.0+with TFM gating in the main csproj. - Changes IMDSv2 attestation behavior to throw
attestation_failedwhen a configured provider yields no token / errors (and preserves cancellation). - Adds/updates unit tests to validate the new attestation failure semantics and native-log-to-MSAL logger bridging.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Identity.Test.Unit/ManagedIdentityTests/ImdsV2Tests.cs | Expands test coverage for attestation failure/cancellation semantics and logger bridging. |
| src/client/Microsoft.Identity.Client/Microsoft.Identity.Client.csproj | Excludes net8+ KMPP provider files from older TFMs. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/V2/ImdsV2ManagedIdentitySource.cs | Fails closed on missing/empty attestation token and surfaces attestation_failed. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityPopExtensions.cs | Enables MI mTLS PoP on Linux for net8.0+. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityKeyProviderFactory.cs | Selects Linux KMPP provider on net8.0+ Linux. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/LinuxManagedIdentityKeyProvider.cs | New Linux provider that creates a KMPP enclave key and falls back to in-memory RSA. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/KmppRsa.cs | New RSA implementation that signs in-enclave and prevents private key export. |
| src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/KmppNativeMethods.cs | New P/Invoke surface to libkmpp.so functions used by the Linux provider. |
| src/client/Microsoft.Identity.Client.KeyAttestation/PopKeyAttestor.cs | Passes MSAL logger into native attestation client for log bridging. |
| src/client/Microsoft.Identity.Client.KeyAttestation/ManagedIdentityAttestationExtensions.cs | Makes WithAttestationSupport() throw attestation_failed when attestation does not produce a usable JWT. |
| src/client/Microsoft.Identity.Client.KeyAttestation/Attestation/AttestationLogger.cs | Adds mapping/formatting + bridge of native attestation logs into MSAL ILoggerAdapter. |
| src/client/Microsoft.Identity.Client.KeyAttestation/Attestation/AttestationClient.cs | Keeps a strong ref to native log callback and translates native error codes to readable reasons. |
| CHANGELOG.md | Documents the attestation behavior change and logging bridge addition. |
| using System; | ||
| using System.Diagnostics; | ||
| using System.Runtime.InteropServices; | ||
| using Microsoft.Identity.Client.Core; |
9a9e886 to
29a15c1
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityPopExtensions.cs:59
- On net8.0+ this enables mTLS PoP on Linux, but the IMDSv2 attestation path currently hard-requires a Windows RSACng key handle (see ImdsV2ManagedIdentitySource.GetAttestationJwtAsync). Since KeyGuard scenarios are documented to require .WithAttestationSupport(), Linux callers will end up with an unavoidable runtime failure instead of a clear "not supported" error. Until a Linux attestation/token-provider contract exists, keep Linux excluded here (or gate behind an explicit experimental switch).
bool isSupportedOs = DesktopOsHelper.IsWindows();
#if NET8_0_OR_GREATER
isSupportedOs = isSupportedOs || DesktopOsHelper.IsLinux();
#endif
if (!isSupportedOs)
src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/KmppRsa.cs:33
- _publicLoaded is used as a publication flag for _publicParameters, but it's not volatile / not accessed via Volatile.Read/Write. Under concurrent use of the cached RSA instance, another thread can observe _publicLoaded == true while still seeing a stale/default _publicParameters. Marking the flag volatile provides the required memory barriers for safe publication.
private readonly string _keyId;
private RSAParameters _publicParameters;
private bool _publicLoaded;
src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/KmppRsa.cs:165
- KeyIso_CLIENT_pkey_rsa_sign returns the signature length. Even if RSA-2048 is expected to be 256 bytes, returning the entire buffer unconditionally can return trailing zeros if the native method ever returns a shorter length (or if key size changes). Return exactly the number of bytes reported by rc.
throw new CryptographicException($"KMPP enclave RSA-PSS sign failed (rc={rc}).");
}
// RSA-2048 PSS signatures are always 256 bytes; the enclave fills the full buffer.
return signature;
…able RSA-PSS) Sources a non-exportable RSA-PSS key for Managed Identity mTLS PoP from the Linux KMPP (KeyIso / OP-TEE / TrustZone) enclave via libkmpp.so - the Linux equivalent of a Windows CredentialGuard / KeyGuard key. - KmppNativeMethods: P/Invoke into libkmpp.so (keygen, open, in-enclave sign, public cert). - KmppRsa: enclave-backed RSA. Public key is read from the enclave certificate; RSA-PSS/SHA-256 signing runs inside the enclave; private key is non-exportable. - LinuxManagedIdentityKeyProvider: creates the KMPP key and reports it as KeyGuard; falls back to in-memory RSA when the enclave is unavailable. - ManagedIdentityKeyProviderFactory: select the Linux provider on net8.0+. - ManagedIdentityPopExtensions: allow MI mTLS PoP on Linux (net8.0+). - Microsoft.Identity.Client.csproj: exclude the net8-only KMPP files from older TFMs. POC / draft. Validated end-to-end on an Azure Linux 3 (LVBS) VM: MSAL creates the non-exportable KMPP key and builds the CSR; attestation still fails on Linux because MSAL only attests Windows CNG/CredentialGuard keys today. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: ef5bb3cc-f7b6-4df1-8da7-8b1eb94e3527
29a15c1 to
0818532
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (3)
src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityPopExtensions.cs:60
- On NET8+, Linux is treated as a supported OS for managed-identity mTLS PoP, but the IMDSv2 attestation path currently only supports Windows RSA CNG keys (see ImdsV2ManagedIdentitySource.GetAttestationJwtAsync). As a result, calling .WithAttestationSupport() on Linux will deterministically fail at runtime with credential_guard_requires_cng. Consider gating Linux/KMPP support behind an explicit opt-in until a Linux attestation implementation exists, so the public API doesn’t advertise a broken path by default.
bool isSupportedOs = DesktopOsHelper.IsWindows();
#if NET8_0_OR_GREATER
isSupportedOs = isSupportedOs || DesktopOsHelper.IsLinux();
#endif
if (!isSupportedOs)
src/client/Microsoft.Identity.Client/ManagedIdentity/ManagedIdentityKeyProviderFactory.cs:118
- The Linux provider is selected unconditionally on NET8_0_OR_GREATER. Given that IMDSv2 attestation currently enforces RSACng (Windows-only), selecting this provider by default can cause Linux hosts to be advertised as KeyGuard-capable and later fail in the attestation path. Consider making Linux/KMPP selection explicitly opt-in until Linux attestation is implemented (e.g., via an environment variable) to avoid changing default runtime behavior on Linux net8.
#if NET8_0_OR_GREATER
if (DesktopOsHelper.IsLinux())
{
// Linux: source a non-exportable RSA-PSS key from the KMPP/OP-TEE enclave
// (KeyGuard-equivalent), falling back to in-memory inside the provider when the
src/client/Microsoft.Identity.Client/ManagedIdentity/KeyProviders/LinuxManagedIdentityKeyProvider.cs:125
- If KeyIso_create_self_sign_pfx_to_key_id returns rc==0 but still allocates a non-null keyIdPtr, the current early-return path leaks that native allocation because KeyIso_clear_free_string is only called on the success path. Free the pointer before returning false whenever keyIdPtr is non-zero.
if (rc == 0 || keyIdPtr == IntPtr.Zero)
{
logger?.Info(() => $"[MI][LinuxKeyProvider] KMPP keygen (KeyIso_create_self_sign_pfx_to_key_id) returned rc={rc}.");
return false;
}
Summary (POC / draft)
Adds a Linux Managed Identity key provider that sources a non-exportable RSA-PSS key from the KMPP (KeyIso / OP-TEE / TrustZone) enclave via
libkmpp.so— the Linux equivalent of a Windows CredentialGuard / KeyGuard key.This is a proof of concept for the MSI v2 mTLS-PoP story on Linux. It gets the flow to where MSAL itself creates the non-exportable key and builds the CSR; attestation is the remaining gap (below).
Changes
ManagedIdentity/KeyProviders/KmppNativeMethods.cs— P/Invoke intolibkmpp.so(keygen, open, in-enclave sign, public cert).ManagedIdentity/KeyProviders/KmppRsa.cs—RSAwhose private key stays in the enclave. Public key read from the enclave self-signed cert; RSA-PSS/SHA-256 signing runs in-enclave; private-key export throws (non-exportable).ManagedIdentity/KeyProviders/LinuxManagedIdentityKeyProvider.cs— creates the KMPP key (KeyIso_create_self_sign_pfx_to_key_id), reports it asKeyGuard, falls back to in-memory RSA when the enclave is unavailable.ManagedIdentityKeyProviderFactory.cs— selects the Linux provider onnet8.0+.ManagedIdentityPopExtensions.cs— allows MI mTLS PoP on Linux (net8.0+), previously Windows-only.Microsoft.Identity.Client.csproj— excludes the net8-only KMPP files fromnet462/net472/netstandard2.0.Validation
Built (all TFMs) and run E2E on an Azure Linux 3 (LVBS "shielded secrets") Trusted Launch VM:
A standalone probe confirmed the native path: keygen -> public key -> in-enclave RSA-PSS sign -> verify = VALID; private key non-exportable (only the public cert is extractable).
Known gap / not in scope
ImdsV2ManagedIdentitySource.GetAttestationJwtAsync(MSAL only attests Windows CNG/CredentialGuard keys). A Linux attestation path (e.g. KMPPKeyIso_create_key_claim-> MAA) is the follow-up.Draft for discussion — feedback welcome on the provider seam, the
net8.0+gating, and the attestation follow-up.