I have been using the new .net 4.7.1 functionality to manage secrets locally in DEV but now I want to publish my web app and of course that doesn't work in PROD.
I am using a service principal to access Azure key vault in my app and was storing the App Id and Password in the local secrets file. I can of course store all of my secrets in Key vault but I still need to supply it the service principal details to connect to that in the first place.
What's the best option here?
You can use Managed Identities so you don't have to store any secret in your app:
How to use managed identities for App Service and Azure Functions
Once configured, add a reference to these nuget packages: Microsoft.Azure.Services.AppAuthentication and Microsoft.Azure.KeyVault
In you application, you can access key vault secrets like that:
using Microsoft.Azure.KeyVault;
using Microsoft.Azure.Services.AppAuthentication;
...
var azureServiceTokenProvider = new AzureServiceTokenProvider();
var keyVaultClient = new KeyVaultClient(
new KeyVaultClient.AuthenticationCallback(azureServiceTokenProvider.KeyVaultTokenCallback));
var secret = await keyVaultClient.GetSecretAsync(
"https://{{my-vault-name}}.vault.azure.net/", "{{my-secret}}");
You need to store the values you use to open the key vault in the App settings of your application. If your production environment is Azure, you can access the App settings of your application and set them there.
Read this article to get an idea of the full picture. But the jist is that you can use application secrets in a Azure key valut and then use app settings to store the credentials to access that key vault later on.
Related
The code below works on my local machine passing DefaultAzureCredential(). But when I host it on IIS on Azure VM, the authentication fails. I tried setting the ApplicationPool's identity to my credential since I have access to the Azure KeyVault, still the same 403 forbidden error. Deploying as AppServices on Azure is not an option currently.
What is the best way to read Azure KeyVault Secrets in this scenario?
string kvUri = "https://mykeyvault.vault.azure.net/";
string secretName = "MyConnectionString";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
var secret = await client.GetSecretAsync(secretName);
Console.WriteLine(secret?.Value?.Value);
still the same 403 forbidden error
We need to enable the system-assigned Managed Identity on the VM which we have deployed our Web App.
And provide the permissions for the Identity which you have set in previous step.Run the below command in Azure CLI
az keyvault set-policy --name 'KeyVaultName' --object-id "ObjectID of SystemAssigned Managed Identity" --secret-permissions get list set delete
Make sure you have given access permissions to retrieve the Key Vault.
In Azure Keyvault => Access policies , select the Get,List permissions and provide the Principal - name will be same as your deployed WebApp , continue with the steps and Review +create
in order to programmatically retrive some AppTraces and AppExceptions info from an Azure Application Insights Logs resource, we followed the instructions included in the following article advicing to adopt the new Azure Monitor Query client library for .NET to fulfill the purpose.
https://learn.microsoft.com/it-it/dotnet/api/overview/azure/Monitor.Query-readme?view=azure-dotnet
Strictly following the above article instructions (and using the DefaultAzureCredential object to authenticate), we managed to get the client library's LogsQueryClient object working fine in the local version of the developed web api (ASP .NET Core 6.0). And so, locally we are eable to fetch the logs info we need.
But once we published the web api on the Cloud (under the same Azure subscription of the Application Insights target resource) we started to get the following error:
Message: The provided credentials have insufficient access to perform the requested operation
Status: 403 (Forbidden)
ErrorCode: InsufficientAccessError
N.B.
Surprisingly we didn't find any thread explaining, step by step how to fix the problem with specific reference to the new Azure Monitor Query client library.
To fix the issue, we tried replacing the class DefaultAzureCredential with the class ClientSecretCredential generating and assigning it a new client secret.
Here are the details concerning the steps we followed to implement the ClientSecretCredentials.
In particular, we have:
Setup a new Azure AD Application.
Assigned it the required permissions ==> Data.Read (Read Log Analytics data - Granted from Admin).
Assinged to the Registered App (AAD Application) the Reader Role from the Application Insights Resource's Access control (IAM) section of the Azure Portal.
Created a new client secret for the AAD Application.
Created a new Azure Web API, on witch we Installed the Azure Monitor Query client library for .NET.
To retrive Logs data, we programmatically istantiated a new Azure.Identity.ClientSecretCredential object, assigning it the right tenantId, the client (application) ID of the AAD Application and the client secret previously generated for the App Registration.
In the Program.cs file of the web api we created a singleton instance of the class LogsQueryClient assigning it the above ClientSecretCredential object.
And finally we invoked the QueryWorkspaceAsync method of the class LogsQueryClient, passing it the WorkSpaceId of the Application Insights Resource (whom logs have to be read) and the query to retrive.
Unfortunately, replacing the class DefaultAzureCredential with ClientSecretCredential didn't work and the error message keeps to be the same.
N.B.
The AAD User Type of the user who: developed and released the web api, registered the new Azure AD Application and granted it the necessary permissions is "Member".
The above user, refers to the same tenant id as the resources he managed in the above steps (Web Api, AAD Application etc).
During the release process of the web api, a new API Management service was specifically created by the same user releasing the app.
Here are the code snippets:
Program.cs
builder.Services.AddAzureClients(builder =>
{
static LogsQueryClient func(LogsQueryClientOptions options)
{
options.Retry.Mode = Azure.Core.RetryMode.Exponential;
options.Retry.MaxRetries = 5;
var csc = new ClientSecretCredential(tenantId, clientId, clientSecret);
return new LogsQueryClient(csc, options);
}
builder.AddClient<LogsQueryClient, LogsQueryClientOptions>(func);
var credentials = new ClientSecretCredential(tenantId, clientId, clientSecret);
builder.UseCredential(credentials);
});
Controller.cs (get logsQueryClient through dependency injection)
Response<LogsQueryResult> response = await logsQueryClient.QueryWorkspaceAsync(workSpaceId, query);
The provided credentials have insufficient access to perform the requested operation
The error clearly shows that it doesn't have a sufficient access to process the request.
To identify the new RBAC permission, Azure App Insights REST API takes an hour to sync with AAD and it throws Status 403 as error code without complete sync. Refer MS-DOC for related stuff
Basically, this error can happen when the user doesn't have access to authorize Access token with AAD
Provide the required access to the user which you want to authorize.
Make sure to check your request are valid before processing your APi request in Production Environment. (Token, GUID, etc.,).
Thanks for your appreciated help.
We finally managed to spot where the problem was.
After the point 3 listed in our original post:
<<Assign to the Registered App (AAD Application) the "Reader Role" for the Application Insights acting from the resource's Access control (IAM) section of the Azure Portal.>>
... We omitted to do the same thing for the Log Analytics workspace.
Each Application Insight resource refers infact to a specific Workspace.
For that reason, it is necessary to assign the same reader role to the Registered App also for the above workspace.
Log Analytics workspaces too have infact an Access control (IAM) section in the Azure Portal, just like the other resources.
Assigning this role to the AAD registered app the issue was fixed.
Connection works fine following this tutorial when using:
var connection = (SqlConnection)Database.GetDbConnection();
connection.AccessToken = (new Microsoft.Azure.Services.AppAuthentication.AzureServiceTokenProvider()).GetAccessTokenAsync("https://database.windows.net/").Result;
But now the docs say "Microsoft.Azure.Services.AppAuthentication is no longer recommended"
So changing my connection as described in Using Azure Active Directory authentication with SqlClient I get the following errors:
Using Active Directory Integrated authentication
Integrated Windows Auth is not supported for managed users.
Using Active Directory Managed Identity authentication
Tried to get token using Managed Identity. Access token could not be acquired. A socket operation was attempted to an unreachable network. (169.254.169.254:80)
Nothing is blocking that address, but also where is it getting that IP from? The tutorial's code used https://database.windows.net/ to get the token (which resolves 65.55.23.107).
Can/should I override that address somewhere?
Any other config missing?
These auth ways apply to different scenarios, for example, if you want to use Active Directory Integrated authentication, you need to federate the on-premises AD with Azure AD via ADFS, if you want to use Active Directory Managed Identity authentication, you must run your code in an Azure service which supports MSI(need to enable MSI first), because the code essentially makes an API call to the azure instance metadata endpoint to get the access token, then use the token to auth, it is just available in the MSI-supported service.
So if you want to migrate the code from the old sdk to the new one, you need to choose the correct auth way that applies to your scenario. Here I recommend you to use the Active Directory Service Principal authentication, it can apply to any scenario, please follow the steps below.
1.Register an application with Azure AD and create a service principal.
2.Get values for signing in and create a new application secret.
3.Grant the permission to the service principal with CREATE USER [Azure_AD_Object] FROM EXTERNAL PROVIDER.
4.Then use the code here, fix the values with yours and got from step 2.
string ConnectionString = #"Server=demo.database.windows.net; Authentication=Active Directory Service Principal; Database=testdb; User Id=AppId; Password=secret";
using (SqlConnection conn = new SqlConnection(ConnectionString)) {
conn.Open();
}
I have an application that runs in a Service Fabric(SF) cluster and I wan't to access Key Vault from it.
The cluster hosts a number of applications and I want to give access to a Key Vault for my application without giving access to the other applications. By default an application runs under the same user as the SF cluster, but each applicatiuon has it's own unique name, mine has the name fabric:/application1.
My question is, is it possible to create an Active Directory application account for fabric:/application1 and grant access to the key vault?
I know it is possible to use the RunAs options in the SF manifest, but that requires me storing an encrypted password in the manifest/source code and I want to try and avoid this if possible.
AFAIK,
The only way to have this flexibility is using ClientID & Secret or Service Principal certificates and each application manage their own credentials.
Service Principal Certificate is already integrated to AD, but does not require the application, the user or the Host to be part of the domain, the only requirement is setup an user on AD to grant the permissions on Keyvault.
There are other solutions using AD integration, like Managed identities for Azure resources(Former: Managed Service Identity) but I am not sure if you are able to restrict access per application like you described, because the MI add this as a service in the node, so technically other applicaitons would have access as well, worth a try to validate if you can restrict this.
If you want to try this approach, you can use with Microsoft.Azure.Services.AppAuthentication for implicit authentication of the services running in your cluster, where the nodes are setup with Managed Identities extension like described here.
Something link this:
When you use the Microsoft.Azure.Services.AppAuthentication, the Step 2 will be handled by the library and you won't have to add much changes to your key vault auth logic.
When you run your code on an Azure App Service or an Azure VM with a
managed identity enabled, the library automatically uses the managed
identity. No code changes are required.
The following docs describe other options you can use for KeyVault Authentication.
PS: I've done other KeyVault integrations using Client Secrets and Certificates and they are secure enough, With Certificates you can store it on the managed store or with the application, I would recommend MI only if is a requirement for your solution.
I am using .NET Core 2.0 and ASP.NET Core 2.0 for application development. The "test" application is a .NET Core Console application. The core code I am writing is a class library. Once proper testing. I choose to do this since I won't be putting this to use for awhile (it's replacing older ASPNET code).
Anyway, since I have to work with a LOT of API keys for various services I decided to use Microsoft Azure Key Vault for storing the keys. I have this all setup and understand how this works. The test application uses a test Azure account so it's not critical. And since this is replacing legacy code and it's in the infancy, I am the sole developer.
Basically, I'm running into this issue. There's not too much information on Azure Key Vault from what I can see. A lot of examples are storing the Client ID and Secret in a plain text json file (for example: https://www.humankode.com/asp-net-core/how-to-store-secrets-in-azure-key-vault-using-net-core). I really don't understand how this can be secure. If someone were to get those keys they could easily access stored information Azure, right?
The Microsoft MSDN has a powershell command that grants access (I lost the original link, this is closest I can find: https://www.red-gate.com/simple-talk/cloud/platform-as-a-service/setting-up-and-configuring-an-azure-key-vault/) My development operating system is Windows 10 and my primary server operating system is Debian.
How would I approach this?
Yes, you are right, the plain text config file could be used only during development, not for production purpose. And in general, available options depend on where and how you host an App.
If you have an Azure Web App, you have at least next built-in options (from the documentation):
add the ClientId and ClientSecret values for the AppSettings in the Azure portal. By doing this, the actual values will not be in the web.config but protected via the Portal where you have separate access control capabilities. These values will be substituted for the values that you entered in your web.config. Make sure that the names are the same.
authenticate an Azure AD application is by using a Client ID and a Certificate instead of a Client ID and Client Secret. Following are the steps to use a Certificate in an Azure Web App:
Get or Create a Certificate
Associate the Certificate with an Azure AD application
Add code to your Web App to use the Certificate
Add a Certificate to your Web App
You may also find an approach that uses env variables to store credentials. This may be OK only if you can guarantee that it's not possible to do a snapshot of env variable on prod machine. Look into Environment Variables Considered Harmful for Your Secrets for more details.
And the last one thing: there is also a technic that based on the idea, that you need to store/pass only a ClientSecret value while ClientId should be constructed based on machine/container details where the App is hosted (e.g. docker container id). I have found an example for Hashicorp Vault and an App hosted on AWS, but the general idea is the same: Secret management with Vault
In addition to the first answer, with the context of running applications on Azure VM, instead of using client_secret to authenticate, you can use client certificate authentication as explained in this documentation: Authenticate with a Certificate instead of a Client Secret.
In the picture above:
Application is authenticating to AAD by proving that it has the private key of the certificate (which is basically stored in CNG if you are using Windows).
Application get back the access_token and then use it to access the Key Vault.
The developer does not need to know the private key value of the certificate in order for their app to be successfully authenticated. Instead, they only need to know the location of the imported pfx (a container for private key and its certificate) in the Certificate Store.
At least on Windows, you as secret administrator can convert the private key and the certificate into pfx format which is password protected, and then deploy it into the Windows Certificate store. This way no one could know the private key unless they know the password of the pfx file.
The other approach specifics for Azure Compute, is to use Azure Managed Service Identity. Using Azure MSI, Azure will automatically assign your resources such as VM with an identity / Service Principal, and you can fire requests at a specific endpoint that are only accessible by your resource to get the access_token. But be wary that Azure MSI are still under public preview, so please review the known issues before using it.
The picture above explain how Azure Resource Manager assign a Service Principal identity to your VM.
When you enable MSI in a VM, Azure will create a service principal in your AAD.
Azure will then deploy a new MSI VM extension to your VM. This provides an endpoint at http://localhost:50432/oauth2/token to be used to get the access_token for the service principal.
You can then use the access_token to access the resources such as Key Vault which authorize the service principal access.