Key Vault secrets not updating in .NET when using Kubernetes CSI driver
.NET Azure KeyVault Kubernetes

How to enable file system polling to make a .NET application automatically reload secrets, which were provided by the Kubernetes CSI driver as files.
April 11, 2022

At one point during development, my team and I realized that our web application, running in Azure Kubernetes Service (AKS), is not rotating secrets correctly. Specifically, when a connection string was changed in Azure Key Vault, the .NET app didn't pick up the change, despite having reloadOnChange enabled and working fine during local development on Windows.

Setup:

Turns out that file changes in Docker (and Linux, according to my experiments) are not always properly reported to the file system watcher and polling needs to be enabled.

tl;dr

Use the DOTNET_USE_POLLING_FILE_WATCHER environment variable and set it to 1 or true to make the application poll for config file changes every 4 seconds.

Getting secrets from Key Vault - the CSI driver

The application is using Secrets Store CSI (Container Storage Interface) driver for Kubernetes. There's an official guide on how to set it up.

The driver gets secrets from the configured Azure Key Vault instance (or multiple) and mounts them to Kubernetes pods as a volume. These secrets are then available to the application as files.

Using files as configuration in .NET Core

Using individual files as configuration can be easily set up in .NET with the Key-per-file configuration provider. File names will become **keys ** and file contents will become values.

To generate nested configuration (ApplicationInsights:InstrumentationKey), use __ (two underscores) like this: ApplicationInsights__InstrumentationKey.

Program.cs

public static IHostBuilder CreateHostBuilder(string[] args) =>
	Host.CreateDefaultBuilder(args)
    .ConfigureAppConfiguration((_, configuration) =>
    {
    	configuration.AddKeyPerFile(directoryPath: "/mnt/secrets-store", optional: true, reloadOnChange: true);
    })
    // ... other stuff

Note the reloadOnChange: true parameter. This should ensure that configuration gets reloaded every time the underlying files change. Running the app in Kubernetes and also locally in WSL Ubuntu revealed that it's not always the case. Which brings us to the solution.

Automatically refreshing configuration

After some investigation I found out that:

Some file systems, such as Docker containers and network shares, may not reliably send change notifications.

And the solution is to set the DOTNET_USE_POLLING_FILE_WATCHER environmental variable to either 1 or true, which makes the application poll the filesystem for changes every 4 seconds. Then the configuration gets properly updated once changed in Key Vault and propagated all the way to the cluster.

Found something inaccurate or plain wrong? Was this content helpful to you? Let me know!

šŸ“§ codez@deedx.cz