Azure Key Vault: Secret not found error - powershell

I've created the Key Vault and entered a secret. When I run my services locally using .NET, I am able to retrieve the secret via the key vault. Here's what I did:
1) Created an SSL certificate
2) Used that SSL certificate to create an AD application
3) Created a Service Principle for the above application
4) Gave full key vault access to this application
5) I put the VaultURI, ServicePrincipal.Application ID, and the cert thumbprint in the Web.config file
6) I also uploaded the *.pfx of that cert to my cloud service
When I run my service locally, I am able to retrieve the secret. I have even tried retrieving the secret via powershell and I have been successful. When I deploy my code to Azure, I am unable to retrieve the secret.
It says:
Type : Microsoft.Azure.KeyVault.Models.KeyVaultErrorException, Microsoft.Azure.KeyVault, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 Message : Secret not found: QSAccounts7126 Source : Microsoft.Azure.KeyVault Help link :
I have spent 3 days looking at it and retesting every possible scenario and haven't figured out what is wrong. Can someone please help in identifying the issue or directing me in the right path for debugging?
I even tried publishing the cloud service in debug mode in Azure, and for some reason that did not work either.
Any help you can provide would be greatly appreciated.
private async Task<string> getSecretConnection(string connectionName)
{
var kvName = ConfigurationManager.AppSettings["vaultName"];
var kvClientId = ConfigurationManager.AppSettings["clientId"];
var kvClientThumbprint = ConfigurationManager.AppSettings["clientThumbprint"];
using (keyVaultHelper = new AzureKeyVaultHelper(kvClientId, kvClientThumbprint, kvName))
{
var bundle = await keyVaultHelper.GetAzureKeyVaultSecretAsync(connectionName);
return bundle;
}
public async Task<string> GetAzureKeyVaultSecretAsync(string secretName)
{
var bundle = await this.KvClient.GetSecretAsync(KeyVaultUrl, secretName);
return bundle.Value;
}
This is the code that runs for authentication:
private async Task<string> getAccessTokenFromSPNAsync(string authority, string resource, string scope)
{
//clientID and clientSecret are obtained by registering
//the application in Azure AD
var certificate = CertificateHelper.FindCertificateByThumbprint(this.ClientThumbprint);
var assertionCert = new ClientAssertionCertificate(this.ClientId, certificate); //needed for authentication
var clientCredential = new ClientCredential(this.ClientId, this.ClientThumbprint);
var authContext = new AuthenticationContext(authority, TokenCache.DefaultShared);
AuthenticationResult result = await authContext.AcquireTokenAsync(resource, assertionCert);
if (result == null)
{
throw new InvalidOperationException("Failed to obtain the token from Azure AD using certificate");
}
return result.AccessToken;
}

Can someone please help in identifying the issue or directing me in the right path for debugging?
It seems that you need to add the WEBSITE_LOAD_CERTIFICATES in your Azure WebApp setting to load certification to your web applications personal certificate store. More details you could refer this blog. We also could remote debug the WebApp to get more detail error information.
Adding an app setting named WEBSITE_LOAD_CERTIFICATES with its value set to the thumbprint of the certificate will make it accessible to your web application. You can have multiple comma-separated thumbprint values or can set this value to “ * “ (without quotes) in which case all your certificates will be loaded to your web applications personal certificate store.

I am not sure that your code is correct.
For example the constructor for ClientCredential takes a Client ID and Client Secret, your code is passing in a Client ID and Certificate Thumbprint.
See the Microsoft article Use Azure Key Vault from a Web Application for example code. I have used the code in this article and it works as expected when deployed to Azure.

Related

Azure terraform Use existing managed identity to authenticate web app

I hope somebody can help me to solve this issue.
Using terraform I scripted some resource groups and webapps. Those web app have some configurations that need to access a key vault to retrieve some secrets.
But to do so, I need to activate the azure identity on the web app.
So far everything is working just fine without any problem. But as I am still learning how to use terraform with azure, I need to keep destroying and spinning up the webapps, which mean everytime I need to activate the identity and add the access policy in the key vault.
So what I did, is to create a azure managed identity resource in the same resource group where I have the key vault. Now I would like to use this managed identity to authenticate my web app everytime I spin up the web app.
My web app code looks like this:
resource "azurerm_app_service" "app-hri-stg-eur-configurations-api" {
name = "app-hri-${var.env}-${var.reg-name}-webapp-testing"
app_service_plan_id = azurerm_app_service_plan.ASP-hri-stg-eur-webapp.id
location = var.location
resource_group_name = azurerm_resource_group.rg-hri-stg-eur-webapp.name
app_settings = {
"secret" = "#Microsoft.KeyVault(SecretUri=https://mykeyvault.vault.azure.net/secrets/test)"
...... <My configuration>
}
identity {
type = "UserAssigned"
}
}
And here is where I am getting confused, how can I reference the azure managed identity that I have already created to grant access to my web app to read the secrets?
I hope I made my question clear enough, and please if not just ask for more info
Thank you so much for any help you can provide
A identity block supports the following:
type - (Required) Specifies the identity type of the App Service. Possible values are SystemAssigned (where Azure will generate a Service Principal for you), UserAssigned where you can specify the Service Principal IDs in the identity_ids field, and SystemAssigned, UserAssigned which assigns both a system managed identity as well as the specified user assigned identities.
identity_ids - (Optional) Specifies a list of user managed identity ids to be assigned. Required if type is UserAssigned.
so you should have sth like this:
data "azurerm_user_assigned_identity" "example" {
name = "name_of_user_assigned_identity"
resource_group_name = "name_of_resource_group"
}
If your identity is in another resource group. It allows you to reference to already created azure resource.
identity {
type = "UserAssigned",
identity_ids = [data.azurerm_user_assigned_identity.example.id]
}

Obtain Access Token for the connection to PostgreSQL in Azure Functions

I have created some Azure Functions, and I would like to connect to Postgres database using an Access Token (for the connection of the user is doing the request to the endpoint of Azure Function). I've been following this page: https://learn.microsoft.com/en-us/azure/postgresql/howto-configure-sign-in-aad-authentication
For Authentication/Authorization in my functions I have created an Azure Active Directory app (in App registrarions). In this app, (not sure if this is useful) I have given permissions to the API of OSSRDBMS:
Then, I get the Access Token of the next endpoint of my App Service (where are my Functions):
MYAPP_SERVICE.azurewebsites.net/.auth/me
And I am trying to get the connection with this Access Token, but appearly is not the good one. What am I missing or how can I get the correct Access Token? I understand how works with Azure CLI, but I am trying to use the Access Token of the endpoint of my App Service... or can I do an HTTP Request to get the correct token?
I need a bit of guidance in this matter.
Thanks beforehand
If you want to connect Postgres database with Azure AD auth in Azure function, we can Azure Managed Identity to do Azure AD auth then get Azure AD access token and connect database.
The detailed steps are as below
Enable Azure MSI fro your Azure function app
Get the client id of the MSI
az login
az ad sp show --id <the object id of the msi> --query appId --output tsv
Configure Azure AD admin in Postgres database
Use the Azure AD admin to connect the database. (I use PgAdmin to connect)
SET aad_validate_oids_in_tenant = off;
CREATE ROLE <userName> WITH LOGIN PASSWORD '<the appid of the MSI>' IN ROLE azure_ad_user;
Configure Postgres server firewall. Please add the Azure function app outbound IP addresses in the firewall. Regarding how to get the Azure function app outbound IP addresses and configure Postgres server firewall, please refer to here and here
If you enable SSL, please download SSL certificate via the link
Function. (I use .net core to write the sample)
a. sdk
<PackageReference Include="Microsoft.Azure.Services.AppAuthentication" Version="1.5.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.8" />
<PackageReference Include="Npgsql" Version="4.1.3.1" />
b. add above SSL certificate in project. For example, I create a folder cert in my project and save cert in the folder
c. code
[FunctionName("Http")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
ILogger log, ExecutionContext context)
{
var azureServiceTokenProvider = new AzureServiceTokenProvider();
string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://ossrdbms-aad.database.windows.net");
string Host = "test05.postgres.database.azure.com";
string User = "testuer#test05";
string Database = "postgres";
string connString =
String.Format(
"Server={0}; User Id={1}; Database={2}; Port={3}; Password={4};SSLMode=Require",
Host,
User,
Database,
5432,
accessToken);
string result = string.Empty;
using (var conn = new NpgsqlConnection(connString))
{
ProvideClientCertificatesCallback provideClientCertificates = clientCerts =>
{
string clientCertPath = context.FunctionAppDirectory + "\\cert\\BaltimoreCyberTrustRoot.crt.pem";
var cert = new X509Certificate2(clientCertPath);
clientCerts.Add(cert);
};
conn.ProvideClientCertificatesCallback += provideClientCertificates;
Console.Out.WriteLine("Opening connection using access token...");
conn.Open();
using (var command = new NpgsqlCommand("SELECT version()", conn))
{
var reader = command.ExecuteReader();
while (reader.Read())
{
Console.WriteLine("\nConnected!\n\nPostgres version: {0}", reader.GetString(0));
result = reader.GetString(0);
}
}
}
return new OkObjectResult(result);
}
For more details, please refer to here

DocuSign JWT access toke call fails in Dev environment while successful in Local

We are trying to integrate with DocuSign, and using JWT authorization in Spring Boot application.
When I am running my application locally (running in local Tomcat) I am able to connect to DocuSign and email documents successfully. However, once I deployed the code to our Development environment (running Websphere), the call to request JWT token is returning below Exception. Keep in mind I am using the same base URL and Integrator key in both environments:
Exception: Error while requesting server, received a non successful HTTP code 401 with response Body: '{
"errorCode": "PARTNER_AUTHENTICATION_FAILED",
"message": "The specified Integrator Key was not found or is disabled. An Integrator key was not specified."
}'
Call to request JWT token:
ApiClient apiClient = new ApiClient(BASE_URL);
apiClient.configureJWTAuthorizationFlow(currentDir + PUBLIC_KEY_FILENAME, currentDir + PRIVATE_KEY_FILENAME, OAUTH_BASE_URL, INTEGRATOR_KEY, USER_ID, expireIn);
BASE_URL = https://demo.docusign.net/restapi
OAUTH_BASE_URL = account-d.docusign.com
I have values set for the rest of the variables as well; I'm just showing variable names only here.
I have already granted access by making the following call and logging in the browser by making the following call in a test class:
String oauthLoginUrl = apiClient.getJWTUri(INTEGRATOR_KEY, REDIRECT_URI, OAUTH_BASE_URL);
Desktop.getDesktop().browse(URI.create(oauthLoginUrl));
I'd appreciate any help. Thank you.
After days of trying out different things finally resolved my issue, posting the answer here in case it helps someone else.
In my dev env even though file name was loaded correctly apiClient could not load the correct file, and in local tomcat server that was not the case. After seeing the same exception when I changed the fileName in for local server, made a guess that server is failing to read the correct key files.
Instead of passing the file name directly I had to use Thread.currentThread().getContextClassLoader().getResource(publicKeyFileName).getPath(); for both public and private keys and that resolved the issue.

azure sdk for net login is takes too long

I am using Fluent Library to develop a web app which can create a sql server on azure. The console app works great but when I implement the code to a web api it stuck in authentication step. I'm sure about the credentials which are true and I have a Service Principal.
// Authenticate
var credentials = new AzureCredentials(new ServicePrincipalLoginInformation { ClientId = ClientId, ClientSecret = Password }, tenantId, AzureEnvironment.AzureGlobalCloud);
var azure = Azure.Configure().Authenticate(credentials).WithDefaultSubscription();
I also can repro it on my side. I try to debug it with following code and add quick watch for azureauth.WithDefaultSubscription(), then get value The function evaluation requires all threads to run. So I guess that it may run some threads that WebAPI can't handle.
var azureauth = Azure.Configure().Authenticate(credentials);
azureauth.WithDefaultSubscription()
Please have a try to use following code to use specified subscriptionId as workaround. It works correctly on my side.
var azure = Azure.Configure().Authenticate(credentials).WithSubscription("subscriptionId");

Loading a server-side certificate *and* a private key from Windows Server cert store?

I'm trying to get this external REST webservice that requires both a server-side certificate and a private key (both of which I got from the publisher as *.pem files of that service).
For my testing, I googled and found a way to combine these two pieces into a *.pfx file - and loading a X509Certificate2 instance from that binary file on disk works just fine.
Now I was trying to put this into the Cert Store on my production Windows Server 2008.
I can get the X509Certificate2 from the cert store in my C# code - no problem:
X509Store store = new X509Store(StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certs = store.Certificates.Find(X509FindType.FindBySerialNumber, "serial-number-here", false);
if (certs.Count > 0)
{
X509Certificate2 cert = certs[0];
// set the certificate on the RestClient to call my REST service
_restClient.ClientCertificates.Add(cert);
}
store.Close();
But when I do this, then the web service barfs at me, claiming it needs a "SSL certificate"...
Also: when I was loading the X509Certificate2 from disk, from that *.pfx file, I had to provide a password - nothing needs to be provided here, when loading from the cert store.... odd....
It seems that even though I imported the *.pfx which contains both the server-side certificate and our private key, somehow I cannot get both back from the cert store...
Any idea how I can get this to work? Do I need to load the private key from the cert store in a second step? How?
These certificates still remain mainly a big voodoo-like mystery to me ..... can anyone enlighten me?
The first thing to check is to see whether the certificate store does have the private key.
Open up the certificate management snappin and find your certificate, double click it and make sure it has the red highlighted section like in the image below:
Next, if the private key is in the store then maybe the account accessing the certificate does not have permissions on the private key. There are two ways to check this:
In the certificate management snappin, right click the certificate > All tasks > Manage private keys. (You should be able to check and edit permissions here)
In your code you could access the PrivateKey property (i.e. Do var privateKey = cert.PrivateKey and see whether you get it back).
You did not write how is the web service implemented.
if it is deployed on IIS
if it is self hosted
Your code to get certificate from store is correct. The question is where did you import the pfx - CurrentUser or LocalMachine store. You are using CurrentUser store in the code example. If you imported the certificate to LocalMachine store it will not be found. Also, please specify the store name - StoreName.My (in MMC or certmgr.msc it means Personal) in the constructor of X509Store (it might be default, but who knows all the defaults anyway :) )
But when I do this, then the web service barfs at me, claiming it
needs a "SSL certificate"...
You need to ensure you have Client Authentication in the extended key usage of the certificate.
Also: when I was loading the X509Certificate2 from disk, from that
*.pfx file, I had to provide a password - nothing needs to be provided here, when loading from the cert store.... odd....
It's how it works. When you have a pfx then the private key in it is secured/encrypted with password (password can be an empty string). When you import pfx to certificate store then the private key is secured/encrypted with other key (not exactly sure what key it is). However you can add another level of protection to the private key by specifying strong protection when importing pfx to the store (I do not recommend it when used with ASP.NET, or web services or anything that does not have a desktop). But when it is your personal certificate to sign emails then it might be good to enable it. Windows will then popup a window whenever an application will try to use the private key.
#DanL might be right about the rights to the private key and his
1) - set rights on private key and
2) - accessing private key in X509Certificate2
are written OK. I would just add to 1) that you are trying to connect to the REST service from a ASP.NET application or another web service on IIS then the name of the account that you need to add permission for is IIS APPPOOL\name_of_the_apppool_your_app_runs_under