Keeping Secrets in Dotnet Core

Steve Ellwood
3 min readOct 7, 2020

--

Now I’m using Github more I need to be more aware of secrets and how to manage them. What I really want is something thats simple and will ideally work without configuration across platforms. I also need it to work easily with Docker. In particular this is something I don’t use that much as I mostly write libraries, so it needs to be something that’s easy to remember. Most importantly I need to be confident that I’m not storing secrets in Github — not even in privte repos Github helps with this and will notify you if it thinks it has spotted something that shouldn’t be there but I don’t want them there in the first place. In one case for me this means a whole new repo.

There is a lot of information on the web about Azure Key vault and the Secret Manager though the latter seems to be aimed more at development environments. I want something that works in Production and I don’t want to use the cloud as a connection drop could be a big issue.

The option that works for me is a combination of Environment variables and gitignore. The advantage here is that not only is it cross platform but it could be adapted to work in other languages too.

If you are using Visual Studio, it sets DOTNET_ENVIRONMENT or ASPNETCORE_ENVIRONMENT to Production by default and you can change this toDevelopment Staging etc. yourself as you can see below. Note that if both are set then ASPNETCORE_ENVIRONMENT overrides DOTNET_ENVIRONMENT.

Obviously you can also do this manually or in some sort of script. However there is another option which may be of more use, that is to set the value at runtime e.g.

dotnet run --environment "Staging"

It can also be set in your launchSettings.json file

"profiles": {"MyWebApplication": {"commandName": "MyProject","launchBrowser": true,"applicationUrl": "http://localhost:54321","environmentVariables": {"ASPNETCORE_ENVIRONMENT": "Development"

As you can chain configuration files we can now make use of this as they are environment aware e.g. you can load appsettings.json and appsettings.MyEnvironment.json by making both files optional and ensuring that appsettings.MyEnvironment.json is chained after appsettings.json. If we load both files then the last one is the only one used.

public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);

Configuration = builder.Build();
}

The next step is to make use of gitignore to ignore all of your appsettings.MyEnvironment.json files e.g.

# ignore appsettings configuration files
**/appsettings.development.json
**/appsettings.SomeOtherEnvironment.json
**/appsettings.production.json

So to tie all of this together, add your appsettings.development.json file and add all of your configuration values. Use appsettings.json as a sample template. This will be checked into source control and used by developers to setup their own appsettings.MyEnvironment.json files. Finally make sure your environment files are ignored in gitignore.

If you have a lot of settings you might break them down into logically seperate files in which case you need to remember to add all the patterns to gitignore. Finally, double check any commits in gits staging before you commit.

Of course this solution isn’t (my idea of) perfect as it still means I need to transfer files across machines when I first setup and I can’t easily manage changes to config across machines. I’m still thinking about that.

--

--

Steve Ellwood

Senior Integrations Officer at Doncaster Council Any views expressed are entirely my own.