Pikeを使ってTerraform実行ユーザー用の最小限のIAMポリシーを生成する

2022.12.14

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

「terraform apply を実行するユーザーやロールにも最小限の権限を付与するようにしたい。」

Terraformで色々管理しているから、Terraform実行ユーザーの権限は強めにしているけど少し不安。最小限の権限を割り当てる方法はないかな?

そんな時には、Pikeを使ってみるのもいいかもしれません。

Pikeとは

一言で言うと、TerraformのコードからTerraform実行に必要な最小限のIAMポリシーを生成してくれるツールです。

JamesWoolfenden/pike: Pike is a tool for determining the permissions or policy required for IAC code

Terraform実行用IAMユーザー・IAMロールの権限

例えば、TerraformでAWSインフラを管理しているとします。 terraform applyを実行するために、AWSリソースを操作する権限を持ったIAMユーザーやIAMロールが必要になります。

このIAMユーザーやIAMロールに渡す権限ですが、理想は最小限の権限であるべきです。 しかし、Terraformで管理するリソースが増えてくると最小限の運用の手間は大きいです。

そのため、セキュリティ的には良くないと分かっていても、Terraform実行用のIAMユーザーやIAMロールにはAdministratorAccessなどの強い権限のIAMポリシーを付与して運用することが多いかと思います。

Pikeを使うと

Pikeを使えば、Terraformのコードからterraform applyに必要な最小限のIAMポリシーを作成するといったことができます。

以下は実行例です。 Pikeはterraformをサポートしていて、AWSプロパイダー以外にもGoogle CloudやAzureのプロパイダーもサポートしています。(2022/12/12時点)

$pike scan -o terraform -d ../modules/aws/terraform-aws-activemq
resource "aws_iam_policy" "terraformXVlBzgba" {
  name        = "terraformXVlBzgba"
  path        = "/"
  description = "Add Description"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "ec2:AuthorizeSecurityGroupEgress",
                "ec2:AuthorizeSecurityGroupIngress",
                "ec2:CreateNetworkInterface",
                "ec2:CreateNetworkInterfacePermission",
                "ec2:CreateSecurityGroup",
                "ec2:CreateTags",
                "ec2:DeleteNetworkInterface",
                "ec2:DeleteNetworkInterfacePermission",
                "ec2:DeleteSecurityGroup",
                "ec2:DeleteTags",
                "ec2:DescribeAccountAttributes",
                "ec2:DescribeInternetGateways",
                "ec2:DescribeNetworkInterfaces",
                "ec2:DescribeSecurityGroups",
                "ec2:DescribeSubnets",
                "ec2:DescribeVpcs",
                "ec2:DetachNetworkInterface",
                "ec2:RevokeSecurityGroupEgress",
                "ec2:RevokeSecurityGroupIngress"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "kms:CreateKey",
                "kms:DescribeKey",
                "kms:EnableKeyRotation",
                "kms:GetKeyPolicy",
                "kms:GetKeyRotationStatus",
                "kms:ListResourceTags",
                "kms:ScheduleKeyDeletion",
                "kms:TagResource",
                "kms:UntagResource"
            ],
            "Resource": "*"
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "mq:CreateBroker",
                "mq:CreateConfiguration",
                "mq:CreateTags",
                "mq:CreateUser",
                "mq:DeleteBroker",
                "mq:DeleteTags",
                "mq:DeleteUser",
                "mq:DescribeBroker",
                "mq:DescribeConfiguration",
                "mq:DescribeConfigurationRevision",
                "mq:DescribeUser",
                "mq:RebootBroker",
                "mq:UpdateBroker",
                "mq:UpdateConfiguration",
                "mq:UpdateUser"
            ],
            "Resource": "*"
        }
    ]
})
}

試してみる

サンプルコード

DynamoDBとSQSのモジュールを作って、main.tfで呼び出すようにしています。 ディレクトリ構成は以下になっています。

このサンプルコードを使って、Pikeではどのようにポリシーが生成されるか見ていきます。

├── backend.tfvars
├── main.tf
├── module
│   ├── dynamodb
│   │   └── main.tf
│   └── sqs
│       └── main.tf
└── provider.tf

main.tf

module "sqs" {
  source = "./module/sqs"
}

module "dynamodb" {
  source = "./module/dynamodb"
}

module/sqs/main.tf

resource "aws_sqs_queue" "this" {
  name                        = "terraform-example-queue"
}

module/dynamodb/main.tf

resource "aws_dynamodb_table" "this" {
  hash_key     = "partition_key"
  range_key    = "sort_key"
  attribute {
    name = "partition_key"
    type = "S"
  }
  attribute {
    name = "sort_key"
    type = "S"
  }
}

インストール

brewでインストールできます。

$ brew tap jameswoolfenden/homebrew-tap
$ brew install jameswoolfenden/tap/pike

ポリシーを生成してみる

まずは、モジュールを呼び出すmain.tfがあるディレクトリでPikeを実行してみます。

$ pike scan .
resource "aws_iam_policy" "terraform_pike" {
  name_prefix = "terraform_pike"
  path        = "/"
  description = "Pike Autogenerated policy from IAC"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:DeleteTable",
                "dynamodb:DescribeContinuousBackups",
                "dynamodb:DescribeTable",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:ListTagsOfResource",
                "dynamodb:UpdateTable",
                "dynamodb:UpdateTimeToLive"
            ],
            "Resource": [
                "*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "sqs:CreateQueue",
                "sqs:DeleteQueue",
                "sqs:GetQueueAttributes",
                "sqs:ListQueueTags",
                "sqs:SetQueueAttributes"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
})
}

ディレクトリを指定して実行可能なので、module単位でポリシーを作成することもできます。

 pike scan -d module/dynamodb
resource "aws_iam_policy" "terraform_pike" {
  name_prefix = "terraform_pike"
  path        = "/"
  description = "Pike Autogenerated policy from IAC"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "dynamodb:CreateTable",
                "dynamodb:DeleteTable",
                "dynamodb:DescribeContinuousBackups",
                "dynamodb:DescribeTable",
                "dynamodb:DescribeTimeToLive",
                "dynamodb:ListTagsOfResource",
                "dynamodb:UpdateTable",
                "dynamodb:UpdateTimeToLive"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
})
}

生成したポリシーをデプロイする

ポリシーを生成できることが分かったので、ポリシーを実際にデプロイしてみます。

$ pike make .
3:30PM DBG terraform init at /Users/sato.masaki/work/src/github.com/msato0731/terraform-sample/pike-demo
3:30PM DBG aws role create/updated arn:aws:iam::111111111111111:role/terraform_pike_20221214063044638700000002
3:30PM DBG arn:aws:iam::111111111111111:role/terraform_pike_20221214063044638700000002

コマンドを実行するとIAMロールとIAMポリシーが作成されます。

Pikeを使うことで、簡単にIAMポリシーの生成ができました。

コマンド実行後に、ディレクトリを確認すると以下のように.pikeディレクトリが作成されていてIAMポリシーとロールを作成するためのtfファイルが作成されました。 pike makeではこのtfファイルを使って、IAMポリシーとロールを作成しています。

$ tree -a -L 2
├── .pike
│   ├── .terraform
│   ├── .terraform.lock.hcl
│   ├── aws_iam_role.terraform_pike.tf
│   ├── pike.generated_policy.tf
│   ├── terraform.tfstate
│   └── terraform.tfstate.backup
├── .terraform
│   ├── modules
│   ├── providers
│   └── terraform.tfstate
├── .terraform.lock.hcl
├── backend.tfvars
├── main.tf
├── module
│   ├── dynamodb
│   └── sqs
└── provider.tf

おわりに

TerraformのコードからIAMポリシー生成してくれるツールPikeの紹介でした。

臼田さんから教えてもらって面白いツールだなと思ったのでブログにしました。

Terraform実行ユーザーのIAMポリシーに悩んでいる方の助けになるツールかと思います。

現在(2022/12/14時点)は一部機能が、AWS Providerのみに対応しているようです。

今後の発展が楽しみなツールです。

以上、AWS事業本部の佐藤(@chari7311)でした。