随分前にTerraformでIAMグループ・ユーザを作成するをリファクタリングしたのでUpdateします。
認証戦略としては、属性ベースのアクセスコントロール (ABAC) 、ロールベースのアクセスコントロール (RBAC) がありますが、ABACもRBACもすべてのアクションに対して設定可能なわけではないので適宜Mixさせます。
共通しているのは 「権限のガードレールによりオペミスや悪意のあるアクセスを防ぎ、重大インシデントを未然に防止する」 です。
- 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"
}
]
})
}