見出し画像

AWS KMS SSMでセキュアに環境変数を扱う on Terraform

初めに

本記事は自分が業務で経験した内容を忘れない為に備忘録的に記載しています。色々アドバイスをいただけるとありがたいです。

アジェンダ 
・SSMとは
・KMSとは
・terraformでの記述方法
・ECS タスク定義に変数として埋め込む

SSMとは

インスタンス管理を行う機能群であり、多様な機能があります。セッションマネージャ等々...ほんとに他種多様な機能があります。
ここでssm parameter storeに焦点を合わせて説明します。

parameter store
AWS Systems Manager パラメータストア (パラメータストア) は、設定データ管理と機密管理のための安全な階層型ストレージを提供します。パスワード、データベース文字列、Amazon Machine Image (AMI) ID、ライセンスコードなどのデータをパラメータ値として保存することができます。

parameter storeを用いて環境変数を保存しておくことができます。そしてこの変数をどうやってセキュアに保存し参照するのか。それはKMSというサービスを活用し変数を暗号化していきます。

 KMSとは

データの暗号化/復号用の鍵をAWS上で管理作成できるマネージドサービスです。

データを暗号化するカスタマーキー。
その暗号化に使用したキーを暗号化するマスターキー(鍵の鍵)からなります。
簡単な概要ですが、まずマスターキーをaws cli or cosoleで作成します。そしてそのマスターキーを元にカスタマーキーを作成しデータを暗号化していきます。マスターキーはAWS 上で管理され、カスタマーキーはローカルで管理が可能です。詳しくは下記概要を貼っておきます。

さてkmsの概要はここまでにしてssm parameter storeではどうやって使用していくかというと。
parameter storeで変数を登録する際に別途作成したKMSキーIDを一緒に登録します。コンソールからの操作だと下記の様になります。

スクリーンショット 2021-06-22 20.24.40

上記で変数の暗号化は完了です。

terraformでの記述方法
上記の内容をterraformで記述します。

#kms.tf
resource "aws_kms_key" "application" {
  description         = "A key to encrypt sensitive data in application"
  enable_key_rotation = true
  tags = {
    Group       = var.project
    Environment = var.env
  }
}


#ssm.tf
resource "aws_ssm_parameter" "mysql_password" {
  name        = "/${var.project}/${var.env}/mysql_password"  #ssmの階層
  
  description = "The parameter for mysql master password"
  key_id      = aws_kms_key.application.key_id
  type        = "SecureString"
  value       = var.rds_password

  tags = {
    Group       = var.project
    Environment = var.env
  }
}

上記でセットアップは完了!もし変数を増やしたい場合はssm_parameterを増やしてやる!あとはterraform applyでOK!
ssmで記載しているvalueに関してはvariable.tf、terraform.tfvarsで管理して、terraform.tfvarsに関してはgit ignoreを忘れず!


ここまではssm、kmsの記述でした。次にECS側からSSMの環境変数を取得できるようにIAMを設定しましょう!
実際にpolicyを付与するのはタスク実行ロールです。理由としてはコンテナが立ち上がる時にssmから変数を取得するからです。

#=========================================
#ecs task execution role
#=========================================
data "aws_iam_policy_document" "ecs_task_execution_role_document" {
 statement {
   actions = [
     "sts:AssumeRole"
   ]

   principals {
     type = "Service"
     identifiers = [
       "ecs.amazonaws.com",
       "ecs-tasks.amazonaws.com"
     ]
   }
 }
}

resource "aws_iam_role" "ecs_service_role" {
 name               = "${var.env}_${var.project}_ecs_task_execution_role"
 path               = "/"
 assume_role_policy = data.aws_iam_policy_document.ecs_task_execution_role_document.json
}


#=============================================
#attach task execution policy
#=============================================
data "aws_iam_policy_document" "ecs_task_execution_role_policy_document" {
 statement {
   effect = "Allow"

   actions = [
     "cloudwatch:PutMetricData",
     "ec2:DescribeVolumes",
     "ec2:DescribeTags",
     "logs:PutLogEvents",
     "logs:DescribeLogStreams",
     "logs:DescribeLogGroups",
     "logs:CreateLogStream",
     "logs:CreateLogGroup",
     "kms:Decrypt",      #複合化する為の権限
     
     "ssm:GetParameters" #ssmから環境変数を取得するpolicy
     
   ]

   resources = [
     aws_ssm_parameter.mysql_password.arn  #取得したい環境変数のarn
     
   ]
 }
}

resource "aws_iam_role_policy" "ecs_task_execution_role_policy" {
 name   = "${var.env}_${var.project}_ecs_task_execution_policy"
 role   = aws_iam_role.ecs_service_role.id
 policy = data.aws_iam_policy_document.ecs_task_execution_role_policy_document.json
}

resource "aws_iam_role_policy_attachment" "policy_ecs_task_execution_role_policy_to_ecs_service_role_attachment" {
 role       = aws_iam_role.ecs_service_role.name
 policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

arnをどこで取得するか分からない場合はcliで叩いて取得しちゃいましょう!
cliを使う練習にもなります!

aws ssm get-parameter --name "MyStringParameter"

ここまでterraform側で準備は完了しました。

ECS タスク定義に変数として埋め込む

最後にdeployする為のタスク定義を記述していきます。
自分が勤めている会社ではcircleciを使用しています。

{
   "networkMode": "awsvpc",
   "cpu": "512",
   "memory": "1024",
   "requiresCompatibilities": ["FARGATE"],
   "executionRoleArn": "arn:aws:iam::xxxxxxx:role/prod_evinote_ecs_task_execution_role",
   "taskRoleArn": "arn:aws:iam::xxxxxxxxxx:role/prod_evinote_ecs_task_role",
   "containerDefinitions":
   [
     〜  中略  〜
           ],
           "secrets": [
               {
                   "name": "DB_PASSWORD",
                   "valueFrom": "$DB_PASSWORD_ARN_PROD"
               },
           ]
       }
   ]
}

コードの記述量が多いので一部省略させていただきました。
実際に必要な箇所はsecretsの箇所です。そしてタスク定義に環境変数として埋め込むためにはvalueFromのkeyが必要です。
valueFromのvalueとしてはssmで登録した変数のarnを記述します。今回はcircle ciの環境変数としてarmを登録しいます。

以上で全体の作業は終了しました!あとは無事デプロイでいるか祈るだけ!

終わりに

やぱっりterraformを用いるとコンソール画面からぽちぽちするよりも見通しがよくなと日々感じます。
まだまだ駆け出しですが、色々リソース構築して慣れていきだいです!

最後に参考にした記事を紹介します。安定のクラスメソッド様〜〜


この記事が気に入ったらサポートをしてみませんか?