設定ファイル、環境変数、AWSSecretsManagerに定義された設定値を透過的に扱う
ASP.NET Coreでは 環境変数と設定ファイルの値を両方から取得し、IWebHost構築時にConfigurationに定義した優先度で設定値をマージして利用する。
Kralizerk.Extensions.Configuration.AWSSecretsManager ( https://github.com/Kralizek/AWSSecretsManagerConfigurationExtensions )を利用すると、上記に加えAWS SecretsManagerのエントリも環境変数やJSONファイルと同列に扱うことができ、設定値をセキュアに運用することができる。
方針と設定情報の構築
下記の設定では appSettings.json
⇒appSettings.Development.json
(ASPNETCORE_ENVIRONMENTに依存)⇒環境変数 ⇒SecretsManagerの順に設定値を読み込み、後に定義された値を優先して利用する。SecretsManagerは環境変数SECRETSMANAGER_PREFIX
にDev/App1
のように定義すると、Dev/App1/Secrets1
やDev/App1/Secrets1
といったキーのシークレット情報を取得できるようになる。
AWSが提供するSDKを利用する場合、ServiceCollection構築時にAddDefaultAWSOptionsを指定するとAWS:Regionの設定を考慮してRegionをライブラリに設定してくれるのだが、Kralizerk.Extensions.Configuration.AWSSecretsManager
では読み込んでくれなかったので、環境変数AWS__Region
から明示的にRegionを取得している。
また、ローカル開発時に、無駄にSecretsManagerから値をとらないように、前述した環境変数SECRETSMANAGER_PREFIX
が定義されていない場合は、SecretsManagerから値を取得しないといった小細工を入れている。
public static IWebHost CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { var env = hostingContext.HostingEnvironment.EnvironmentName; config.SetBasePath(Directory.GetCurrentDirectory()); config.AddJsonFile("appSettings.json"); config.AddJsonFile($"appSettings.{env}.json", optional: true); config.AddEnvironmentVariables(); var region = Environment.GetEnvironmentVariable("AWS__Region"); if (string.IsNullOrEmpty(region)) { region = "ap-northeast-1"; } var secretsManagerPrefix = Environment.GetEnvironmentVariable("SECRETSMANAGER_PREFIX"); if (!string.IsNullOrEmpty(secretsManagerPrefix)) { config.AddSecretsManager( region: RegionEndpoint.GetBySystemName(region), configurator: cfg => { cfg.KeyGenerator = (entry, key) => { var customKey = key.Substring(entry.Name.Length).Trim(':').Trim(); return string.IsNullOrEmpty(customKey) ? key : customKey; }; cfg.SecretFilter = entry => entry.Name.StartsWith(secretsManagerPrefix); }); } }) .UseStartup() .Build();
値の設定例
例えば、それぞれ下記のように定義されていた場合
appSettings.json
{ "Section1": { "Value1": "Value1", "Value2": "Value2", "Value3": "Value3", "Value4": "Value4", "Value5": "Value5" } }
appSettings.Development.json
{ "Section1": { "Value2": "from appSettings.Development.json", } }
環境変数.sh
export Section1__Value3=fromEnvironment
SecretsManager Dev/App1/Secrets1
{ "Section1": { "Value4": "from Dev/App1/Secrets1" } }
SecretsManager Dev/App1/Secrets2
{ "Section1": { "Value5": "from Dev/App1/Secrets2" } }
実行結果
下記の結果で出力される値は次のようになる。
public partial class App { public IConfiguration Configuration { get; } public App(IConfiguration configuration) { Configuration = configuration; } public void Run() { Console.WriteLine(Configuration.GetValue("Section1:Value1"); Console.WriteLine(Configuration.GetValue("Section1:Value2"); Console.WriteLine(Configuration.GetValue("Section1:Value3"); Console.WriteLine(Configuration.GetValue("Section1:Value4"); Console.WriteLine(Configuration.GetValue("Section1:Value5"); } }
実行結果
Value1 from appSettings.Development.json fromEnvironment from Dev/App1/Secrets1 from Dev/App1/Secrets2
AWS で必要となるポリシー
Kralizerk.Extensions.Configuration.AWSSecretsManagerはSecretFilterで与えられたキーをSecretsManagerから検索して列挙するため、AWSのポリシーとしてsecretsmanager:ListSecretsとGetSecretValueの二つが必要になる。
これらのポリシーが無いロールで実行しようとした場合、下記のような例外メッセージが表示される。
Unhandled Exception: Amazon.SecretsManager.AmazonSecretsManagerException: User: arn:aws:sts::571419068914:assumed-role/EcsLinuxClusterInstance-Stg-EcsInstanceRole-16ODQN6YR1TPC/i-0159b6dc3deb1e536 is not authorized to perform: secretsmanager:ListSecrets —> Amazon.Runtime.Internal.HttpErrorResponseException: Exception of type ‘Amazon.Runtime.Internal.HttpErrorResponseException’ was thrown.