Skip to content
This repository was archived by the owner on Dec 18, 2018. It is now read-only.

Commit 2519ffc

Browse files
committed
Add docker secrets config provider
1 parent 371f995 commit 2519ffc

File tree

27 files changed

+832
-93
lines changed

27 files changed

+832
-93
lines changed

Configuration.sln

Lines changed: 136 additions & 0 deletions
Large diffs are not rendered by default.

src/Microsoft.Extensions.Configuration.Abstractions/ConfigurationExtensions.cs

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,30 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67

78
namespace Microsoft.Extensions.Configuration
89
{
910
/// <summary>
10-
/// Extension methods for <see cref="IConfiguration" />.
11+
/// Extension methods for configuration classes./>.
1112
/// </summary>
1213
public static class ConfigurationExtensions
1314
{
15+
/// <summary>
16+
/// Adds a new configuration source.
17+
/// </summary>
18+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
19+
/// <param name="configureSource">Configures the source secrets.</param>
20+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
21+
public static IConfigurationBuilder Add<TSource>(this IConfigurationBuilder builder, Action<TSource> configureSource) where TSource : IConfigurationSource, new()
22+
{
23+
var source = new TSource();
24+
configureSource?.Invoke(source);
25+
return builder.Add(source);
26+
}
27+
1428
/// <summary>
1529
/// Shorthand for GetSection("ConnectionStrings")[name].
1630
/// </summary>
@@ -44,9 +58,8 @@ public static IEnumerable<KeyValuePair<string, string>> AsEnumerable(this IConfi
4458
while (stack.Count > 0)
4559
{
4660
var config = stack.Pop();
47-
var section = config as IConfigurationSection;
4861
// Don't include the sections value if we are removing paths, since it will be an empty key
49-
if (section != null && (!makePathsRelative || config != configuration))
62+
if (config is IConfigurationSection section && (!makePathsRelative || config != configuration))
5063
{
5164
yield return new KeyValuePair<string, string>(section.Path.Substring(prefixLength), section.Value);
5265
}

src/Microsoft.Extensions.Configuration.CommandLine/CommandLineConfigurationExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using System.Collections.Generic;
56
using Microsoft.Extensions.Configuration.CommandLine;
67

@@ -37,5 +38,14 @@ public static IConfigurationBuilder AddCommandLine(
3738
configurationBuilder.Add(new CommandLineConfigurationSource { Args = args, SwitchMappings = switchMappings });
3839
return configurationBuilder;
3940
}
41+
42+
/// <summary>
43+
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from the command line.
44+
/// </summary>
45+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
46+
/// <param name="configureSource">Configures the source.</param>
47+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
48+
public static IConfigurationBuilder AddCommandLine(this IConfigurationBuilder builder, Action<CommandLineConfigurationSource> configureSource)
49+
=> builder.Add(configureSource);
4050
}
4151
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
using System;
2+
using Microsoft.Extensions.Configuration.DockerSecrets;
3+
4+
namespace Microsoft.Extensions.Configuration
5+
{
6+
/// <summary>
7+
/// Extension methods for registering <see cref="DockerSecretsConfigurationProvider"/> with <see cref="IConfigurationBuilder"/>.
8+
/// </summary>
9+
public static class DockerSecretsConfigurationBuilderExtensions
10+
{
11+
/// <summary>
12+
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from docker secrets.
13+
/// </summary>
14+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
15+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
16+
public static IConfigurationBuilder AddDockerSecrets(this IConfigurationBuilder builder) =>
17+
builder.AddDockerSecrets(configureSource: null);
18+
19+
/// <summary>
20+
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from docker secrets.
21+
/// </summary>
22+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
23+
/// <param name="secretsPath">The path to the secrets directory.</param>
24+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
25+
public static IConfigurationBuilder AddDockerSecrets(this IConfigurationBuilder builder, string secretsPath)
26+
=> builder.AddDockerSecrets(source => source.SecretsDirectory = secretsPath);
27+
28+
/// <summary>
29+
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from docker secrets.
30+
/// </summary>
31+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
32+
/// <param name="secretsPath">The path to the secrets directory.</param>
33+
/// <param name="optional">Whether the directory is optional.</param>
34+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
35+
public static IConfigurationBuilder AddDockerSecrets(this IConfigurationBuilder builder, string secretsPath, bool optional)
36+
=> builder.AddDockerSecrets(source =>
37+
{
38+
source.SecretsDirectory = secretsPath;
39+
source.Optional = optional;
40+
});
41+
42+
/// <summary>
43+
/// Adds a docker secrets configuration source to <paramref name="builder"/>.
44+
/// </summary>
45+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
46+
/// <param name="configureSource">Configures the source.</param>
47+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
48+
public static IConfigurationBuilder AddDockerSecrets(this IConfigurationBuilder builder, Action<DockerSecretsConfigurationSource> configureSource)
49+
=> builder.Add(configureSource);
50+
}
51+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IO;
4+
using Microsoft.Extensions.FileProviders;
5+
6+
namespace Microsoft.Extensions.Configuration.DockerSecrets
7+
{
8+
/// <summary>
9+
/// An docker secrets based <see cref="ConfigurationProvider"/>.
10+
/// </summary>
11+
public class DockerSecretsConfigurationProvider : ConfigurationProvider
12+
{
13+
DockerSecretsConfigurationSource Source { get; set; }
14+
15+
/// <summary>
16+
/// Initializes a new instance.
17+
/// </summary>
18+
/// <param name="source">The settings.</param>
19+
public DockerSecretsConfigurationProvider(DockerSecretsConfigurationSource source)
20+
{
21+
Source = source ?? throw new ArgumentNullException(nameof(source));
22+
}
23+
24+
private static string NormalizeKey(string key)
25+
{
26+
return key.Replace("__", ConfigurationPath.KeyDelimiter);
27+
}
28+
29+
/// <summary>
30+
/// Loads the docker secrets.
31+
/// </summary>
32+
public override void Load()
33+
{
34+
Data = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
35+
36+
if (Source.FileProvider == null)
37+
{
38+
if (Directory.Exists(Source.SecretsDirectory))
39+
{
40+
Source.FileProvider = new PhysicalFileProvider(Source.SecretsDirectory);
41+
}
42+
else if (Source.Optional)
43+
{
44+
return;
45+
}
46+
else
47+
{
48+
throw new DirectoryNotFoundException("DockerSecrets directory doesn't exist and is not optional.");
49+
}
50+
}
51+
52+
var secretsDir = Source.FileProvider.GetDirectoryContents("/");
53+
if (!secretsDir.Exists && !Source.Optional)
54+
{
55+
throw new DirectoryNotFoundException("DockerSecrets directory doesn't exist and is not optional.");
56+
}
57+
58+
foreach (var file in secretsDir)
59+
{
60+
if (file.IsDirectory)
61+
{
62+
continue;
63+
}
64+
65+
using (var stream = file.CreateReadStream())
66+
using (var streamReader = new StreamReader(stream))
67+
{
68+
if (Source.IgnoreCondition == null || !Source.IgnoreCondition(file.Name))
69+
{
70+
Data.Add(NormalizeKey(file.Name), streamReader.ReadToEnd());
71+
}
72+
}
73+
}
74+
}
75+
}
76+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System;
2+
using System.IO;
3+
using Microsoft.Extensions.FileProviders;
4+
5+
namespace Microsoft.Extensions.Configuration.DockerSecrets
6+
{
7+
/// <summary>
8+
/// An <see cref="ConfigurationProvider"/> for docker secrets.
9+
/// </summary>
10+
public class DockerSecretsConfigurationSource : IConfigurationSource
11+
{
12+
/// <summary>
13+
/// Constructor
14+
/// </summary>
15+
public DockerSecretsConfigurationSource()
16+
{
17+
IgnoreCondition = s => IgnorePrefix != null && s.StartsWith(IgnorePrefix);
18+
}
19+
20+
/// <summary>
21+
/// The secrets directory which will be used if FileProvider is not set.
22+
/// </summary>
23+
public string SecretsDirectory { get; set; } = "/run/secrets";
24+
25+
/// <summary>
26+
/// The FileProvider representing the secrets directory.
27+
/// </summary>
28+
public IFileProvider FileProvider { get; set; }
29+
30+
/// <summary>
31+
/// Docker secrets that start with this prefix will be excluded.
32+
/// </summary>
33+
public string IgnorePrefix { get; set; } = "ignore.";
34+
35+
/// <summary>
36+
/// Used to determine if a file should be ignored using its name.
37+
/// </summary>
38+
public Func<string, bool> IgnoreCondition { get; set; }
39+
40+
/// <summary>
41+
/// If false, will throw if the secrets directory doesn't exist.
42+
/// </summary>
43+
public bool Optional { get; set; }
44+
45+
/// <summary>
46+
/// Builds the <see cref="DockerSecretsConfigurationProvider"/> for this source.
47+
/// </summary>
48+
/// <param name="builder">The <see cref="IConfigurationBuilder"/>.</param>
49+
/// <returns>A <see cref="DockerSecretsConfigurationProvider"/></returns>
50+
public IConfigurationProvider Build(IConfigurationBuilder builder)
51+
{
52+
return new DockerSecretsConfigurationProvider(this);
53+
}
54+
}
55+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
2+
3+
<Import Project="..\..\build\common.props" />
4+
5+
<PropertyGroup>
6+
<Description>Configuration providers that work with docker secrets for Microsoft.Extensions.Configuration.</Description>
7+
<TargetFrameworks>net451;netstandard1.3</TargetFrameworks>
8+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
9+
<PackageTags>configuration;docker</PackageTags>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<ProjectReference Include="..\Microsoft.Extensions.Configuration\Microsoft.Extensions.Configuration.csproj" />
14+
<PackageReference Include="Microsoft.Extensions.FileProviders.Physical" Version="1.2.0-*" />
15+
</ItemGroup>
16+
17+
<ItemGroup Condition=" '$(TargetFramework)' == 'netstandard1.3' ">
18+
<PackageReference Include="System.Threading.Thread" Version="4.4.0-*" />
19+
</ItemGroup>
20+
21+
</Project>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
This is a configuration provider forthe up-coming secrets feature of Docker 1.13.
3+
4+
You can use this provider in an ASP.NET Core application that is running in Docker Swarm Mode with commands like the following:
5+
6+
```
7+
$ echo "mySecretValue" | docker secrets create mysecret
8+
9+
```
10+
11+
```
12+
$ docker service create --name test --secret mysecret <myImageName>
13+
```
14+
15+
Now the `test` service will be running and have access to a secret called mysecret. In your app you can access it with
16+
17+
```
18+
var secretValue = Configuration["mysecret"];
19+
```
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"version": "1.2.0-*",
3+
"buildOptions": {
4+
"keyFile": "../../tools/Key.snk"
5+
},
6+
7+
"dependencies": {
8+
"NETStandard.Library": "1.6.1",
9+
"Microsoft.Extensions.Configuration": {
10+
"target": "project"
11+
},
12+
"Microsoft.Extensions.FileProviders.Physical": "1.2.0-*"
13+
},
14+
15+
"frameworks": {
16+
"netstandard1.3": {
17+
"dependencies": {
18+
"System.Threading.Thread": "4.3.0-*"
19+
}
20+
}
21+
},
22+
23+
"tooling": {
24+
"defaultNamespace": "Microsoft.Extensions.Configuration.DockerSecrets"
25+
}
26+
}

src/Microsoft.Extensions.Configuration.EnvironmentVariables/EnvironmentVariablesExtensions.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) .NET Foundation. All rights reserved.
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

4+
using System;
45
using Microsoft.Extensions.Configuration.EnvironmentVariables;
56

67
namespace Microsoft.Extensions.Configuration
@@ -35,5 +36,14 @@ public static IConfigurationBuilder AddEnvironmentVariables(
3536
configurationBuilder.Add(new EnvironmentVariablesConfigurationSource { Prefix = prefix });
3637
return configurationBuilder;
3738
}
39+
40+
/// <summary>
41+
/// Adds an <see cref="IConfigurationProvider"/> that reads configuration values from environment variables.
42+
/// </summary>
43+
/// <param name="builder">The <see cref="IConfigurationBuilder"/> to add to.</param>
44+
/// <param name="configureSource">Configures the source.</param>
45+
/// <returns>The <see cref="IConfigurationBuilder"/>.</returns>
46+
public static IConfigurationBuilder AddEnvironmentVariables(this IConfigurationBuilder builder, Action<EnvironmentVariablesConfigurationSource> configureSource)
47+
=> builder.Add(configureSource);
3848
}
3949
}

0 commit comments

Comments
 (0)