.NETでDockerを利用したローカル開発時にもAWS Profileを使ってシークレットを安全に引き渡す


はじめに

 ローカル開発時に.NETのプログラムからAWSの各サービスを利用するには、認証情報としてAWSのプロファイル情報を利用するか、アクセスキーとシークレットキーを設定する必要があります。プログラムの設定ファイルなどにアクセスキーやシークレットキーを記載すると、誤ってソース管理リポジトリに公開してしまう危険があるため、可能であればAWSプロファイルを利用してAWSに接続することが推奨されます。
 この記事では、Dockerを利用したローカル開発時に、AWS Profileを利用してシークレットを安全に取り扱う方法について解説します。

プロファイルを利用した.NET CoreでのAWSサービスの利用

 .NET CoreでAWSの各サービスを利用する場合は下記のドキュメントに記載されている通り、設定ファイル(appsettings.json)に利用するAWSのプロファイル情報を定義し、Startup時にAddDefaultAWSOptions拡張メソッドで前述の設定内容を有効にすることが推奨されています。
https://docs.aws.amazon.com/ja_jp/sdk-for-net/v3/developer-guide/net-dg-config-netcore.html

具体的には、下記のようになります。

appsettings.json
{
  "AWS": {
    "Profile": "DevProfile",
    "Region": "ap-northeast-1"
  }
}
Startup.cs(AWSサービスのDI設定)
public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddDefaultAWSOptions(Configuration.GetAWSOptions());
        services.AddAWSService<IAmazonSecretsManager>();

        /// ... 略 ...
    }
    /// ... 略 ...
}
サービスの利用
public class HomeController : ControllerBase
{
    private readonly IAmazonSecretsManager _amazonSecretsManager;
    public HomeController (IAmazonSecretsManager amazonSecretsManager)
    {
        _amazonSecretsManager = amazonSecretsManager;
    }
    public async Task<string> Get()
    {
        var secret = await _amazonSecretsManager.GetSecretValueAsync(new GetSecretValueRequest
            {
                SecretId = "MySecret"
            });
        return secret.SecretString;
    }
}

 Visual StudioでIISにホストしたり、dotnet runでWebサイトをセルフホストした場合はこれで問題ありませんが、Dockerにホストした場合はDocker内に設定ファイル(appsettings.jsonで指定した)AWSプロファイルが存在しないため、下記のような例外が発生します。

AWSプロファイルからAWSの接続情報が取得できないために発生する例外
Amazon.Runtime.AmazonServiceException: Unable to get IAM security credentials from EC2 Instance Metadata Service.
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.FetchCredentials()
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.GetCredentials()
   at Amazon.Runtime.DefaultInstanceProfileAWSCredentials.GetCredentialsAsync()
   at Amazon.Runtime.Internal.CredentialsRetriever.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.RetryHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.CallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.ErrorCallbackHandler.InvokeAsync[T](IExecutionContext executionContext)
   at Amazon.Runtime.Internal.MetricsHandler.InvokeAsync[T](IExecutionContext executionContext)

Docker利用のAWSプロファイルの利用

 アクセスキーとシークレットキーを設定してもよいのですが、せっかく機密情報を記載しなくてもよい仕組みがあるのでプロファイルを利用したいですよね。AWSプロファイルは、各ユーザーのホームディレクトリ直下の.awsディレクトリに保存されています。このディレクトリ配下の設定情報はどのOSでも共通になっているので、このフォルダーをボリュームマウントしてあげればよさそうです。

Visual Studioを利用している場合は、コンテナーオーケストレーターのサポートから

Docker Composeを追加して、

docker-compose.override.ymlにホームディレクトリ/.awsディレクトリを各コンテナのホームディレクトリにマウントする設定を追加してあげましょう。AWSプロファイル名は各開発者で異なる可能性があるので、AWSプロファイル名もこちらに書いておくと良いかもしれませんね。

docker-compose.override.yml
version: '3.4'

services:
  app:
    environment:
      - ASPNETCORE_ENVIRONMENT=Development
      - ASPNETCORE_URLS=https://+:443;http://+:80
      - AWS__Profile=DevProfile # 追加
    ports:
      - "80"
      - "443"
    volumes:
      - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro
      - ${APPDATA}/ASP.NET/Https:/root/.aspnet/https:ro
      - ${USERPROFILE}/.aws:/root/.aws/:ro # 追加

プロファイルからアクセスキーとシークレットキーが取得できていますね。

まとめ

  • AWSに接続する場合はできるだけAWSプロファイルを利用する。
  • ローカルでDockerコンテナの中からAWSに接続する場合はホストの.awsディレクトリをマウントしてAWSプロファイルを利用する。