Azure / azure-sdk-for-net

This repository is for active development of the Azure SDK for .NET. For consumers of the SDK we recommend visiting our public developer docs at https://learn.microsoft.com/dotnet/azure/ or our versioned developer docs at https://azure.github.io/azure-sdk-for-net.
MIT License
5.17k stars 4.53k forks source link

[BUG] Persistence check failed exception on Windows #44792

Open hoarfrostm opened 1 week ago

hoarfrostm commented 1 week ago

Library name and version

azure.identity 1.10.4

Describe the bug

An error occured while we use .Net Azure.Identiy named persistent token cache options.

Code:

TokenCredential tokenCredential = new ClientCertificateCredential(
             TenantId,
             ClientId,
             authCert,
             new ClientCertificateCredentialOptions
             {
                SendCertificateChain = true,
                TokenCachePersistenceOptions = new TokenCachePersistenceOptions
                {
                   Name = $"{Id}-{ClientId}"
                }
             }
         );

TableServiceClient tableServiceClient = new TableServiceClient(
                new Uri(tableUrl),
                tokenCredential,
                DefaultTableClientOptions
            );

TableClient tableClient = tableServiceClient.GetTableClient(tableName);

Pageable<EntityNew> records = tableClient.Query<EntityNew>();
List<X> ips = new List<X>();
foreach (EntityNew record in records)
{
    if (!string.IsNullOrWhiteSpace(record.X))
    {
    ...
        }
    }
}          

Exception:

Azure.Identity.AuthenticationFailedException: ClientCertificateCredential authentication failed: Persistence check failed. Data was written but it could not be read. Possible cause: on Linux, LibSecret is installed but D-Bus isn't running because it cannot be started over SSH.

See the troubleshooting guide for more information.
https://aka.ms/azsdk/net/identity/clientcertificatecredential/troubleshoot
---> Microsoft.Identity.Client.Extensions.Msal.MsalCachePersistenceException: Persistence check failed. Data was written but it could not be read. Possible cause: on Linux, LibSecret is installed but D-Bus isn't running because it cannot be started over SSH.

   at Microsoft.Identity.Client.Extensions.Msal.Storage.VerifyPersistence()

   at Azure.Identity.MsalCacheHelperWrapper.VerifyPersistence()

   at Azure.Identity.TokenCache.<GetCacheHelperAsync>d__26.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at Azure.Identity.TokenCache.<GetCacheHelperAsync>d__26.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Identity.TokenCache.<RegisterCache>d__21.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Identity.MsalClientBase`1.<GetClientAsync>d__26.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Identity.MsalConfidentialClient.<AcquireTokenForClientCoreAsync>d__21.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Identity.MsalConfidentialClient.<AcquireTokenForClientAsync>d__20.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Identity.ClientCertificateCredential.GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)

   --- End of inner exception stack trace ---

   at Azure.Identity.CredentialDiagnosticScope.FailWrapAndThrow(Exception ex, String additionalMessage, Boolean isCredentialUnavailable)

   at Azure.Identity.ClientCertificateCredential.GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken)

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.AccessTokenCache.<GetHeaderValueFromCredentialAsync>d__9.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.AccessTokenCache.<GetHeaderValueAsync>d__6.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.AccessTokenCache.<GetHeaderValueAsync>d__6.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.AuthenticateAndAuthorizeRequest(HttpMessage message, TokenRequestContext context)

   at Azure.Data.Tables.TableBearerTokenChallengeAuthorizationPolicy.<AuthorizeRequestInternal>d__6.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Data.Tables.TableBearerTokenChallengeAuthorizationPolicy.AuthorizeRequest(HttpMessage message)

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.<ProcessAsync>d__11.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Core.Pipeline.BearerTokenAuthenticationPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelinePolicy.ProcessNext(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.RedirectPolicy.<ProcessAsync>d__7.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Core.Pipeline.RedirectPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelinePolicy.ProcessNext(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.RetryPolicy.<ProcessAsync>d__5.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at Azure.Core.Pipeline.RetryPolicy.<ProcessAsync>d__5.MoveNext()

--- End of stack trace from previous location where exception was thrown ---

   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()

   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)

   at Azure.Core.Pipeline.RetryPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelinePolicy.ProcessNext(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelineSynchronousPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelinePolicy.ProcessNext(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelineSynchronousPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelinePolicy.ProcessNext(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipelineSynchronousPolicy.Process(HttpMessage message, ReadOnlyMemory`1 pipeline)

   at Azure.Core.Pipeline.HttpPipeline.Send(HttpMessage message, CancellationToken cancellationToken)

   at Azure.Data.Tables.TableRestClient.QueryEntities(String table, Nullable`1 timeout, String nextPartitionKey, String nextRowKey, QueryOptions queryOptions, CancellationToken cancellationToken)

   at Azure.Data.Tables.TableClient.<>c__DisplayClass56_0`1.<Query>b__0(Nullable`1 pageSizeHint)

   at Azure.Core.PageableHelpers.FuncPageable`1.<AsPages>d__4.MoveNext()

   at Azure.Pageable`1.<GetEnumerator>d__8.MoveNext()

Expected behavior

No exception

Actual behavior

threw Persistence check failed exception

Reproduction Steps

Randomly happened multiple times in multiple services. Most frequently after a reboot of VM. But Not necessarily.

Environment

Server OS Version 6.3 (20348) - Windows Server 2022 Datacenter

.Net Framework 4.7.2

"nuget": [ { "id": "azure.core", "version": "1.36.0", "platform": "windows" }, { "id": "azure.identity", "version": "1.10.4", "platform": "windows" }

jsquire commented 1 week ago

@hoarfrostm: It is not recommended that you set TokenCachePersistenceOptions as a general practice. Those options are intended for unusual scenarios, such as when you are writing a CLI that requires durable state persistence. Setting those options causes the normal memory-based cache of the credential to be persisted to/from disk for every operation, which is a large performance hit and can cause issues in some environments when the local OS key store is not available.

To mitigate, we advise not setting the TokenCachePersistenceOptions.

github-actions[bot] commented 1 week ago

Hi @hoarfrostm. Thank you for opening this issue and giving us the opportunity to assist. We believe that this has been addressed. If you feel that further discussion is needed, please add a comment with the text "/unresolve" to remove the "issue-addressed" label and continue the conversation.

rayluo commented 3 days ago

It is the encryption that is not always successful. There can be a middle ground of still persisting token cache on disk, but without encryption.

Azure Identity's underlying library MSAL .Net supports turning off encryption: "The cross-platform token cache allows you to store unencrypted tokens in an ACL-restricted plain-text file. This is useful in cases where encryption at rest fails, which ocassionally happens due to environmnent-related reasons."

But I'm not sure whether Azure Identity exposes that behavior. Azure Identity's doc seemingly hints that it would always enable token encryption when running on Windows.