https://nyyang.tistory.com/145
위 글에 이어서 작성하는 글이며, Security Account 외 다른 어카운트들에서 동일한 Resource를 배포하기 위한 Terraform 코드이다.
가장 중요한 부분은 Provider Alias를 이해하는 부분이다.
아래 글을 읽으면 빠르게 이해가 될 것이라고 생각한다.
https://blog.outsider.ne.kr/1399
1. Structure (구조)
각 Account에 대해 동일한 Resource를 배포하기 위해 Terraform을 사용했고, 결론적으로 얘기하자면 Provider Alias 부분에 대해 for_each 등의 반복문을 사용할 수 있을 줄 알았는데, 의외로 안되는 것 같았다... 흠
(혹시 아시는분 알려주시면 감사하겠습니다.)
2. 파일 설명
반복문으로 처리하지 못해 코드가 좀 길음.
1. _backend.tf
Backend를 S3로 지정하였다.
여기서 Profile은 _provider.tf 파일에서 정의해주었는데, 어떤 AWS 어카운트에 대해서 수행할 것인지 정의해주면 Terraform은 _provider.tf의 Provider Alias에 대한 인증 및 인가를 수행한 뒤 backend 설정을 하게 된다.
terraform {
backend "s3" {
bucket = "xxxx-xxxx-xxxx-tfstate"
dynamodb_table = "xxxx-xxxx-xxxx-terraform-lock-table"
encrypt = true
key = "xxxx-xxxx/xxxx-xxxx/terraform.tfstate"
profile = "xxxx-xxxx"
region = "ap-northeast-2"
}
}
2. _provider.tf
~/.aws/credentials 파일에서 profile을 찾아서 인증을 수행하고, Resource를 생성할 때는 alias를 설정한 뒤, Resource 혹은 Data Block에서 profile로 접근하면 된다.
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "4.8.0"
}
}
}
// a Account
provider "aws" {
alias = "a"
profile = format("xxxx-%s", "a")
region = local.region
shared_credentials_files = [local.cred_file]
}
// b Account
provider "aws" {
alias = "b"
profile = format("xxxx-%s", "b")
region = local.region
shared_credentials_files = [local.cred_file]
}
// c Account
provider "aws" {
alias = "c"
profile = format("xxxx-%s", "c")
region = local.region
shared_credentials_files = [local.cred_file]
}
// DI Account
provider "aws" {
alias = "d"
profile = format("xxxx-%s", "d")
region = local.region
shared_credentials_files = [local.cred_file]
}
// d Account
provider "aws" {
alias = "e"
profile = format("xxxx-%s", "e")
region = local.region
shared_credentials_files = [local.cred_file]
}
// e Account
provider "aws" {
alias = "g"
profile = format("xxxx-%s", "g")
region = local.region
shared_credentials_files = [local.cred_file]
}
// f Account
provider "aws" {
alias = "f"
profile = format("xxxx-%s", "f")
region = local.region
shared_credentials_files = [local.cred_file]
}
3. _data.tf
A Account -> B Account로 접근하기 위해서는 양측 간 Policy에 대해 명시적 Allow가 되어 있어야 한다.
이 때문에 각 Account에서 Security Account의 EventBus로 PutEvents를 Allow해주는 Policy를 data 블록으로 생성하였음
data "aws_iam_policy_document" "a" {
provider = aws.a
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
data "aws_iam_policy_document" "b" {
provider = aws.b
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
data "aws_iam_policy_document" "c" {
provider = aws.c
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
data "aws_iam_policy_document" "d" {
provider = aws.d
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
data "aws_iam_policy_document" "e" {
provider = aws.e
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
data "aws_iam_policy_document" "f" {
provider = aws.f
statement {
sid = "ssss"
effect = "Allow"
resources = [local.security_account_event_bus_arn]
actions = ["events:PutEvents"]
}
}
4. _shared_vars.tf, _vars.tf, _terraform.atuo.tfvars
: 이전 글에서 용도 확인 가능, 생략
5. eventPattern.json , _local.tf
eventPattern을 설정한 뒤, local 변수를 통해 사용할 수 있도록 설정하였다.
{
"source": [
"aws.iam"
],
"detail-type": [
"AWS API Call via CloudTrail"
],
"detail": {
"eventSource": [
"iam.amazonaws.com"
],
"eventName": [
"AddRoleToInstanceProfile",
"AddUserToGroup",
"AttachGroupPolicy",
"AttachRolePolicy",
"AttachUserPolicy",
"ChangePassword",
"CreateAccessKey",
"CreateGroup",
"CreateInstanceProfile",
"CreateLoginProfile",
"CreateOpenIDConnectProvider",
"CreatePolicy",
"CreateRole",
"CreateServiceLinkedRole",
"CreateUser",
"CreateVirtualMFADevice",
"DeactivateMFADevice",
"DeleteAccessKey",
"DeleteGroup",
"DeleteGroupPolicy",
"DeleteInstanceProfile",
"DeleteLoginProfile",
"DeletePolicy",
"DeletePolicyVersion",
"DeleteRole",
"DeleteRolePolicy",
"DeleteServerCertificate",
"DeleteServiceLinkedRole",
"DeleteSSHPublicKey",
"DeleteUser",
"DeleteUserPolicy",
"DeleteVirtualMFADevice",
"DetachGroupPolicy",
"DetachRolePolicy",
"DetachUserPolicy",
"EnableMFADevice",
"PutGroupPolicy",
"PutRolePolicy",
"PutUserPolicy",
"RemoveClientIDFromOpenIDConnectProvider",
"RemoveRoleFromInstanceProfile",
"RemoveUserFromGroup",
"ResetServiceSpecificCredential",
"ResyncMFADevice",
"UpdateAccessKey",
"UpdateAssumeRolePolicy",
"UpdateGroup",
"UpdateLoginProfile",
"UpdateRole",
"UpdateServiceSpecificCredential",
"UpdateSigningCertificate",
"UpdateSSHPublicKey",
"UpdateUser",
"CreatePolicyVersion"
]
}
}
이렇게 하면 다른 파일에서 local.event_pattern 으로 해당 이벤트 패턴을 설정할 수 있게 될 것이다.
locals {
prefix = format("%s-security-audit", "xxxx")
cred_file = var.cred_file
region = var.region
tags = merge(var.tags,
{
Team = var.team
})
security_account_event_bus_arn = var.security_account_event_bus_arn
event_pattern = file("./config/eventPattern.json")
}
6. event_rule.tf
동일한 Resource가 반복되기 때문에 6개 중 1개만 글에 포함하였다.
resource "aws_cloudwatch_event_rule" "a" {
name = "${local.prefix}-event-rule"
description = "Capture IAM API Actions for security audit"
provider = aws.a
event_pattern = local.event_pattern
}
7. event_target.tf
arn : 이 부분은 Security Account의 EventBus ARN을 설정해주면 된다.
resource "aws_cloudwatch_event_target" "a" {
arn = local.security_account_event_bus_arn
provider = aws.a
rule = aws_cloudwatch_event_rule.a.name
role_arn = aws_iam_role.prod.arn
target_id = "SendToCloudWatchLogs"
depends_on = [
aws_iam_role.a
]
}
8. iam_role.tf
iam role을 생성한 뒤 data block에서 기 생성한 iam policy를 attach해주었다.
// a
resource "aws_iam_role" "a" {
name = format("Amazon-EventBridge-to-sec-%s-iam-role", "a")
provider = aws.a
path = "/"
assume_role_policy = <<EOF
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": {
"Service": "events.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
EOF
}
resource "aws_iam_role_policy" "a" {
role = aws_iam_role.a.id
provider = aws.a
policy = data.aws_iam_policy_document.a.json
}
배포한 리소스 내역은 event bridge 밖에 없지만 event pattern 및 iam role 까지 추가적으로 필요하였다.
EventBridge
- Event Pattern : IAM Through CloudTrail
- Target : Security 계정의 EventBridge
다음으로는 EventRule -> SNS -> Lambda -> Slack으로 Notification을 보내기 위한 메커니즘 및 Python Code에 대해 정리해볼 생각이다.
아래의 글과 메커니즘은 동일하나, 테스트를 진행하면서 추가적으로 고려해야 할 사항을 발견하여 그에 대해서 좀 정리해보도록 하겠다.
(IAM Role, IAM User에서 각각 userIdentity 부분이 달라서 이 부분을 고려하지 않으면 Slack Noti에서 오류가 발생해 노티가 전달되지 않을수도 있다.)
https://nyyang.tistory.com/126
'AWS' 카테고리의 다른 글
[ECS] ECS Exec를 사용하여 ECS Fargate 컨테이너에 접속하기 (0) | 2022.04.29 |
---|---|
[AWS] Multi Account에서 IAM 내역 감사하기 #4 - Lambda를 활용한 Slack Notification (2) | 2022.04.17 |
[AWS] Multi Account에서 IAM 내역 감사하기 #2 - 테라폼으로 인프라 프로비저닝 (0) | 2022.04.15 |
[AWS] Multi Account에서 IAM 내역 감사하기 #1 - 개요, 아키텍쳐, 고려 사항 (0) | 2022.04.06 |
[Python] CLB 인스턴스 상태 및 등록, 해제하는 파이썬 스크립트 (0) | 2022.01.06 |