Docker Secrets secret provider¶
This provider allows you to work with Docker secrets. When using Docker secrets in Docker Swarm, the secrets are injected in the Docker container as files. The Docker secrets secret provider provides access to those secrets via the secret store.
This secret provider offers functionality which is equivalent to the KeyPerFile Configuration Provider, but instead of adding the secrets to the Configuration, this secret provider allows access to the Docker Secrets via the ISecretProvider interface.
⚡ Supports synchronous secret retrieval.
Installation¶
Adding secrets from the User Secrets manager into the secret store requires following package;
`Install-Package GridLab.Abp.Security.Providers.DockerSecrets`
Configuration¶
After installing the package, the additional extensions becomes available when building the secret store.
using Microsoft.Extensions.Hosting;
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.ConfigureSecretStore((context, config, builder) =>
{
// Docker secrets are by default mounted into the /run/secrets directory when using Linux containers on Docker Swarm.
builder.AddDockerSecrets(directoryPath: "/run/secrets");
})
.ConfigureWebHostDefaults(webBuilder => webBuilder.UseStartup<Startup>());
}
}
Retrieving secrets¶
Suppose you have the following docker-compose file:
services:
apihost:
...
secrets:
- ConnectionStrings__Default
secrets:
ConnectionStrings__Default:
external: true
After adding the Docker Secrets secret provider to the secret store, the Docker secrets can simply be retrieved by calling the appropriate methods on the ISecretProvider
:
namespace Application.Controllers
{
public class PersonController
{
private readonly ISecretProvider _secrets;
public PersonController(ISecretProvider secrets)
{
_secrets = secrets;
}
[HttpGet]
public async Task GetPerson(Guid personId)
{
string connectionString = await _secrets.GetRawSecretAsync("ConnectionStrings:Default")
using (var connection = new SqlDbConnection(connectionString))
{
var person = new PersonRepository(connection).GetPersonById(personId);
return Ok(new { Id = person.Id, Name = person.Name });
}
}
}
.NET Core configuration provider¶
This extensions allows reading docker secrets files and pull them into the .net core configuration. Docker secrets by default are mounted as files within the /run/secrets
directory on the container file system. All we need to do is read all files from that directory and apply their contents to our existing configuration.
After installing the GridLab.Abp.Security.Providers.DockerSecrets
package, the additional extensions becomes available when building the configuration provider.
Configuration¶
By default, all files within the directory /run/secrets
(depending on operation system) are scanned and processed as configuration.
.NET Core configuration uses the colon character : as section delimiter. As : cannot be used in file names, we use the string __
in place where :
is needed.
using Microsoft.Extensions.Hosting;
using Serilog;
public class Program
{
static async Task Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
#if DEBUG
.MinimumLevel.Override("DockerSecrets", LogEventLevel.Debug)
#else
.MinimumLevel.Override("DockerSecrets", LogEventLevel.Information)
#endif
.Enrich.FromLogContext()
.WriteTo.Async(c => c.File("Logs/logs.txt"))
.WriteTo.Async(c => c.Console())
.CreateLogger();
await CreateHostBuilder(args).RunConsoleAsync();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.....
.ConfigureAppConfiguration((context, config) => {
if (!context.HostingEnvironment.IsDevelopment())
{
config.AddDockerSecrets(
options: () => new DockerSecretsConfigurationOptions
{
ColonPlaceholder = "__"
},
secretsPath: "/run/secrets",
logger: new SerilogLoggerProvider(Log.Logger).CreateLogger("DockerSecrets")
);
}
})
.....
}
}
Often we want to process just specific secrets files. By setting allowed prefixes we can narrow down which files will be processed.
Only process files that start with a predefined prefix¶
.ConfigureAppConfiguration((context, config) => {
if (!context.HostingEnvironment.IsDevelopment())
{
config.AddDockerSecrets(
options: () => new DockerSecretsConfigurationOptions
{
ColonPlaceholder = "__",
AllowedPrefixes = new List<string> { "ConnectionStrings__", "Openiddict__Applications__" }
},
secretsPath: "/run/secrets",
logger: new SerilogLoggerProvider(Log.Logger).CreateLogger("DockerSecrets")
);
}
})
Specify the name of the environment variable which holds a comma-delimited list of allowed prefixes¶
services:
apihost:
...
environment:
- ALLOWED_PREFIXES=ConnectionStrings__,Openiddict__Applications__
secrets:
- ConnectionStrings__Default
secrets:
ConnectionStrings__Default:
external: true
Filtering docker secrets based on the list in the environment variable separated by ,
is done when scanning docker secrets.
.ConfigureAppConfiguration((context, config) => {
if (!context.HostingEnvironment.IsDevelopment())
{
config.AddDockerSecrets(
options: () => new DockerSecretsConfigurationOptions
{
ColonPlaceholder = "__",
AllowedPrefixesEnvironmentName = "ALLOWED_PREFIXES"
},
secretsPath: "/run/secrets",
logger: new SerilogLoggerProvider(Log.Logger).CreateLogger("DockerSecrets")
);
}
})
Example : Docker compose¶
Docker compose compatible file:
services:
apihost:
....
environment:
- ALLOWED_PREFIXES=ConnectionStrings__,Openiddict__Applications__
# No need to declare `ConnectionStrings__Default` here since it is going to provide by secret provider.
....
secrets:
- ConnectionStrings__Default
secrets:
ConnectionStrings__Default:
external: true
....
When you set external: true
in your Compose file, it tells Docker that the secret doesn’t need to be created by the Compose file itself. Instead, it should already exist and be available when the Compose is run. If the secret isn’t there, it will result in an error.