본문으로 건너뛰기

보안

Multi-Region Shopping Mall은 다층 보안 아키텍처를 구현하여 인프라, 데이터, 애플리케이션 수준에서 포괄적인 보안을 제공합니다. 이 문서에서는 WAF, 암호화, IAM, 네트워크 보안에 대해 상세히 설명합니다.

보안 아키텍처 개요

AWS WAF v2

WAF 아키텍처

WAF 규칙 구성

1. AWS Managed Rules

규칙 세트우선순위액션설명
AWSManagedRulesCommonRuleSet1Block일반적인 웹 취약점 차단
AWSManagedRulesSQLiRuleSet2BlockSQL Injection 공격 차단
AWSManagedRulesKnownBadInputsRuleSet3Block알려진 악성 입력 차단
AWSManagedRulesLinuxRuleSet4BlockLinux 특화 공격 차단
AWSManagedRulesAmazonIpReputationList5Block악성 IP 차단

2. Rate Limiting

resource "aws_wafv2_web_acl" "main" {
name = "production-waf"
description = "Production WAF Web ACL"
scope = "CLOUDFRONT"

default_action {
allow {}
}

# Rate Limiting: 5분에 2000 요청
rule {
name = "RateLimitRule"
priority = 10

override_action {
none {}
}

statement {
rate_based_statement {
limit = 2000
aggregate_key_type = "IP"
}
}

action {
block {}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimitRule"
sampled_requests_enabled = true
}
}

# API 엔드포인트별 세분화된 Rate Limit
rule {
name = "LoginRateLimit"
priority = 11

statement {
rate_based_statement {
limit = 100 # 5분에 100회
aggregate_key_type = "IP"

scope_down_statement {
byte_match_statement {
search_string = "/api/auth/login"
positional_constraint = "STARTS_WITH"
field_to_match {
uri_path {}
}
text_transformation {
priority = 0
type = "LOWERCASE"
}
}
}
}
}

action {
block {
custom_response {
response_code = 429
custom_response_body_key = "TooManyRequests"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "LoginRateLimit"
sampled_requests_enabled = true
}
}
}

3. Geo Restriction

# 허용 국가: 한국(KR), 미국(US), 일본(JP)
rule {
name = "GeoRestriction"
priority = 20

statement {
not_statement {
statement {
geo_match_statement {
country_codes = ["KR", "US", "JP"]
}
}
}
}

action {
block {
custom_response {
response_code = 403
custom_response_body_key = "GeoBlocked"
}
}
}

visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "GeoRestriction"
sampled_requests_enabled = true
}
}

WAF 규칙 요약 테이블

규칙우선순위Rate Limit액션적용 대상
AWS Common Rules1-Block모든 요청
SQLi Rules2-Block모든 요청
Known Bad Inputs3-Block모든 요청
IP Reputation5-Block모든 요청
Global Rate Limit102000/5minBlock모든 요청
Login Rate Limit11100/5minBlock/api/auth/login
Order Rate Limit1250/5minBlock/api/orders POST
Geo Restriction20-Block비허용 국가

KMS 암호화

암호화 아키텍처

데이터 스토어별 암호화

데이터 스토어암호화 유형KMS 키알고리즘
Aurora PostgreSQLAt-RestCustomer ManagedAES-256
DocumentDBAt-RestCustomer ManagedAES-256
ElastiCache ValkeyAt-Rest + In-TransitCustomer ManagedAES-256, TLS 1.2
S3At-RestCustomer ManagedAES-256 (SSE-KMS)
EBSAt-RestCustomer ManagedAES-256
MSK KafkaAt-Rest + In-TransitCustomer ManagedAES-256, TLS 1.2
OpenSearchAt-Rest + In-TransitCustomer ManagedAES-256, TLS 1.2
Secrets ManagerAt-RestAWS ManagedAES-256

KMS Key Policy

{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow RDS to use the key",
"Effect": "Allow",
"Principal": {
"Service": "rds.amazonaws.com"
},
"Action": [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"aws:SourceAccount": "123456789012"
}
}
},
{
"Sid": "Allow EKS Pods via IRSA",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::123456789012:role/eks-*-irsa"
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey"
],
"Resource": "*"
}
]
}

Terraform 구성

# KMS Key for Data Encryption
resource "aws_kms_key" "data_encryption" {
description = "KMS key for data encryption"
deletion_window_in_days = 30
enable_key_rotation = true
multi_region = true

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "Enable IAM User Permissions"
Effect = "Allow"
Principal = {
AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
}
Action = "kms:*"
Resource = "*"
},
{
Sid = "Allow services to use the key"
Effect = "Allow"
Principal = {
Service = [
"rds.amazonaws.com",
"elasticache.amazonaws.com",
"kafka.amazonaws.com",
"es.amazonaws.com"
]
}
Action = [
"kms:Encrypt",
"kms:Decrypt",
"kms:ReEncrypt*",
"kms:GenerateDataKey*",
"kms:DescribeKey",
"kms:CreateGrant"
]
Resource = "*"
}
]
})

tags = {
Name = "production-data-encryption-key"
Environment = "production"
}
}

resource "aws_kms_alias" "data_encryption" {
name = "alias/production-data-encryption"
target_key_id = aws_kms_key.data_encryption.key_id
}

Secrets Manager

시크릿 관리 구조

시크릿 목록

시크릿 이름유형로테이션 주기사용 서비스
production/aurora/masterDB Credentials30일Order, Payment, Inventory
production/documentdb/masterDB Credentials30일Product, Profile, Review
production/msk/saslSASL Credentials90일All Kafka consumers
production/opensearch/masterService Credentials30일Search, Analytics
production/api/jwt-secretAPI Secret90일API Gateway

시크릿 접근 예시

# Python - Secrets Manager 사용
import boto3
import json
from botocore.exceptions import ClientError

def get_db_credentials(secret_name: str) -> dict:
session = boto3.session.Session()
client = session.client(
service_name='secretsmanager',
region_name='us-east-1'
)

try:
response = client.get_secret_value(SecretId=secret_name)
secret = json.loads(response['SecretString'])
return {
'host': secret['host'],
'port': secret['port'],
'username': secret['username'],
'password': secret['password'],
'database': secret['dbname']
}
except ClientError as e:
raise e

# 사용 예시
creds = get_db_credentials('production/aurora/master')
connection_string = f"postgresql://{creds['username']}:{creds['password']}@{creds['host']}:{creds['port']}/{creds['database']}"

IAM & IRSA

IRSA (IAM Roles for Service Accounts)

서비스별 IAM Role

서비스IAM Role권한
Order Serviceorder-service-irsaSecretsManager:GetSecretValue, KMS:Decrypt, SQS:*
Payment Servicepayment-service-irsaSecretsManager:GetSecretValue, KMS:Decrypt
Product Catalogproduct-catalog-irsaSecretsManager:GetSecretValue, S3:GetObject
Search Servicesearch-service-irsaSecretsManager:GetSecretValue, ES:*
Notificationnotification-irsaSecretsManager:GetSecretValue, SES:SendEmail, SNS:Publish
Analyticsanalytics-irsaSecretsManager:GetSecretValue, S3:, Athena:

IRSA 구성

# IRSA for Order Service
module "order_service_irsa" {
source = "terraform-aws-modules/iam/aws//modules/iam-role-for-service-accounts-eks"
version = "~> 5.0"

role_name = "order-service-irsa"

oidc_providers = {
main = {
provider_arn = module.eks.oidc_provider_arn
namespace_service_accounts = ["production:order-service"]
}
}

role_policy_arns = {
policy = aws_iam_policy.order_service.arn
}
}

resource "aws_iam_policy" "order_service" {
name = "order-service-policy"
description = "Policy for Order Service"

policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "SecretsManagerAccess"
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret"
]
Resource = [
"arn:aws:secretsmanager:*:${data.aws_caller_identity.current.account_id}:secret:production/aurora/*",
"arn:aws:secretsmanager:*:${data.aws_caller_identity.current.account_id}:secret:production/msk/*"
]
},
{
Sid = "KMSDecrypt"
Effect = "Allow"
Action = [
"kms:Decrypt",
"kms:GenerateDataKey"
]
Resource = aws_kms_key.data_encryption.arn
}
]
})
}

Kubernetes Service Account

# order-service ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: order-service
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/order-service-irsa
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: order-service
namespace: production
spec:
template:
spec:
serviceAccountName: order-service
containers:
- name: order-service
image: 123456789012.dkr.ecr.us-east-1.amazonaws.com/order-service:latest
env:
- name: AWS_REGION
value: us-east-1

네트워크 보안

Security Groups

Network ACLs

NACL인바운드 규칙아웃바운드 규칙적용 서브넷
Public443, 80 from 0.0.0.0/0All to 0.0.0.0/0Public Subnets
PrivateAll from VPC CIDRAll to 0.0.0.0/0Private Subnets
DataAll from Private CIDRAll to Private CIDRData Subnets

데이터 서브넷 NACL

resource "aws_network_acl" "data" {
vpc_id = aws_vpc.main.id
subnet_ids = aws_subnet.data[*].id

# 인바운드: Private 서브넷에서만 접근 허용
ingress {
protocol = "tcp"
rule_no = 100
action = "allow"
cidr_block = "10.0.11.0/24" # Private Subnet A
from_port = 0
to_port = 65535
}

ingress {
protocol = "tcp"
rule_no = 101
action = "allow"
cidr_block = "10.0.12.0/24" # Private Subnet B
from_port = 0
to_port = 65535
}

ingress {
protocol = "tcp"
rule_no = 102
action = "allow"
cidr_block = "10.0.13.0/24" # Private Subnet C
from_port = 0
to_port = 65535
}

# Cross-region replication
ingress {
protocol = "tcp"
rule_no = 200
action = "allow"
cidr_block = "10.1.0.0/16" # us-west-2 VPC
from_port = 0
to_port = 65535
}

# 아웃바운드: Private 서브넷으로만 응답
egress {
protocol = "tcp"
rule_no = 100
action = "allow"
cidr_block = "10.0.0.0/16"
from_port = 0
to_port = 65535
}

tags = {
Name = "data-subnet-nacl"
}
}

TLS/SSL

TLS 구성

구간TLS 버전인증서관리
CloudFront ↔ ClientTLS 1.2+ACM (*.atomai.click)AWS Managed
ALB ↔ CloudFrontTLS 1.2+ACMAWS Managed
EKS ↔ ALBTLS 1.2Self-signedKubernetes
Services ↔ AuroraTLS 1.2RDS CAAWS Managed
Services ↔ DocumentDBTLS 1.2RDS CAAWS Managed
Services ↔ ElastiCacheTLS 1.2ElastiCache CAAWS Managed
Services ↔ MSKTLS 1.2MSK CAAWS Managed

애플리케이션 TLS 설정

// Go - TLS 설정 예시
package main

import (
"crypto/tls"
"crypto/x509"
"database/sql"
"io/ioutil"

_ "github.com/lib/pq"
)

func connectToAurora() (*sql.DB, error) {
// RDS CA 인증서 로드
rootCert, err := ioutil.ReadFile("/etc/ssl/certs/rds-ca-2019-root.pem")
if err != nil {
return nil, err
}

rootCertPool := x509.NewCertPool()
rootCertPool.AppendCertsFromPEM(rootCert)

tlsConfig := &tls.Config{
RootCAs: rootCertPool,
MinVersion: tls.VersionTLS12,
}

connStr := fmt.Sprintf(
"host=%s port=5432 user=%s password=%s dbname=%s sslmode=verify-full sslrootcert=/etc/ssl/certs/rds-ca-2019-root.pem",
host, user, password, dbname,
)

return sql.Open("postgres", connStr)
}

보안 모니터링

CloudTrail 로깅

resource "aws_cloudtrail" "main" {
name = "production-trail"
s3_bucket_name = aws_s3_bucket.cloudtrail.id
include_global_service_events = true
is_multi_region_trail = true
enable_logging = true

event_selector {
read_write_type = "All"
include_management_events = true

data_resource {
type = "AWS::S3::Object"
values = ["arn:aws:s3:::"]
}
}

cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail.arn}:*"
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail.arn

kms_key_id = aws_kms_key.cloudtrail.arn

tags = {
Name = "production-cloudtrail"
}
}

GuardDuty 활성화

resource "aws_guardduty_detector" "main" {
enable = true

datasources {
s3_logs {
enable = true
}
kubernetes {
audit_logs {
enable = true
}
}
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = true
}
}
}
}

finding_publishing_frequency = "FIFTEEN_MINUTES"

tags = {
Name = "production-guardduty"
}
}

보안 알림

# WAF 블록 알림
resource "aws_cloudwatch_metric_alarm" "waf_blocked_requests" {
alarm_name = "waf-high-block-rate"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = 2
metric_name = "BlockedRequests"
namespace = "AWS/WAFV2"
period = 300
statistic = "Sum"
threshold = 1000

dimensions = {
WebACL = aws_wafv2_web_acl.main.name
Region = "us-east-1"
Rule = "ALL"
}

alarm_actions = [aws_sns_topic.security_alerts.arn]

alarm_description = "High number of WAF blocked requests"
}

# GuardDuty 위협 탐지 알림
resource "aws_cloudwatch_event_rule" "guardduty_findings" {
name = "guardduty-findings"
description = "GuardDuty finding events"

event_pattern = jsonencode({
source = ["aws.guardduty"]
detail-type = ["GuardDuty Finding"]
detail = {
severity = [{ numeric = [">=", 7] }]
}
})
}

resource "aws_cloudwatch_event_target" "guardduty_sns" {
rule = aws_cloudwatch_event_rule.guardduty_findings.name
target_id = "SendToSNS"
arn = aws_sns_topic.security_alerts.arn
}

보안 체크리스트

인프라 보안

  • VPC 3-tier 아키텍처 (Public/Private/Data)
  • Security Groups - 최소 권한 원칙
  • Network ACLs - 서브넷 레벨 필터링
  • VPC Flow Logs 활성화
  • VPC Endpoints - Private 연결

데이터 보안

  • KMS Customer Managed Keys 사용
  • 모든 데이터 스토어 At-Rest 암호화
  • TLS 1.2+ In-Transit 암호화
  • Secrets Manager 자동 로테이션

접근 제어

  • IAM IRSA - Pod 레벨 권한
  • 최소 권한 원칙 적용
  • MFA 활성화 (콘솔 접근)
  • CloudTrail 감사 로깅

애플리케이션 보안

  • WAF v2 - OWASP Top 10 방어
  • Rate Limiting 적용
  • Geo Restriction (KR/US/JP)
  • 입력 검증 및 새니타이징

모니터링

  • GuardDuty 위협 탐지
  • CloudWatch 알람
  • Security Hub 통합
  • 실시간 알림 구성

다음 단계