ESM アジャイル事業部 開発者ブログ

永和システムマネジメント アジャイル事業部の開発者ブログです。

AWS ECS + Active Storage(AWS S3) で秘匿情報を秘匿して利用する方法

안녕하세요.이토 쿠니히코입니다.*1
Nizi Project から韓国ドラマやK-POP を聞くようになった kunitoo です。

Webアプリケーションを書いていると、アイコン画像やPDFなどファイルをアップロードして保存したいというケースに遭遇すると思います。私が関わってきたプロジェクトではファイル添付する要件が十中八九入っていました。
ファイル添付を実現する Ruby のライブラリは様々ありますが、Rails 5.2 から標準でファイルをアップロードして Active Record モデルにファイルを添付する機能の Active Storage が利用できるようになっています。

今回は RailsAWS ECS で動作させる場合に Active Storage + S3 を Rails サーバーを介して利用する際に秘匿情報を秘匿して利用する設定方法について書いていきます。

簡単な設定方法

Rails ガイド Active Storage の概要 2 セットアップAmazon S3サービスを利用する設定方法について以下のように記載されています。

# config/storage.yml
amazon:
  service: S3
  access_key_id: ""
  secret_access_key: ""
  region: ""
  bucket: ""

一番簡単な設定方法としては、IAM で S3 にフルアクセスできるユーザーを作成し、アクセスキーを発行して、その access_key_id, secret_access_keyYAML に記載するという方法があります。

しかし、この方法はセキュリティとしてあまりよろしくありません。 よろしくない点としては以下があります。

  1. IAM ユーザーのAWS S3 アクセスできる権限が強すぎるため、Active Storage に利用するバケット以外にもアクセスできてしまう
  2. IAM のアクセスキーは流用することができるため、今回利用したい Rails サーバー(ECS) 以外からもアクセスすることができ、キーが漏れると悪用される可能性がある

秘匿情報を秘匿した設定方法

今回はアクセスキーを作成せずに、ECS に必要な必要最低限の権限を付加することで、安全に Active Storage を利用する方法を紹介します。 設定のポイントは以下です。

  • S3 Bucket を private で作成する
  • S3 Bucket にアクセス可能な必要最低限の IAM ポリシーを作成する
  • ECS Task ロールに IAM ポリシーをアタッチする

以下に Terraform を使って実行可能な具体的な設定を記載します。

S3 Bucket を private で作成する

Active Storage で利用する S3 を private で作成します。 これにより、外部からのアクセスを防ぎます。 この状態ではどこからもアクセスができません。

resource "aws_s3_bucket" "images" {
  bucket = "${local.server_subdomain}images.example.com"
  acl    = "private"
}

S3 Bucket にアクセス可能な必要最低限の IAM ポリシーを作成する

ここのポイントは Rails ガイドにも記載されている、s3:ListBucket、s3:PutObject、s3:GetObject、s3:DeleteObject の4つのパーミッションを付与することと、resources に bucket/ とbucket を指定する点です。
はじめはすべてのオブジェクト (bucket/
) の指定だけで動作するかと思ったのですが、 バケット全体 (bucket) の指定がないと Active Storage で purge をする際に失敗します。

data "aws_iam_policy_document" "allow_s3_policy" {
  statement {
    actions = ["s3:ListBucket", "s3:PutObject", "s3:GetObject", "s3:DeleteObject"]
    resources = [
      "${aws_s3_bucket.images.arn}/*",
      "${aws_s3_bucket.images.arn}"
    ]
  }
}

resource "aws_iam_policy" "allow_s3_policy" {
  name        = "${var.environment}_allow_s3"
  description = "allow s3 bucket"

  policy = "${data.aws_iam_policy_document.allow_s3_policy.json}"
}

ECS Task ロールに IAM ポリシーをアタッチする

ここのポイントはタスク実行ロールではなくタスクロールにIAMポリシーを設定する点です。
参考のため、ECS task definition を定義する部分までを載せていますが、aws_iam_role_policy_attachment が重要な点です。

resource "aws_iam_role_policy_attachment" "ecs_task_role_allow_s3" {
  role       = aws_iam_role.ecs_task_role.name
  policy_arn = aws_iam_policy.allow_s3_policy.arn
}

resource "aws_iam_role" "ecs_task_role" {
  name = "${var.environment}EcsTaskRole"
  path = "/"

  assume_role_policy = "${data.aws_iam_policy_document.ecs_assume_role_policy.json}"
}

data "aws_iam_policy_document" "ecs_assume_role_policy" {
  statement {
    principals {
      type        = "Service"
      identifiers = ["ecs-tasks.amazonaws.com"]
    }
    actions = ["sts:AssumeRole"]
  }
}

resource "aws_ecs_task_definition" "server" {
  family                = "${var.environment}-server"
  container_definitions = data.template_file.server_container_definitions.rendered
  execution_role_arn    = aws_iam_role.ecs_task_execution.arn
  task_role_arn         = aws_iam_role.ecs_task_role.arn

  network_mode = "awsvpc"

  cpu                      = 256
  memory                   = 512
  requires_compatibilities = ["FARGATE"]
}

Active Storage の設定

ポリシーを設定することで Rails の設定から access_key_id, secret_access_key は不要になり以下を書いておくだけでよくなります。

# config/storage.yml
amazon:
  service: S3
  region: ap-northeast-1
  bucket: <%= ENV.fetch("AWS_S3_BUCKET_NAME") %>

まとめ

AWS ECS で Rails サーバーを動かす際の Active Storage の設定を IAM ポリシーを適切に設定することで、アクセスキー管理せず、より安全に S3 を利用できるようになりました。
また、具体的な設定方法として Terraform で示しました。
(いくつか記載していないリソースがあるのでそのままでは動作しませんが、実際に構築し動作させたものをベースに記載しています)

AWS で Active Storage を使う際の参考になればと思います。

*1:こんにちは。伊藤邦彦です。