728x90

1. AWS SES란?

AWS SES는 마케팅 이메일, 거래 이메일 및 기타 대규모 이메일 발송을 쉽게 처리할 수 있는 서비스로 저렴한 비용으로 대규모 이메일 발송이 가능합니다. SMTP(Simple Mail Transfer Protocol)를 통해 직접 통합하거나 AWS SDK를 사용하여 애플리케이션에서 쉽게 사용할 수 있습니다. SES는 이메일 발송뿐 아니라 수신도 가능하며, 이메일을 인증하는 기능도 제공합니다.

주요 기능:

도메인 인증 : 발송 도메인에 대한 검증 및 설정을 지원하여 이메일의 신뢰성을 높입니다.
DKIM 서명 : DKIM(Domain Keys Identified Mail)을 통해 이메일이 변조되지 않았음을 수신 서버에 증명할 수 있습니다.
전송 상태 모니터링 : 이메일 발송 성공 여부, 반송 상태 등을 실시간으로 모니터링할 수 있습니다.
대량 이메일 발송 : AWS 인프라를 활용해 대규모 이메일 발송이 가능합니다.

2. Amazon SES 요금

Amazon SES는 사용한 만큼 비용 지불이 되며, 이메일 발신 및 수신, 데이터 사용량 및 추가 기능에 대한 요금은 별도로 부과됩니다.

서비스 유형 요금 추가 요금
아웃바운드 이메일 0.10 USD/이메일 1,000건 발신 첨부 파일의 데이터 기가바이트(GB)당 0.12 USD
인바운드 이메일 0.10 USD/이메일 1,000건 수신 이메일 청크 1,000개당 0.09 USD
자세한 내용은 요금페이지 참조

3. AWS SES 설정

AWS SES 는 두가지 자격 증명 방식을 제공합니다.

  • 도메인 : 확인된 도메인의 모든 하위 도메인이나 이메일 주소에서 이메일을 보낼 수 있습니다.
  • 이메일 주소 : 이메일 주소 자격 증명은 확인된 이메일 주소만 메일을 보내는 데 사용할 수 있습니다.

사전 요구 사항:

  1. AWS 계정 : AWS SES를 사용하려면 먼저 AWS 계정이 필요합니다.
  2. 도메인 정보 : 이메일을 발송할 도메인이 필요합니다. 이 도메인에 대해 DNS 설정을 추가해야 하기 때문에 도메인 관리 권한이 필요합니다.

4. AWS SES 도메인 인증 프로비저닝

도메인을 사용하여 이메일을 발송 하려면 도메인을 먼저 검증해야 합니다. SES는 DNS 레코드를 통해 도메인이 실제 소유자의 것인지 확인합니다.

4.1 Terraform Provider 설정

provider "aws" {
  region = "ap-northeast-2"
}

4.2 SES 도메인 인증 리소스 생성:

resource "aws_ses_domain_identity" "example" {
  domain = "example.com"
}

# Route53에 도메인 검증을 위한 TXT 레코드 설정
resource "aws_route53_record" "ses_verification_record" {
  zone_id = "Z3P5QSUBK4POTI"  # Route53 Hosted Zone ID
  name    = "_amazonses.example.com"
  type    = "TXT"
  ttl     = 300
  records = [aws_ses_domain_identity.example.verification_token]
}

4.3 DKIM(Domain Keys Identified Mail) 설정

DKIM(Domain Keys Identified Mail)은 발송된 이메일이 수신 도메인에서 변조되지 않았음을 증명하는 방식입니다. DKIM 서명을 추가하면 이메일의 신뢰도를 높일 수 있습니다. DKIM을 사용하려면 별도의 CNAME 레코드를 DNS에 추가해야 합니다.


# DKIM 설정을 위한 SES 이메일 인증 자격 증명 생성
resource "aws_ses_domain_dkim" "example_dkim" {
  domain = aws_ses_domain_identity.example.domain
}


# Route53에서 DKIM을 위한 CNAME 레코드 설정
resource "aws_route53_record" "ses_dkim_records" {
  count   = length(aws_ses_domain_dkim.example_dkim.dkim_tokens)
  zone_id = "Z3P5QSUBK4POTI"  # Route53 Hosted Zone ID
  name    = "${aws_ses_domain_dkim.example_dkim.dkim_tokens[count.index]}._domainkey.example.com"
  type    = "CNAME"
  ttl     = 300
  records = ["${aws_ses_domain_dkim.example_dkim.dkim_tokens[count.index]}.dkim.amazonses.com"]
}

DKIM 설정을 적용한 후에는 AWS SES 콘솔에서 DKIM 상태가 verified로 표시 되는지 확인합니다.

4.4 Mail From 도메인 설정

반송 메일이 있을 경우 설정된 Mail From 도메인으로 해당 메일을 처리합니다.

# Mail From 도메인 설정
resource "aws_ses_domain_mail_from" "example_mail_from" {
  domain           = aws_ses_domain_identity.example.domain
  mail_from_domain = "mail.example.com"
}

# Route53에서 Mail From 도메인을 위한 MX 레코드 설정
resource "aws_route53_record" "mail_from_mx" {
  zone_id = "Z3P5QSUBK4POTI"   # Route53 Hosted Zone ID
  name    = "mail.example.com"
  type    = "MX"
  ttl     = 300
  records = ["10 feedback-smtp.{region}.amazonses.com"]
}

# Route53에서 Mail From 도메인을 위한 SPF 레코드 설정
resource "aws_route53_record" "mail_from_spf" {
  zone_id = "Z3P5QSUBK4POTI"
  name    = "mail.example.com"
  type    = "TXT"
  ttl     = 300
  records = ["v=spf1 include:amazonses.com -all"]
}

사기 및 남용을 방지하기 위해 신규로 생성시에는 SandBox로 구성되어, 서비스 제한이 있습니다. 생성 후에는 프로덕션으로 전환하도록 요청하여합니다.

  • 확인된 이메일 주소 및 도메인 또는 Amazon SES 메일박스 시뮬레이터로만 메일을 보낼 수 있습니다.
  • 24시간 동안 최대 200개 발송 가능
  • 확인되지 않은 이메일 주소로 이메일 발송 불가

4. 이메일 발송 테스트

AWS 콘솔 및 CLI를 사용하여 이메일을 발송할 수 있습니다.

콘솔에서 테스트 방법 :

CLI로 테스트 하는 방법 :

aws ses send-email \
  --from "no-reply@example.com" \
  --destination "ToAddresses=user@recipient.com" \
  --message "Subject={Data=Email Send Test},Body={Text={Data=This is a test email}}"

결론

AWS SES를 프로비저닝하는 과정은 생각보다 복잡해 보일 수 있지만, 도메인 검증, DKIM 설정, Mail From 설정 등 단계별로 차근차근 설정하면 손쉽게 신뢰도 높은 대규모 이메일 발송 인프라를 구축할 수 있습니다. 추가적으로, 이메일 발송 후 결과를 지속적으로 모니터링하여 도메인 평판을 유지하는 것이 중요합니다.

Reference

728x90
반응형
728x90

AWS의 Systems Manager (SSM)을 활용하면 프라이빗 서브넷에 위치한 EC2 인스턴스에 SSH 없이 안전하게 접근할 수 있습니다.
Terraform을 사용하여 프라이빗 EC2 서버를 프로비저닝하고, SSM을 통해 접근하는 방법을 단계별로 설명합니다.

1. AWS Systems Manager - Session Manager(SSM)

AWS Systems Manager (SSM)은 AWS 리소스 관리와 운영을 자동화하는 서비스로 Session Manager 는 EC2 인스턴스에 대한 안전하고 감사 가능한 접근환경을 제공합니다.

Session Manager 특징

  • SSH 키를 관리할 필요가 없습니다.
  • 인터넷 접근없이 프라이빗 환경으로 인스턴스 접근 가능
  • Bastion 호스트를 사용하지 않습니다.
  • 세션 활동의 로깅 및 감사 가능

Session Manager Prerequisites

  • 지원 운영체제 확인 (AWS Linux OS는 모두 기본 탑재되어 있음)
  • 인스턴스에서 작업 수행을 위한 권한 부여 (AWS Systems Manager는 기본적으로 인스턴스에서 작업을 수행할 권한이 없습니다.)
    • Session Manager에 대한 필수 권한 : AmazonSSMManagedInstanceCore
  • 엔드포인트에 대한 HTTPS(포트 443) 트래픽 허용 필요
    • ec2messages.region.amazonaws.com
    • ssm.region.amazonaws.com
    • ssmmessages.region.amazonaws.com

2. 아키텍처 구성도

다음은 SSM을 통해 EC2 인스턴스에 접근하기 위한 구성도 입니다.

2.1. VPC 및 서브넷 구성

프라이빗 서브넷을 포함한 VPC를 생성합니다.


# VPC 생성
resource "aws_vpc" "main" {
  cidr_block           = "10.0.0.0/16"
  enable_dns_hostnames = true
  enable_dns_support   = true

  tags = {
    Name = "VPC"
  }
}

# Subnet 생성
resource "aws_subnet" "main" {
  vpc_id            = aws_vpc.main.id
  cidr_block        = "10.0.1.0/24"
  availability_zone = "ap-northeast-2a"

  tags = {
    Name = "Subnet"
  }
}

# Route Table 생성
resource "aws_route_table" "main" {
  vpc_id = aws_vpc.main.id

  tags = {
    Name = "Route Table"
  }
}

# Route Table 연결
resource "aws_route_table_association" "main" {
  subnet_id      = aws_subnet.main.id
  route_table_id = aws_route_table.main.id
}

2.2. IAM 역할 및 정책 설정

SSM을 사용하기 위해 EC2 인스턴스에 연결할 IAM 역할과 정책(AmazonSSMManagedInstanceCore)을 설정합니다.


# IAM Role 생성
data "aws_iam_policy_document" "assume_role" {
  statement {
    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }

    actions = ["sts:AssumeRole"]
  }

}

# EC2 역할 생성
resource "aws_iam_role" "ec2-role" {
  name               = "RoleForEC2"
  assume_role_policy = data.aws_iam_policy_document.assume_role.json
  description        = "Role for EC2 Instance"
}

# Manged Policy 연결(AmazonSSMManagedInstanceCore)
resource "aws_iam_role_policy_attachment" "AmazonSSMManagedInstanceCore" {
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
  role       = aws_iam_role.ec2-role.name
}

# EC2 인스턴스 프로필 생성
resource "aws_iam_instance_profile" "ec2_instance_profile" {
  name = "ec2-instance-profile-for-ssm"
  role = aws_iam_role.ec2-role.name
}

2.3. Security Group 및 VPC Endpoint 설정

VPC EndPoint 연동을 위한 433 포트를 오픈하기 위한 보안그룹 설정과 VPC Endpoint를 생성합니다.


# EC2 Security Group 생성
resource "aws_security_group" "ec2_group" {
  name        = "ec2-SG"
  description = "Allow SSH inbound traffic and all outbound traffic"
  vpc_id      = aws_vpc.main.id

  tags = {
    Name = "EC2-SG"
  }
}

# 아웃바운드 트래픽 설정
resource "aws_vpc_security_group_egress_rule" "ec2_group" {
  security_group_id = aws_security_group.ec2_group.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = -1
}

# Endpoint Security Group 생성
resource "aws_security_group" "vpc_endpoint_group" {
  name        = "endpoint-SG"
  description = "Allow TLS inbound traffic and all outbound traffic"
  vpc_id      = aws_vpc.main.id

  tags = {
    Name = "VPC-Endpoint-SG"
  }

}

# 인바운드 트래픽 설정
resource "aws_vpc_security_group_ingress_rule" "vpc_endpoint" {
  security_group_id = aws_security_group.vpc_endpoint_group.id
  cidr_ipv4         = "0.0.0.0/0"
  from_port         = 443
  ip_protocol       = "tcp"
  to_port           = 443
  description       = "Allow Access SSL"
}

# 아웃바운드 트래픽 설정
resource "aws_vpc_security_group_egress_rule" "vpc_endpoint" {
  security_group_id = aws_security_group.vpc_endpoint_group.id
  cidr_ipv4         = "0.0.0.0/0"
  ip_protocol       = -1

}

# VPC Endpoint 생성
resource "aws_vpc_endpoint" "ssm_endpoint" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ssm"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.vpc_endpoint_group.id]
  subnet_ids          = [aws_subnet.main.id]
  private_dns_enabled = true

  tags = {
    Name = "SSM-Endpoint"

  }
}

resource "aws_vpc_endpoint" "ssmmessages_endpoint" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ssmmessages"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.vpc_endpoint_group.id]
  subnet_ids          = [aws_subnet.main.id]
  private_dns_enabled = true

  tags = {
    Name = "SSM-Messages-Endpoint"
  }
}

resource "aws_vpc_endpoint" "ec2messages_endpoint" {
  vpc_id              = aws_vpc.main.id
  service_name        = "com.amazonaws.${var.region}.ec2messages"
  vpc_endpoint_type   = "Interface"
  security_group_ids  = [aws_security_group.vpc_endpoint_group.id]
  subnet_ids          = [aws_subnet.main.id]
  private_dns_enabled = true

  tags = {
    Name = "EC2-Messages-Endpoint"
  }
}

2.4. EC2 인스턴스 생성

인터넷으로 접근되지 않도록 외부에 오픈 되어 있지 않는 보안그룹에 연결하고, 인스턴스를 생성합니다.

# EC2 인스턴스 생성
resource "aws_instance" "main" {
  ami           = "ami-04ea5b2d3c8ceccf8"
  instance_type = "t2.micro"
  subnet_id     = aws_subnet.main.id

  vpc_security_group_ids = [aws_security_group.ec2_group.id]

  iam_instance_profile = aws_iam_instance_profile.ec2_instance_profile.name

  tags = {
    Name = "EC2-Server"
  }
}

2.5. (결과) AWS SystemManager - Session Manager

프로비저닝 구성이 완료되면 AWS SystemManager > Session Manager 탭에서 생성 결과를 확인할 수 있습니다.

2.6. EC2에 접속하기

AWS Console과 CLI를 통해 접속할 수 있습니다.

AWS Management Console를 통한 접속

Console을 통해 SSM 세션을 접속합니다.

AWS CLI를 통한 접속

다음 명령어를 통해 SSM 세션을 시작합니다.

aws ssm start-session --target <instance-id>

2.7. 결과

AWS Systems Manager (SSM)과 Session Manager를 활용하여 프라이빗 EC2 서버를 프로비저닝하고, SSM을 통해 안전하게 접근하는 방법을 다루었습니다. 이를 통해 SSH 키 관리의 번거로움을 줄이고, 보안을 강화할 수 있습니다. SSM과 Session Manager를 사용하면 원격 명령 실행, 파일 전송 등 다양한 기능을 활용할 수 있으므로, 이를 적극적으로 활용해보시기 바랍니다.

Reference

728x90
반응형
728x90

Terraform Backend

Terraform Backend는 Terraform State 파일의 저장하는 위치를 정의합니다.
State는 Terraform에서 프로비저닝 결과를 추적하고 이후 프로비저닝 수행 시 비교하는 과정에 사용되므로, Terraform 사용 빈도와 대상의 규모가 커질수록 계획적인 관리가 요구 됩니다.

Backend 구성 목적

State Backend는 관리 / 공유 / 격리의 목적으로 사용됩니다

목적 내용
관리 State는 local 구성이 기본으로 현재 State인 terraform.tfstate와 이전 상태인 terraform.tfstate.backup만이 보관되므로 지속적인 State 백업을 위해서 Remote 저장소 필요 Remote 저장소에 state 파일을 관리하는 경우, 여러 사용자가 동시에 state 파일을 변경하는 경우 문제가 발생할 수 있으므로 동시에 같은 state 파일을 접근하는 것을 막아 의도치 않는 변경을 방지
공유 다수의 작업자가 동일한 State로 접근하여 프로비저닝하기 위한 공유 스토리지 필요
격리 State 파일에 저장될 수 있는 민감 데이터에 접근 권한을 환경별로 접근권한을 격리

사용 가능한 Backend

기본적으로 Terraform은 local상태를 디스크에 local 파일로 저장하는 Backend를 사용합니다.
AWS 리소스를 프로비저닝하는 경우 S3, Google Cloud의 경우 GCS가 적합 할 수 있습니다. HashiCorp는 Terraform Cloud를 통해 State 백엔드 기능을 제공합니다.

(기타)

  • Consul
  • Postgres Database (pg)

Terraform Workspace

Terraform Workspace는 State를 관리하는 단위입니다. 기본적으로 default Workspace가 생성됩니다.
terraform plan 을 새로운 Workspace에서 수행하면, Terraform은 다른 Workspace에 있는 리소스에 접근하지 않습니다.
기존에 있던 리소스를 관리하려면 Workspace를 전환한 후에 변경해야 합니다.

Terraform Backend 구현(AWS)

Terraform을 사용하여 인프라스트럭처를 관리할 때, Terraform의 상태 파일(backend)을 보관하는 AWS 계정과 실제 리소스가 프로비저닝되는 AWS 계정을 분리하는 것은 일반적인 보안 및 관리 방법입니다.
이러한 접근 방식은 Terraform 상태 파일의 보안을 강화하고, 팀 간의 접근 권한을 분리 하며, 여러 프로젝트 또는 환경 간의 충돌을 방지하는 데 도움이 됩니다.

AWS 인프라 관리 시, Multi-Account 관리를 통해 독립적인 환경을 구현하고 보안 및 관리 용이성을 가질 수 있도록 권고되고 있습니다.
Terraform Backend 환경은 공유 자원을 관리하는 Shared AWS Account에 관리 되며, 각 환경 및 Workspace별로 state를 저장 관리합니다.

+------------------+----------------+
| AWS Account Name | AWS Account ID |
+------------------+----------------+
| Shared           |  111111111111  |
| Dev              |  222222222222  |
| Qa               |  333333333333  |
| Prod             |  444444444444  |
| Test             |  555555555555  |
+------------------+----------------+

S3, DynamoDB 생성

상태를 Amazon S3의 버킷에 저장하고, Dynamo DB를 활용하여 Apply 시 State Locking을 통해 State의 일관성을 확보합니다.
실수로 삭제하거나 사람의 실수가 발생한 경우 상태를 복구할 수 있도록 버킷(Bucket)의 버전 관리를 활성화 합니다.

# Create S3 Bucket for Terraform State
resource "aws_s3_bucket" "terraform_state" {

    bucket = "tf-state-bucket" 


    tags = {
        Name        = "tf-state-bucket"
        Environment = ""    # Global, Shared, Dev, QA, Prod??
    }
}

# Provides a resource for controlling versioning on an S3 bucket
resource "aws_s3_bucket_versioning" "this" {
    bucket = aws_s3_bucket.terraform_state.id
    versioning_configuration {
      status = "Enabled"
    }
}


# Provides a S3 bucket server-side encryption.
resource "aws_s3_bucket_server_side_encryption_configuration" "this" {
  bucket = aws_s3_bucket.terraform_state.id

  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "AES256"      # (SSE-S3)
    }
  }
}


# Provision DynamoDB Table for state locking
resource "aws_dynamodb_table" "this" {
    name = "terraform-state-lock"
    hash_key = "LockID"
    billing_mode = "PAY_PER_REQUEST"

    attribute {
      name = "LockID"
      type = "S"
    }
}

State Configuration

State는 <workspace_key_prefix>/<workspace_name>/<key> 형식으로 저장됩니다.
Terraform Backend 환경은 공유 자원을 관리하는 shared-layer 에 관리되며, 각 환경 및 Workspace별로 state를 저장 관리합니다.

Terraform Backend Configuration S3 Backend Configuration

terraform{
    backend "s3" {
            bucket               = {S3 Bucket Name}                    # S3 Bucket Name
            key                  = "terraform.tfstate"                 # Path to tfstate in S3 Bucket (workspace/terraform.tfstate)
            region               = "ap-northeast-2"                    # Region Name
            dynamodb_table       = {DynamoDB Table Name}               # DynamoDB Table for state locking
            encrypt              = true                                # Enable Encyrtion state file
            workspace_key_prefix = {layer 이름}                         # default(env:)
            profile              = {backend-account-profile}           # backend가 위치한 AWS Profile
        }
}

# Configure the AWS Provider
provider "aws" {
    region = "ap-northeast-2"
    profile = {resource-account-profile}                               # 환경(Dev/Qa/Prod/Test)의 AWS Account Profile
}

Backend를 변경하고, terraform init 으로 초기화합니다.

yes 를 입력하면 local의 state가 지워지고, S3 로 Backend가 변경 되었음을 확인 할 수 있습니다.

728x90
반응형

+ Recent posts