LoginSignup
2
0

More than 1 year has passed since last update.

認証戦略を用いてIAMユーザを作成する

Posted at

随分前にTerraformでIAMグループ・ユーザを作成するをリファクタリングしたのでUpdateします。

認証戦略としては、属性ベースのアクセスコントロール (ABAC) 、ロールベースのアクセスコントロール (RBAC) がありますが、ABACもRBACもすべてのアクションに対して設定可能なわけではないので適宜Mixさせます。
共通しているのは 「権限のガードレールによりオペミスや悪意のあるアクセスを防ぎ、重大インシデントを未然に防止する」 です。

  • 基本設計
    Untitled (4).png

  • 下記ではIAMユーザーがメンバーになれるグループ数は上限緩和ができない10に設定されているので、注意が必要。

Untitled (3).png


  • Dir構成
iam
├── group-path
│   ├── dev-user
│   │   ├── iam_user.tf
│   │   ├── main.tf
│   │   ├── output.tf
│   │   └── variable.tf
├── policy
│   ├── ip-restriction
│   │   ├── iam_policy.tf
│   │   └── main.tf
│   ├── password-mfa
│   │   ├── iam_policy.tf
│   │   └── main.tf
│   └── switch-role
│       ├── dev
│       │   ├── iam_policy.tf
│       │   └── main.tf
├── role
│   ├── dev_role
│   │   ├── iam_role.tf
│   │   └── main.tf
  • dev-userというPATHの括りでuser作成する
iam/group-path/dev-user
├── iam_user.tf
├── main.tf
├── output.tf
└── variables.tf
iam_user.tf
data "aws_caller_identity" "current" {}

resource "aws_iam_user" "dev_user" {
  for_each = var.dev_users

  name          = each.value.name
  path          = each.value.path
  force_destroy = each.value.force_destroy
}

resource "aws_iam_user_login_profile" "dev_user_login_profile" {
  for_each = { for k, v in var.dev_users : k => v if v.create_login_profile }

  user                    = each.value.name
  pgp_key                 = "keybase:development"
  password_reset_required = true
  password_length         = "10"

  lifecycle {
    ignore_changes = [
      password_length,
      password_reset_required,
      pgp_key,
    ]
  }
}

resource "aws_iam_access_key" "dev_user_access_key" {
  for_each = var.dev_users

  user    = each.value.name
  pgp_key = "keybase:development"
}

resource "aws_iam_user_policy_attachment" "dev_user_policy_attach_1" {
  for_each = var.dev_users

  user       = each.value.name
  policy_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/ip_restriction_policy"
}

resource "aws_iam_user_policy_attachment" "dev_user_policy_attach_2" {
  for_each = var.dev_users

  user       = each.value.name
  policy_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:policy/password_mfa_policy"
}
output.tf
output "user1_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user1"].encrypted_secret
}

output "user1_id" {
  value = aws_iam_access_key.dev_user_access_key["user1"].id
}

output "user1_user" {
  value = aws_iam_access_key.dev_user_access_key["user1"].user
}

output "user1_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user1"].encrypted_password
}

output "user2_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user2"].encrypted_secret
}

output "user2_id" {
  value = aws_iam_access_key.dev_user_access_key["user2"].id
}

output "user2_user" {
  value = aws_iam_access_key.dev_user_access_key["user2"].user
}

output "user2_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user2"].encrypted_password
}

output "user3_encrypted_secret" {
  value = aws_iam_access_key.dev_user_access_key["user3"].encrypted_secret
}

output "user3_id" {
  value = aws_iam_access_key.dev_user_access_key["user3"].id
}

output "user3_user" {
  value = aws_iam_access_key.dev_user_access_key["user3"].user
}

output "user3_encrypted_password" {
  value = aws_iam_user_login_profile.dev_user_login_profile["user3"].encrypted_password
}
variables.tf
variable "dev_users" {
  type = map(any)

  default = {
    user1 = {
      name                 = "user1"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user2 = {
      name                 = "user2"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user3 = {
      name                 = "user3"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user4 = {
      name                 = "user4"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
    user5 = {
      name                 = "user5"
      path                 = "/dev_user/"
      force_destroy        = true
      create_login_profile = true
    }
  }
}
  • SecretAccessKeyとPasswordの復号化
% echo “xxxxxxxxxxx” | base64 -d | gpg -r name

  • dev_userがスイッチ可能なRole
iam/role
├── dev_role
│   ├── iam_role.tf
│   └── main.tf
iam_role.tf
resource "aws_iam_role" "dev_role" {
  name                 = "dev_role"
  max_session_duration = "43200" // 12hour

  assume_role_policy = jsonencode({
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": [
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user1",
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user2",
          "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/user3"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
 })
}

resource "aws_iam_policy" "dev_ssm_policy" {
  name        = "dev_ssm_policy"
  description = "dev_ssm_policy"

  policy = <<EOF
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:StartSession",
                "ssm:SendCommand"
            ],
            "Resource": [
                "arn:aws:ec2:*:*:instance/*"
            ],
            "Condition": {
                "StringLike": {
                    "ssm:resourceTag/Service": [
                        "*"
                    ]
                }
            }
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:GetConnectionStatus",
                "ssm:DescribeInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssm:TerminateSession"
            ],
            "Resource": [
                "arn:aws:ssm:*:*:session/$${aws:username}-*"
            ]
        }
    ]
}
EOF
}
  • 接続元ip制限
iam/policy/ip-restriction
├── iam_policy.tf
└── main.tf
iam_policy.tf
resource "aws_iam_policy" "ip_restriction_policy" {
  name        = "ip_restriction_policy"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Deny",
            "Action": "*",
            "Condition": {
                "NotIpAddress": {
                    "aws:SourceIp": [
                        "***.***.***.***/32",
                        "***.***.***.***/32"
                    ]
                }
            },
            "Resource": "*"
        },
        {
            "Effect": "Deny",
            "NotAction": [
                "iam:*"
            ],
            "Resource": "*",
            "Condition": {
                "BoolIfExists": {
                    "aws:MultiFactorAuthPresent": "false"
                }
            }
        }
    ]
  })
}

iam/policy/switch-role/dev
├── iam_policy.tf
└── main.tf
iam_policy.tf
resource "aws_iam_policy" "dev_user_switch_role_policy" {
  name = "dev_user_switch_role_policy"
  path = "/dev/"

  policy = jsonencode({
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Resource": "arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/dev_role"
  }
 })
}
  • パスワード変更とMFA設定許可
iam/policy/password-mfa
├── iam_policy.tf
└── main.tf
iam_policy.tf
resource "aws_iam_policy" "password_mfa_policy" {
  name        = "password_mfa_policy"

  policy = jsonencode({
    "Version": "2012-10-17",
    "Statement": [
    	{
    		"Resource": [
                "arn:aws:iam::${data.aws_caller_identity.current.account_id}:user/dev_user/$${aws:username}"
    		],
    		"Action": [
          "iam:Get*",
          "iam:List*",
          "iam:ChangePassword",
    			"iam:CreateAccessKey",
    			"iam:CreateVirtualMFADevice",
    			"iam:DeactivateMFADevice",
    			"iam:DeleteAccessKey",
    			"iam:DeleteVirtualMFADevice",
    			"iam:EnableMFADevice",
    			"iam:GetAccountPasswordPolicy",
    			"iam:UpdateAccessKey",
    			"iam:UpdateSigningCertificate",
    			"iam:UploadSigningCertificate",
    			"iam:UpdateLoginProfile",
    			"iam:ResyncMFADevice"
    		],
    		"Effect": "Allow"
    	},
     {
     "Resource": "*",
       "Action": [
         "iam:List*"
       ],
       "Effect": "Allow"
     }
  ]
 })
}
2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0