1

My application is hosted in AKS, the secrets are stored in Azure secrets storage. What my client has is created a volume which stores /mnt/secrets-store that directory contains all of the secrets for my application.

For example:

  • RabbitMq--Password
  • DB--ConnectionString

Apparently this is the standard practice with my current client. There solution is then to read the file every time they want to load the settings. This is all set up in the healm charts.

   volumeMounts:
        - name: secrets-store-inline
          mountPath: "/mnt/secrets-store"
          readOnly: true
        {{- end}}

A number of my services are loaded as singletons at startup which means the settings need to be available at startup.

Note: My service has no direct access to Azure key vault and this is not something that can be changed. All I have access to are these files.

What I did was create a custom IConfigurationBuilder called UseAzureKeyVaultMountedSecrets everything is very strait forward with this. It just checks for the folder and then loads the secrets in replacing whatever is in appsettings..json for that key being the name.

  public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseAzureKeyVaultMountedSecrets()
                .UseSerilog((context, config) => config.ReadFrom.Configuration((context.Configuration)))
                .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });

Locally to test I just created the folder with the files in it and it works like a charm. However when hosted on AKS I started having issues. My health checks state that it can detect the mnt dir. But its not reading them.

After quite a bit of debugging, I found that the issue is that the mnt dir is not available when the microservice is starting up in the pod. Its first after the pod is running that it can access the mnt dir.

Is there a solution for this?

  • How can I get it to mnt the volume before start up in AKS?
  • Is it possible to load the settings again after the service has started? If it is. then I'm wondering if I could then create something like an IHostedService which would just keep trying to access it and then stop once it has grabbed them.

The main part of the problem is that these settings are used to configure all of the singletons running in the system. If I wait until everything has loaded, they have already been configured.

Linda Lawton - DaImTo
  • 106,405
  • 32
  • 180
  • 449
  • 1
    Is your service runnable at all without the secrets? If not, I would suggest UseAzureKeyVaultMountedSecrets could simply stall for a while until the mnt shows up. E.g do a delay up to ~30 seconds or so – Christian Sauer Dec 09 '21 at 08:40
  • Correct me if I'm wrong that would require that my services to have direct connection to Azure Key vault. My client has refused this option, My service can not have any direct communication with azure key vault. (government data everything is locked down hard.) – Linda Lawton - DaImTo Dec 09 '21 at 08:54
  • why? check if the mnt is there if not, wait – Christian Sauer Dec 09 '21 at 09:56
  • You cant put a wait in program.cs as far as i know. The pod wont finish loading. Its first there when the pod is done loading that the mnt directory exists. I have add logging that shows this. – Linda Lawton - DaImTo Dec 09 '21 at 10:56
  • Also the seetings are used in startup.cs to configure a number of singletons. If i want until after startup has completed its to late. – Linda Lawton - DaImTo Dec 09 '21 at 11:27

1 Answers1

0

@christian-sauer is correct I believe your "problem" is that it takes some time for these "virtual" files to show up as they are mapped from Azure Vault. ie the mount is complete and so pod initialization moves on, but the files take time to show up while the secrets are being projected.

So you need to make your app wait until they show up. I'm not sure what you mean by "The pod wont finish loading". It will be fine. It's just that your process will be doing a little extra work while it starts up.

So, just add a little loop that checks for there being files in that folder, then continue. It'll take a little effort to make it robust and you will need to experiment and observe how these files appear. ie do they all appear at once? Or do they appear over a period of time? Maybe you count the files, wait, count again. If the count is stable for some period of time (500ms?) then break the loop and continue. If it takes too long (10s, 30s?) then throw and the process (and pod) will fail. Which is the correct behavior.

You are right to be thinking about health checks, but... config is a tricky thing. As you have pointed out it is required to be available before other things can be bootstrapped.

Maybe use the IHostBuilder ConfigureHostConfiguration extension to do you file check loop. This is early enough such that startup is blocked before anything refers to config. (Note: there are two phased: Host config; App config).

You are right, this does block the process while the check is working. This is completely fine.


For completeness... you might think of using IOptionsMonitor instead (updates IConfiguration when the underlying provider detects a change. ie file update). Seems "good", but...

  1. will it be updated by the time your other pieces need it? You might be able to refactor these pieces to hook into the Changed event and re-init themselves. Here be dragons.
  2. In k8s the "files" you're reading are often symlinks. The SystemFileWatcher will not catch changes as the symlink attrs do not change, only the data being pointed to.

So, KISS, just block the process until the files show up

AndyPook
  • 2,762
  • 20
  • 23
  • My clients dev ops guy said that the mnt wasn't available until the app had completely started. This may be worth a try thanks for the tip :) It should also be pretty easy to add that. – Linda Lawton - DaImTo Dec 09 '21 at 14:06
  • hmmm, that would be unusual (in my experience). The mounts happen _before_ the container starts. Q: how would k8s know that the process had "completely started"? how does it know when all the bootstrapping is "done"; when all the IHostedServices have started? Add a little console "logging" first in Main. Check the folder/mount exists, list the files. Add a timer that repeats that, to see when files appear. – AndyPook Dec 09 '21 at 14:18
  • https://kubernetes.io/docs/concepts/storage/volumes/ in the "Background" section "A process in a container sees a filesystem view composed from the initial contents of the container image, _plus_ volumes (if defined) mounted inside the container." – AndyPook Dec 09 '21 at 14:22
  • Check the folder/mount exists, list the files. <-- i actually added a custom metrics for Prometheus. Once that comes up then i can see the directory. But as its loading my logs say its not there. Adding a timer now but i cant push it until the morning. – Linda Lawton - DaImTo Dec 09 '21 at 14:33
  • this is a timing thing. prom metrics are generally "pulled" with many seconds in between polling. Also requires the prom endpoint to the up and accessible... too late. I assume you have k8s on your dev machine? You'll be able to "prove" the mount exists immediately for the process. The mount will point at some local folder. Manually add some files. You should be able to verify the whole cycle – AndyPook Dec 09 '21 at 14:42
  • No I don't have k8s running on my dev machine. The machine is locked down by my client I'm restricted in what I can install on it. I don't have access to all the helm charts either. – Linda Lawton - DaImTo Dec 09 '21 at 14:50
  • 1
    I hate it when they do that :( I am pretty confident that the volumeMounts are mounted _before_ the ENTRYPOINT/CMD of your container is invoked (as per that doc reference). I think the "devops" person has got a bit mixed up. Putting the console logging first in Main will be the least controversial way of proving that. – AndyPook Dec 09 '21 at 14:57