Azure / data-api-builder

Data API builder provides modern REST and GraphQL endpoints to your Azure Databases and on-prem stores.
https://aka.ms/dab/docs
MIT License
901 stars 182 forks source link

Support for User Assigned Managed identity - CosmosDB NoSQL / Container Apps #1939

Open jmkelljr opened 9 months ago

jmkelljr commented 9 months ago

What happened?

A bug happened!

Version

Microsoft.DataApiBuilder 0.9.7+e560142426d1c080b9fd7b7fabff51a276f6bf61

What database are you using?

CosmosDB NoSQL

What hosting model are you using?

Container Apps

Which API approach are you accessing DAB through?

GraphQL

Relevant log output

Please support RBAC Authentication for CosmosDB via a ManagedIdentity.  My company's security policies do not allow local authentication via keys.

I get the following error when trying to connect with the master key
{
  "errors": [
    {
      "message": "Response status code does not indicate success: Unauthorized (401); Substatus: 5202; ActivityId: ccfad820-3641-48e2-8808-8b0c3993f8f8; Reason: (Local Authorization is disabled. Use an AAD token to authorize all requests.\r\nActivityId: ccfad820-3641-48e2-8808-8b0c3993f8f8, Microsoft.Azure.Documents.Common/2.14.0, Linux/2.0 cosmos-netstandard-sdk/3.19.3);",
      "locations": [
        {
          "line": 2,
          "column": 5
        }
      ],
      "path": [
        "books"
      ]
    }
  ]
}

For more info see... 

https://joonasaijala.com/2021/07/01/how-to-using-managed-identities-to-access-cosmos-db-data-via-rbac-and-disabling-authentication-via-keys/

Code of Conduct

jmkelljr commented 9 months ago

I realized that if I didn't specify the "AccountKey" parameter in the connection string that the code would drop into trying to connect with a managed identity. However, I am using a user-assigned managed identity and now have this issue:

fail: Azure.DataApiBuilder.Service.Startup[0] A GraphQL request execution error occurred. Azure.Identity.AuthenticationFailedException: ManagedIdentityCredential authentication failed: Service request failed. Status: 400 (Bad Request)

  Content:
  {"statusCode":400,"message":"Unable to load the proper Managed Identity.","correlationId":"c4d8cdec-5a73-4a90-88f9-8b6fcef97dac"}

  Headers:
  Date: Wed, 20 Dec 2023 21:58:57 GMT
  Server: Kestrel
  Transfer-Encoding: chunked
  X-CORRELATION-ID: REDACTED
  Content-Type: application/json; charset=utf-8

  See the troubleshooting guide for more information. https://aka.ms/azsdk/net/identity/managedidentitycredential/troubleshoot
   ---> Azure.RequestFailedException: Service request failed.
  Status: 400 (Bad Request)

  Content:
  {"statusCode":400,"message":"Unable to load the proper Managed Identity.","correlationId":"c4d8cdec-5a73-4a90-88f9-8b6fcef97dac"}

  Headers:
  Date: Wed, 20 Dec 2023 21:58:57 GMT
  Server: Kestrel
  Transfer-Encoding: chunked
  X-CORRELATION-ID: REDACTED
  Content-Type: application/json; charset=utf-8

     at Azure.Identity.ManagedIdentitySource.HandleResponseAsync(Boolean async, TokenRequestContext context, Response response, CancellationToken cancellationToken)
     at Azure.Identity.ManagedIdentitySource.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
     at Azure.Identity.ManagedIdentityClient.AuthenticateCoreAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
     at Azure.Identity.ManagedIdentityClient.AppTokenProviderImpl(AppTokenProviderParameters parameters)
     at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.SendTokenRequestToAppTokenProviderAsync(ILoggerAdapter logger, CancellationToken cancellationToken)
     at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.GetAccessTokenAsync(CancellationToken cancellationToken, ILoggerAdapter logger)
     at Microsoft.Identity.Client.Internal.Requests.ClientCredentialRequest.ExecuteAsync(CancellationToken cancellationToken)
     at Microsoft.Identity.Client.Internal.Requests.RequestBase.RunAsync(CancellationToken cancellationToken)
     at Microsoft.Identity.Client.ApiConfig.Executors.ConfidentialClientExecutor.ExecuteAsync(AcquireTokenCommonParameters commonParameters, AcquireTokenForClientParameters clientParameters, CancellationToken cancellationToken)
     at Azure.Identity.AbstractAcquireTokenParameterBuilderExtensions.ExecuteAsync[T](AbstractAcquireTokenParameterBuilder`1 builder, Boolean async, CancellationToken cancellationToken)
     at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientCoreAsync(String[] scopes, String tenantId, Boolean enableCae, Boolean async, CancellationToken cancellationToken)
     at Azure.Identity.MsalConfidentialClient.AcquireTokenForClientAsync(String[] scopes, String tenantId, Boolean enableCae, Boolean async, CancellationToken cancellationToken)
     at Azure.Identity.ManagedIdentityClient.AuthenticateAsync(Boolean async, TokenRequestContext context, CancellationToken cancellationToken)
     at Azure.Identity.ManagedIdentityCredential.GetTokenImplAsync(Boolean async, TokenRequestContext requestContext, CancellationToken cancellationToken)
     --- End of inner exception stack trace ---
     at Microsoft.Azure.Cosmos.Routing.GlobalEndpointManager.GetAccountPropertiesHelper.GetAccountPropertiesAsync()
     at Microsoft.Azure.Cosmos.GatewayAccountReader.InitializeReaderAsync()
     at Microsoft.Azure.Cosmos.CosmosAccountServiceConfiguration.InitializeAsync()
     at Microsoft.Azure.Cosmos.DocumentClient.InitializeGatewayConfigurationReaderAsync()
     at Microsoft.Azure.Cosmos.DocumentClient.GetInitializationTaskAsync(IStoreClientFactory storeClientFactory)
     at Microsoft.Azure.Cosmos.DocumentClient.EnsureValidClientAsync(ITrace trace)
     at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.EnsureValidClientAsync(RequestMessage request, ITrace trace)
     at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(RequestMessage request, CancellationToken cancellationToken)
     at Microsoft.Azure.Cosmos.Handlers.RequestInvokerHandler.SendAsync(String resourceUriString, ResourceType resourceType, OperationType operationType, RequestOptions requestOptions, ContainerInternal cosmosContainerCore, FeedRange feedRange, Stream streamPayload, Action`1 requestEnricher, ITrace trace, CancellationToken cancellationToken)
     at Microsoft.Azure.Cosmos.ContainerCore.ReadContainerAsync(ITrace trace, ContainerRequestOptions requestOptions, CancellationToken cancellationToken)
     at Microsoft.Azure.Cosmos.ClientContextCore.RunWithDiagnosticsHelperAsync[TResult](ITrace trace, Func`2 task)
     at Microsoft.Azure.Cosmos.ClientContextCore.OperationHelperWithRootTraceAsync[TResult](String operationName, RequestOptions requestOptions, Func`2 task, TraceComponent traceComponent, TraceLevel traceLevel)
     at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.GetPartitionKeyPath(Container container, ISqlMetadataProvider metadataStoreProvider) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 267
     at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.GetIdAndPartitionKey(IDictionary`2 parameters, Container container, CosmosQueryStructure structure, ISqlMetadataProvider metadataStoreProvider) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 278
     at Azure.DataApiBuilder.Core.Resolvers.CosmosQueryEngine.ExecuteAsync(IMiddlewareContext context, IDictionary`2 parameters, String dataSourceName) in /src/src/Core/Resolvers/CosmosQueryEngine.cs:line 72
     at Azure.DataApiBuilder.Core.Services.ResolverMiddleware.InvokeAsync(IMiddlewareContext context) in /src/src/Core/Services/ResolverMiddleware.cs:line 106
     at HotChocolate.Utilities.MiddlewareCompiler`1.ExpressionHelper.AwaitTaskHelper(Task task)
     at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)
     at HotChocolate.Execution.Processing.Tasks.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)

info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1] Executed endpoint 'Hot Chocolate GraphQL Pipeline' info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished HTTP/1.1 POST http://graphqlapp.nicesky-e371e381.eastus2.azurecontainerapps.io/graphql application/json 117 - 500 - application/json;+charset=utf-8 68.6274ms

It appears this is a known issue with SDK's in Azure Container Apps as referenced here:

https://github.com/microsoft/azure-container-apps/issues/442

You may need to modify how you are getting the credential to allow for the config file to specify the AZURE_CLIENT_ID

seantleonard commented 9 months ago

Thank you for raising this issue. We are tracking this ask via #1944. Currently, only system assigned managed identities are supported.

dgcaron commented 1 week ago

we are actually using a user assigened managed identity and this does work. you need to specify the clientid in an environment variable called AZURE_CLIENT_ID in the container apps app.