Docker, AspNetCore, DB connection string best practices Docker, AspNetCore, DB connection string best practices docker docker

Docker, AspNetCore, DB connection string best practices


If you are using Docker Swarm then you can take advantage of the secrets feature and store all your sensitive information like passwords or even the whole connection string as docker secret.

For each secret that is created Docker will mount a file inside the container. By default it will mount all the secrets in /run/secrets folder.

You can create a custom configuration provider to read the secret and map it as configuration value

public class SwarmSecretsConfigurationProvider : ConfigurationProvider{    private readonly IEnumerable<SwarmSecretsPath> _secretsPaths;    public SwarmSecretsConfigurationProvider(        IEnumerable<SwarmSecretsPath> secretsPaths)    {        _secretsPaths = secretsPaths;    }    public override void Load()    {        var data = new Dictionary<string, string>            (StringComparer.OrdinalIgnoreCase);        foreach (var secretsPath in  _secretsPaths)        {            if (!Directory.Exists(secretsPath.Path) && !secretsPath.Optional)            {                throw new FileNotFoundException(secretsPath.Path);            }            foreach (var filePath in Directory.GetFiles(secretsPath.Path))            {                var configurationKey = Path.GetFileName(filePath);                if (secretsPath.KeyDelimiter != ":")                {                    configurationKey = configurationKey                        .Replace(secretsPath.KeyDelimiter, ":");                }                var configurationValue = File.ReadAllText(filePath);                data.Add(configurationKey, configurationValue);            }        }        Data = data;    }}

then you must add the custom provider to the application configuration

public static IHostBuilder CreateHostBuilder(string[] args){    return Host.CreateDefaultBuilder(args)        .ConfigureAppConfiguration((hostingContext, config) =>        {            config.AddSwarmSecrets();        })        .ConfigureWebHostDefaults(webBuilder =>        {            webBuilder.UseStartup<Startup>();        });}

then if you create a secret with name "my_connection_secret"

$ echo "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;" \| docker secret create my_connection_secret -

and map it to your service as connectionstrings:DatabaseConnection

services:  app:    secrets:      - target: ConnectionStrings:DatabaseConnection        source: my_connection_secret

it will be the same as writing it to the appsettings.config

{  "ConnectionStrings": {    "DatabaseConnection": "Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd=myPassword;"  }}

If you don't want to store all the connection string as secret then you can use a placeholder for the password

Server=myServerAddress;Database=myDataBase;Uid=myUsername;Pwd={{pwd}};

and use another custom configuration provider to replace it with the password stored as secret.

On my blog post How to manage passwords in ASP.NET Core configuration files I explain in detail how to create a custom configuration provider that allows you to keep only the password as a secret and update the configuration string at runtime. Also the the full source code of this article is hosted on github.com/gabihodoroaga/blog-app-secrets.


Secrets are complicated. I will say that pulling them out into environment variables kicks the problem down the road a bit, especially when you are only using docker-compose (and not something fancier like kubernetes or swarm). Your docker-compose.yaml file would look something like this:

environment:- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}

Compose will pull MYSQL_ROOT_PASSWORD from an .env file or a command line/environment variable when you spin up your services. Most CI/CD services provide ways (either through a GUI or through some command line interface) of encrypting secrets that get mapped to environment variables on the CI server.

Not to say that environment variables are necessarily the best way of handling secrets. But if you do move to an orchestration platform, like kubernetes, there will be a straightforward path to mapping kubernetes secrets to those same environment variables.