CLIでAssumeRoleしてみる

Assume Roleについてなんとなく理解はしているつもりでしたが、 手元で動かしたことはなかったのでCLIでAssume Roleをしてみて理解を深めようと思います。

Assume Roleとは?

Returns a set of temporary security credentials that you can use to access AWS resources. These temporary credentials consist of an access key ID, a secret access key, and a security token. Typically, you use AssumeRole within your account or for cross-account access.
AWSのリソースにアクセスするための一時的なセキュリティ認証情報を返します。 これらの一時的な認証情報は「access key ID」「 secret access key」「security token」で構成されています。一般的にあなたのアカウントやクロスアカウントへのアクセスでAssumeRoleを使います。

docs.aws.amazon.com

つまりAssumeRoleとは、AWSのリソースにアクセスするための一時的なクレデンシャルを返す仕組みです。

動かしてみる

今回下記のような状況を想定してAssumeRoleしてみます。

  • とあるIAMユーザ(以下IAMユーザAとします)は自身のAWSアカウント内で、Action iam:ListPoliciesの実行が許可されていません。そのため aws iam list-policiesを実行しても失敗します。
  • IAMユーザAがiam:ListPoliciesの実行が許可されているIAMロールのAssumeRoleを実行し、一時的なクレデンシャルを受け取り、それを使用してaws iam list-policiesを実行します。

それでは必要なIAMポリシーやIAMロールをTerraformで作成していきます。

IAMポリシーとロールの作成

iam:ListPoliciesを許可するポリシーを作成します。

# iam:ListPoliciesを許可するポリシーのドキュメント
data "aws_iam_policy_document" "iam-list-policies-policy-document" {
  statement {
    sid = "IamListPoliciesPolicyDocument"
    effect = "Allow"
    actions = [
      "iam:ListPolicies"
    ]
    resources = ["*"]
  }
}

# iam:ListPoliciesを許可するポリシー
resource "aws_iam_policy" "iam-list-policies-policy" {
  name = "iam-list-policies-policy"
  policy = data.aws_iam_policy_document.iam-list-policies-policy-document.json
}

次にIAMロールの作成です。 次のポリシードキュメントは、作成するIAMロールを誰がAssumeRoleすることができるかを書いたものです。

# 誰にロールを引き渡すかを書いたポリシー
data "aws_iam_policy_document" "iam-test-role-policy-document" {
  statement {
    sid = "IamTestRolePolicyDocument"
    effect = "Allow"
    actions = [
      "sts:AssumeRole"
    ]
    principals {
      type = "AWS"
      identifiers = ["arn:aws:iam::000000000000:user/user_a"]
    }
  }
}

上記ポリシーを持ったIAMロールを作成します。

# ロールの作成
resource "aws_iam_role" "iam-list-policies-role" {
  name = "iam-test-role"
  assume_role_policy = data.aws_iam_policy_document.iam-test-role-policy-document.json
}

初めに作成したiam:ListPoliciesを許可するポリシーを上記IAMロールにアタッチします。

# ロールに`iam:ListPolicies`を許可するポリシーをアタッチ
resource "aws_iam_role_policy_attachment" "iam-list-policies-role-iam-list-policies-policy-attachment" {
  role       = aws_iam_role.iam-list-policies-role.name
  policy_arn = aws_iam_policy.iam-list-policies-policy.arn
}

以上でIAMユーザAがiam:ListPoliciesの実行可能なIAMロールiam-test-roleをAssumeRoleすることが可能になりました。

AWS CLIからAssumeRoleしてみる

初めにIAMユーザAで下記コマンドを実行してみます。

aws iam list-policies --profile user_a

ユーザAはiam:ListPoliciesが許可されていないので失敗します。

An error occurred (AccessDenied) when calling the ListPolicies operation〜

それでは次にIAMユーザAでAssumeRoleしてみます。 --role-session-nameは任意の文字列です。

aws sts assume-role --role-arn arn:aws:iam::000000000000:role/iam-test-role --role-session-name hoge --profile user_a

すると下記のように一時的なクレデンシャルが返却されます。

{
    "Credentials": {
        "AccessKeyId": "WWWWWWWWWW",
        "SecretAccessKey": "xxxxxxxxxx",
        "SessionToken": "yyyyyyyyyy",
        "Expiration": "2023-11-03T15:10:06+00:00"
    },
    "AssumedRoleUser": {
        "AssumedRoleId": "ZZZZZZZZZZ:hoge",
        "Arn": "arn:aws:sts::000000000000:assumed-role/iam-test-role/hoge"
    }
}

これを環境変数にセットして

export ACCESS_KEY_ID=WWWWWWWWWW
export SECRET_ACCESS_KEY=xxxxxxxxxx
export SESSION_TOKEN=yyyyyyyyyy

下記コマンドを実行すると、今回は成功しました!

aws iam list-policies

AssumeRoleしている状態を抜ける場合は、環境変数を破棄します。

unset AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY AWS_SESSION_TOKEN

AssumeRoleを用いて、許可されていないアクションを実行する流れを確認することができました。

LocalStackでAWS KMSを操作してみる

AWS KMS。 AWS認定ソリューションアーキテクトアソシエイトの勉強をしていた時や、仕事で読んだコードで度々見かけていましたが、 実際に自分で操作をしたことがなかったのでLocalStackでKMSを操作してみました。

AWS KMSってなに?

AWS Key Management Service (KMS) を利用すると、データ保護に使用される暗号化キーを一元管理できます。

aws.amazon.com

つまり、データ保護に使用する暗号化キーをAWS側が管理してくれるので、我々が暗号化キーを管理する必要がありません。 IAM ユーザやIAM ロールに適切なポリシーを設定することで、暗号化キーを安全に管理することができます。

KMSをLocalStackで操作してみる

ローカルにpassword.txtというファイルを用意しました。 ファイルの中には平文で mypassword と記述してあります。

$ ls -la
password.txt

$ cat password.txt
mypassword

KMSキーを作成する

平文のパスワードを暗号化したいです。 しかし、暗号化するためのキーは自分で管理したくありません。 暗号化されたパスワードと暗号化に使用したキーの両方を盗まれた場合にパスワードを複合されてしまうからです。 そこでKMSの出番です。KMSは暗号化に使用するキーであるKMSキーを管理してくれます。

$ awslocal kms create-key

{
    "KeyMetadata": {
        "AWSAccountId": "000000000000",
        "KeyId": "e6a25557-e58a-4a47-929d-5404b4ac73e1",
        "Arn": "arn:aws:kms:ap-northeast-1:000000000000:key/e6a25557-e58a-4a47-929d-5404b4ac73e1",
        "CreationDate": "2023-09-13T10:15:00.026118+09:00",
        "Enabled": true,
        "Description": "",
        "KeyUsage": "ENCRYPT_DECRYPT",
        "KeyState": "Enabled",
        "Origin": "AWS_KMS",
        "KeyManager": "CUSTOMER",
        "CustomerMasterKeySpec": "SYMMETRIC_DEFAULT",
        "KeySpec": "SYMMETRIC_DEFAULT",
        "EncryptionAlgorithms": [
            "SYMMETRIC_DEFAULT"
        ],
        "MultiRegion": false
    }
}

KMSキーを作成するコマンドを実行するとKMSキーの情報が’返却されます。 暗号化をする際にKeyIdを使用します。

KMSキーを使用して平文のパスワードを暗号化する

下記のコマンドで平文のパスワードpassword.txtを暗号化し、 encrypted_password.txtを作成します。 KMSキーを作成した際に取得したKeyId--key-idに渡します。

$ awslocal kms encrypt \
    --key-id e6a25557-e58a-4a47-929d-5404b4ac73e1 \
    --plaintext fileb://password.txt \
    --output text \
    --query CiphertextBlob | base64 \
    --decode > encrypted_password.txt

暗号化されたパスワードの書かれているencrypted_password.txtを確認すると 平文のパスワードは暗号化されており、KeyIdが記述されています。

$ cat encrypted_password.txt
e6a25557-e58a-4a47-929d-5404b4ac73e1?{o???'>pZ??l??A??1?????Ui?&??b??9??+?"??

このKeyId が記述されていることにより、encrypted_password.txtを複合化する際にKMSは このKeyId を持つKMSキーを使用します。

平文パスワードの削除

暗号化されたパスワード encrypted_password.txtがあり、 それを複合化するためのKMSキーがAWS KMSで管理されています。 平文のパスワードpassword.txtをローカルに置いておく必要はないので削除します。

$ rm password.txt
$ ls -la
encrypted_password.txt

暗号化されたパスワードの複合化

暗号化されたパスワードにKMSキーのKeyIdが記述されているので、 複合化する際はKMSにKeyIdを知らせる必要はありません。

$ awslocal kms decrypt \
    --ciphertext-blob fileb://encrypted_password.txt \
    | jq -r '.Plaintext' | base64 --decode > password.txt
$ ls -la
encrypted_password.txt
password.txt

$ cat password.txt
mypassword

暗号化されたパスワードを平文で取得できました!

AWS Glue crawlerとAthenaを使ってS3に置いてあるCSVファイルへSQLを実行する

AWS Athenaを使ってS3に置いてあるCSVファイルへSQLを実行する機会はあるのですが、自分で環境構築をしたことがありませんでした。 また、AWS Glueは様々な機能を持っていて、Athenaとどう関係があるのかを理解できていませんでした。 そこで、Athenaの環境構築をTerraformでやってみます。

最低限下記の3つを作成する必要があります。

テーブルについては手動で作成することもできますが、Glueクローラーを使ってCSVファイルから自動で作成することもできます。 今回はGlueクローラーを使います。

Glueクローラーの作成

Glueクローラーを作成するために下記を行います。

  • テーブルを登録するデータベースの作成
  • Glue Crawlerを作成して、CSVファイルが保存されているS3バケットを指定
resource "aws_glue_catalog_database" "aws_glue_catalog_database_example" {
  name = "athena-example-database"
}

data "aws_s3_bucket" "hoge_test_athena_bucket" {
  bucket = "hoge-test-athena"
}

data "aws_iam_role" "glue_crawler_athena_example_role" {
  name = "glue_crawler_athena_example_role"
}

resource "aws_glue_crawler" "glue_crawler_athena_example" {
  database_name = aws_glue_catalog_database.aws_glue_catalog_database_example.name
  name = "athena-example-crawler"
  role = data.aws_iam_role.glue_crawler_athena_example_role.arn

  s3_target {
    path = "s3://${data.aws_s3_bucket.hoge_test_athena_bucket.bucket}"
  }
}

Glueクローラーを実行してテーブルの作成

今回は下記の内容でCSVファイルを用意し、hoge-test-athena bucketへ置きました。

name,amount,price,producer,country
apple,10,1500,Michael,Japan

それでは先ほど作成したクローラの名前を指定して、クローラを実行します。

aws glue start-crawler --name athena-example-crawler

しばらくするとAWSコンソール > AWS Glue > Data Catalog > Databases > Tables にhoge_test_athenaというテーブルが作成され、テーブルスキーマが生成されていることが確認できました。

SQLの実行

Athenaを使ってSQLを実行します。 実行するにはワークグループを指定する必要があります。 今回は下記のようにdeveloperという名前のワークグループを作成しました。 CloudWatchメトリクスは今回不要なのでfalseに、 また、result_configuration.output_locationでクエリの結果を保存するS3 bucketを指定しています。

data "aws_s3_bucket" "hoge_test_athena_result_bucket" {
  bucket = "hoge-test-athena-result"
}

resource "aws_athena_workgroup" "developer" {
  name = "developer"

  configuration {
    publish_cloudwatch_metrics_enabled = false

    result_configuration {
      output_location = "s3://${data.aws_s3_bucket.hoge_test_athena_result_bucket.bucket}"
    }
  }
}

それではクローラによって作成されたテーブルに対してSELECT文を実行してみます。 ワークグループはdeveloperを指定します。

期待通りの結果が確認できました。