Skip to content

jordanbean-msft/keyvault-web

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

keyvault-web

This repo shows how to retrieve secrets stored in an Azure Key Vault using a .NET Framework & .NET Core application. It demonstrates both pulling the secrets via middleware at startup time and dynamically when a page is loaded.

Onprem deployment

architecture-onprem

Onprem, you need to provide a way for the running application to access the Key Vault securly. This is accomplished via service principal provisioned in Azure Active Directory. This service principal is then granted access to the Key Vault. The application authenticates to Azure Active Directory using a X.509 certificate so that it can use the service principal to access the Key Vault.

Note: The application is written to pull the certificate from the cert:\LocalMachine\My certificate store on the onprem server. If this is not the right location to get your certificate from, you will need to modify the code to pull the certificate from the correct location.

You will need to modify code similar to the below code to pull from the right store for your deployment.

var x509Store = new X509Store(StoreName.My,
                              StoreLocation.LocalMachine);

Azure deployment

architecture-azure

In Azure, the process can be simplified by using a Managed Identity. The Managed Identity is granted access to the Key Vault & is assigned to the App Service so code running in the App Service can use it. The deployment script will set the Managed Identity client ID for you as part of deployment. This will override the values specified in the configuration files.

Note: This repo intentionally doesn't access the secrets through the Azure App Service configuration so that it is portable between onprem & Azure. If you are only targeting Azure, you can store the secrets in the App Service configuration.

Note: You could still use the certificate-based authentication method to allow your application to authenticate with Azure AD instead of using Managed Identity. Managed Identity makes the process simpler, but is not required.

Disclaimer

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

.NET Framework

The .NET Framework version of this application pulls the secrets from Key Vault when the web page is loaded. It does not have a default inversion of control container that pulls the secrets at startup time. Instead, it uses a custom implementation of the ConfigBuilder to pull the certificate from the Windows certificate store to authenticate to Azure with & pull all the secrets from Key Vault.

The following NuGet packages are required to access Key Vault using .NET Framework (these will install some additional dependencies):

  • Azure.Identity
  • Azure.Security.KeyVault.Secrets
  • Microsoft.Configuration.ConfigurationBuilders.Azure

If you look at the ./web-net-framework/Web.config file, you can see the following configBuilder section which tells teh custom CertificateAuthenticationAzureKeyVaultConfigBuilder class how to authenticate with Azure Active Directory so it can use the service principal to access the Key Vault.

<configBuilders>
    <builders>
      <add name="KeyVault" mode="Greedy" vaultName="kv-keyvault-web-ussc-dev" enabled="true" certificateStoreName="My" certificateStoreLocation="LocalMachine" certificateThumbprint="a17d4362fbf40049bb4aa7eb465d082358c7878a" tenantId="72f988bf-86f1-41af-91ab-2d7cd011db47" clientId="9bfd1049-3cfe-4466-a684-2b5fb636b03e" type="web_net_framework.Services.CertificateAuthenticationAzureKeyVaultConfigBuilder, web-net-framework" />
    </builders>
  </configBuilders>
  <appSettings configBuilders="KeyVault">
    <add key="the-king-of-england" value="Elizabeth II" />
  ...
</appSettings>

You will notice that the appSettings section of the Web.config file has a value already for the-king-of-england. Naturally, this value is wrong and we want to override it with the value from Key Vault. The Greedy flag will override the value from the Web.config withe the one from Key Vault if it was able to successfully authenticate & pull secrets.

Here is what the running application should look like if it was able to successfully authenticate with Azure Active Directory & pull secrets from Key Vault.

web-net-framework

Onprem authentication with certificate

In the ./web-net-framework/Services/CertificateAuthenticationAzureKeyVaultConfigBuilder.cs, we pull the certificate from the local store, authenticate with Azure AD, then pull the secrets that are needed. This custom class is needed because the default implementation of the AzureKeyVaultConfigBuilder will try to use the DefaultAzureCredentials class which will not work on the onprem server since it doesn't have a managed identity nor does the service account running the app pool have access to Azure. Instead, we want to pull a certificate from the local store and authenticate with Azure AD.

protected override TokenCredential GetCredential()
{
    StoreName storeName = (StoreName)Enum.Parse(typeof(StoreName), CertificateStoreName);

    StoreLocation storeLocation = (StoreLocation)Enum.Parse(typeof(StoreLocation), CertificateStoreLocation);

    var x509Store = new X509Store(storeName,
                                  storeLocation);

    x509Store.Open(OpenFlags.ReadOnly);

    X509Certificate2 x509Certificate;

    try
    {
        x509Certificate = x509Store.Certificates.Find(X509FindType.FindByThumbprint,
                                                      CertificateThumbprint,
                                                      validOnly: false)
                                                .OfType<X509Certificate2>()
                                                .Single();
    }
    catch (Exception ex)
    {
        throw new ArgumentException($"Unable to find certificate in cert:\\{CertificateStoreLocation}\\{CertificateStoreName} with thumbprint: {CertificateThumbprint}", ex);
    }

    var tokenCredential = new ClientCertificateCredential(TenantId,
                                                          ClientId,
                                                          x509Certificate);

    return tokenCredential;
}

App Service with Managed Identity

Using the Managed Identity associated with the App Service, it much simpler to authenticate with Azure AD.

 client = new SecretClient(new Uri(kvUri),
                            new DefaultAzureCredential(new DefaultAzureCredentialOptions
                            {
                                ManagedIdentityClientId = ConfigurationManager.AppSettings["Authentication:ManagedIdentityClientId"]
                            }));

.NET Core

Similarly, the .NET Core version of this application pulls the Key Vault secrets when the web page is loaded. However, because .NET Core already has a middleware installed, most of the secrets are pulled at startup time. Only 1 secret is pulled at page load time to demonstrate how to pull secrets dynamically.

If you look at the ./web-net-core/appsettings.json file, you can see the following app settings which will allow the application to authenticate with Azure Active Directory so it can use the service principal to access the Key Vault.

"KeyVaultName": "kv-keyvault-web-ussc-dev",
"Authentication": {
  "AzureADApplicationId": "9bfd1049-3cfe-4466-a684-2b5fb636b03e",
  "AzureADCertificateThumbprint": "539cd5afadb7b25b85cf90a78c261074a6db6445",
  "AzureADDirectoryId": "72f988bf-86f1-41af-91ab-2d7cd011db47",
  "ManagedIdentityClientId": ""
},
"IsHostedOnPrem": "true"

Here is what the running application should look like if it was able to successfully authenticate with Azure Active Directory & pull secrets from Key Vault.

web-net-core

Startup.cs code to pull all secrets as configuration values

The middleware allows us to pull all secrets from Key Vault at startup time and store them as configuration values that can be used throughout the application (look at the ./web-net-core/Program.cs file).

builder.Configuration.AddAzureKeyVault(new Uri(kvUri), new ClientCertificateCredential(
                                          builder.Configuration["Authentication:AzureADDirectoryId"],
                                          builder.Configuration["Authentication:AzureADApplicationId"],
                                          x509Certificate));

Prerequisites

Deployment

Create the certificate

It is recommeded that you get a signed certificate from your company's certificate authority. However, if you cannot, you can generate a self-signed certificate locally.

Run this script as Administrator on the onprem server where you are running the application.

./create-certificate.ps1

This script will generate a self-signed certificate, install it in the cert:\LocalMachine\My certificate store and write the public key .cer file to the current user's Desktop. You will need to upload this file to the Azure AD service principal so the local application can use it to authenticate.

Create the app registration/service principal

You will need a service principal in Azure Active Directory to be the identity that your application uses to access the Key Vault.

  1. Navigate to the Azure portal and sign in.

  2. Click on the Azure Active Directory link in the left-hand navigation menu.

  3. Click on the App registrations blade.

  4. Click on the New registration button.

  5. Give it a name & a redirect uri where your application will be listening (http://localhost as an example for running locally).

register-application

  1. Click on the Certificates & secrets blade.

  2. Upload the .cer certificate file that was created in the previous step. Save the Thumbprint value for configuring the application.

upload-cert

  1. On the Overview blade, copy the Application ID (client ID) and the Directory (tenant) ID values for configuring the application.

aad-overview

Deploy the infrastructure

  1. Modify the ./infra/env/dev.parameters.json file as needed. Make sure and update the azureADApplicationId with the Application ID (client ID) value from the previous step.
az deployment group create -g rg-keyvault-web-ussc-dev --template-file ./infra/main.bicep --parameters ./infra/env/dev.parameters.json --parameters theKingOfAustriaSecretValue="Joseph the 2nd" theKingOfPrussiaSecretValue="Fredrick Wilhelm the 3rd" theKingOfEnglandSecretValue="Why the tyrant King George, of course!"

The script will output the name of the Key Vault & the URLs to the web apps.

Grant the app registration access to Key Vault

  1. Navigate to the Azure portal and sign in.

  2. Select the Key Vault created in the previous step.

  3. Click on the Access policies blade.

  4. Click on the Add Access Policy button.

  5. Add Get and List Secret permissions.

get-list-permissions

  1. Click on the Select principal button.

  2. Search for your newly created service principal (from the previous step), select it and click the Select button. Click on the Add button.

keyVault-add-access-policy

  1. Click on the Save button.

  2. Repeat these steps for the Managed Identity created in the previous step.

keyVault-access-policies

Configure onprem server to use certificate

You will likely need to configure the onprem server to be able to use the certificate from the local certificate store.

  1. Login to the onprem server as Administrator.

  2. Open the Computer certificate store and select the store where you have provisioned your certificate (Personal in this example).

  3. Right-click on the provisioned certificate and select All Tasks->Manage Private Keys.

certificate-manage-private-keys

  1. Click on the Add button and select the account that the IIS app pool is running under (IIS_ISRS in this example).

  2. Select Full control as the permissions and click OK.

Build/publish .NET Framework Web App & deploy to onprem server

  1. Open the ./web-net-framework/web-net-framework.sln file in Visual Studio.

  2. Update the ./web-net-framework/Web.config file with the your values in the configBuilders section.

    • vaultName - the name of your Key Vault
    • clientId - the application ID (client ID) of your service principal
    • certificateThumbprint - the thumbprint of the certificate installed on the machine that will be used to authenticate with Azure Active Directory
    • certificateStoreName - the name of the certificate store you want to pull from
    • certificateStoreLocation - the location of the certificate store you want to pull from
    • tenantId - the tenant ID where your service principal is instantiated
  3. Right-click on the project and select Build.

  4. Right-click on the project and select Publish.

Note: The following instructions assume you are using Web Deploy for the onprem IIS server. You could also manually copy your application to the onprem server.

  1. Click the New button.

  2. Select Web Server (IIS) as the publish type. Click Next.

  3. Choose Web Deploy as the specific target. Click Next.

  4. Enter the credentials for the onprem server. Click Next. and Finish.

  5. Click Publish to push your code to the onprem server.

Build/publish .NET Core Web App & deploy to onprem server

  1. Open the ./web-net-core/web-net-core.csproj file in Visual Studio.

  2. Update the ./web-net-core/appsettings.json file with the your values.

    • KeyVaultName - the name of your Key Vault
    • Authentication:AzureADApplicationId - the application ID (client ID) of your service principal
    • Authentication:AzureADCertificateThumbprint - the thumbprint of the certificate installed on the machine that will be used to authenticate with Azure Active Directory
    • Authentication:AzureADDirectoryId - the tenant ID where your service principal is instantiated
    • Authentication:ManagedIdentityClientId - this value doesn't need to be set when running onprem (since there is no managed identity to use)
    • IsHostedOnPrem - set this value to true

Note: You can also set these values in IIS and override the values in the ./web-net-core/appsettings.json file.

iis-application-settings

iis-application-settings-thumbprint

  1. Right-click on the project and select Build.

  2. Right-click on the project and select Publish.

Note: The following instructions assume you are using Web Deploy for the onprem IIS server. You could also manually copy your application to the onprem server.

  1. Click the New button.

  2. Select Web Server (IIS) as the publish type. Click Next.

  3. Choose Web Deploy as the specific target. Click Next.

  4. Enter the credentials for the onprem server. Click Next. and Finish.

  5. Click Publish to push your code to the onprem server.

Build/publish .NET Framework Web App & deploy to Azure

  1. Open the ./web-net-framework/web-net-framework.sln file in Visual Studio.

  2. You don't need to modify the values of the ./web-net-framework/Web.config file since the values will be set in the App Service Configuration settings automatically by the Infrastructure as Code Bicep scripts.

  3. Right-click on the project and select Build.

  4. Right-click on the project and select Publish.

  5. Click the New button.

  6. Select Azure, then Next. Select Azure App Service (Windows), then Next.

  7. Select your Azure subscription, Resource Group and App Service instance (make sure and select the wa-net-framework App Service), then Finish and Close.

  8. Click Publish to push your app to the App Service.

Build/publish .NET Core Web App & deploy to Azure

  1. Navigate to the ./web-net-core directory on the command line (or use Visual Studio).

  2. Build the application & create a publish package.

dotnet publish --configuration Release
  1. Zip up the publish package.
Compress-Archive -DestinationPath ./app.zip -Update ./bin/Release/net6.0/publish
  1. Deploy your zip package to Azure.
az webapp deployment source config-zip --resource-group rg-keyvault-web-ussc-dev --name wa-keyvault-web-ussc-dev --src ./app.zip

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published