Spring BootでAWS Secrets Managerを使う


AWS(ECS / EC2)で、データベースのパスワードやRSAの秘密鍵を使う場合は、 AWS Secrets Managerから取得すると良い

Terraformの設定

AWS Secrets Manager

AWS Secrets Managerをterraformで作成する

resource "aws_secretsmanager_secret" "something" {
  name       = "${var.app_name}/${terraform.workspace}/something"
  kms_key_id = "${aws_kms_key.main.key_id}"
}

resource "aws_secretsmanager_secret_version" "something" {
  secret_id     = "${aws_secretsmanager_secret.something.id}"
  secret_string = "{}"

  lifecycle {
    ignore_changes = ["secret_string"]
  }
}

IAM Role

AWS Secrets Managerから読みこむための権限を追加する
AWS Secrets Managerのデータは暗号化されているので、復号のためにKMSへのアクセス権も必要になる

data "aws_caller_identity" "current" {}


resource "aws_iam_role_policy" "something" {
  name = "${var.app_name}_${terraform.workspace}_something_secrets"
  role = "${aws_iam_role.something.id}"

  policy = <<POLICY
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [ "secretsmanager:GetSecretValue" ],
      "Resource": "arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.current.account_id}:secret:${var.app_name}/${terraform.workspace}/something-*"
    },
    {
      "Action": [
        "kms:Decrypt"
      ],
      "Resource": "${aws_kms_key.main.arn}",
      "Effect": "Allow"
    }
  ]
}
POLICY
}

ECS

Spring BootからAWS Secrets Managerにアクセスするための情報を環境変数にセットする

resource "aws_ecs_task_definition" "something" {
  container_definitions = <<CONTAINER_DEF
  [
    {
      ...
      "environment": [
        {
          "name": "AWS_SECRETSMANAGER_ENABLED",
          "value": "true"
        },
        {
          "name": "AWS_SECRETSMANAGER_NAME",
          "value": "${var.app_name}/${terraform.workspace}/something"
        },
        {
          "name": "AWS_SECRETSMANAGER_REGION",
          "value": "${var.aws_region}"
        }
      ]
    }
  ]
  CONTAINER_DEF
}

依存関係

依存関係にAWS SDK for Javaを追加する

// build.gradle
buildscript {
  dependencies {
    classpath "io.spring.gradle:dependency-management-plugin:1.0.3.RELEASE"
  }
}
apply plugin: "io.spring.dependency-management"

dependencyManagement {
    imports {
        mavenBom 'com.amazonaws:aws-java-sdk-bom:1.11.343'
    }
}
dependencies {
  compile "com.amazonaws:aws-java-sdk-secretsmanager"
}

SpringのEnvironmentにAWS Secrets Managerから取得したデータをセットする

package org.example

import com.amazonaws.services.secretsmanager.*
import com.amazonaws.services.secretsmanager.model.*
import com.fasterxml.jackson.databind.*
import org.springframework.boot.*
import org.springframework.boot.env.*
import org.springframework.core.env.*

class AwsSecretsManagerEnvironmentPostProcessor : EnvironmentPostProcessor {
    override fun postProcessEnvironment(env: ConfigurableEnvironment, application: SpringApplication) {
        // AWS上で動いているときのみにAWS Secrets Managerを使う
        if (!env.getProperty("aws.secretsmanager.enabled", Boolean::class.java, false)) {
            return
        }

        val req = GetSecretValueRequest()
        req.secretId = env.getProperty("aws.secretsmanager.name") ?: return

        val region = env.getProperty("aws.secretsmanager.region") ?: return
        val client = AWSSecretsManagerClientBuilder.standard()
                .withRegion(region)
                .build()
        val secretString = client.getSecretValue(req).secretString

        @Suppress("UNCHECKED_CAST")
        val map = ObjectMapper().readValue(secretString, Map::class.java) as Map<String, Any>
        env.propertySources.addFirst(MapPropertySource("secrets", map))
    }
}

EnvironmentPostProcessorをSpringに登録する

src/main/resources/META-INF/spring.factoriesに追記する

org.springframework.boot.env.EnvironmentPostProcessor=org.example.AwsSecretsManagerEnvironmentPostProcessor

See also