Skip to main content

Security

Multi-Region Shopping Mall implements a multi-layered security architecture providing comprehensive security at the infrastructure, data, and application levels. This document details WAF, encryption, IAM, and network security.

Security Architecture Overview

AWS WAF v2

WAF Architecture

WAF Rule Configuration

1. AWS Managed Rules

Rule SetPriorityActionDescription
AWSManagedRulesCommonRuleSet1BlockBlock common web vulnerabilities
AWSManagedRulesSQLiRuleSet2BlockBlock SQL Injection attacks
AWSManagedRulesKnownBadInputsRuleSet3BlockBlock known malicious inputs
AWSManagedRulesLinuxRuleSet4BlockBlock Linux-specific attacks
AWSManagedRulesAmazonIpReputationList5BlockBlock malicious IPs

2. Rate Limiting

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

default_action {
allow {}
}

# Rate Limiting: 2000 requests per 5 minutes
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
}
}

# Granular Rate Limit per API endpoint
rule {
name = "LoginRateLimit"
priority = 11

statement {
rate_based_statement {
limit = 100 # 100 per 5 minutes
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

# Allowed countries: Korea (KR), United States (US), Japan (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 Rules Summary Table

RulePriorityRate LimitActionApplied To
AWS Common Rules1-BlockAll requests
SQLi Rules2-BlockAll requests
Known Bad Inputs3-BlockAll requests
IP Reputation5-BlockAll requests
Global Rate Limit102000/5minBlockAll requests
Login Rate Limit11100/5minBlock/api/auth/login
Order Rate Limit1250/5minBlock/api/orders POST
Geo Restriction20-BlockNon-allowed countries

KMS Encryption

Encryption Architecture

Encryption by Data Store

Data StoreEncryption TypeKMS KeyAlgorithm
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 Configuration

# 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

Secret Management Structure

Secret List

Secret NameTypeRotation PeriodUsed By
production/aurora/masterDB Credentials30 daysOrder, Payment, Inventory
production/documentdb/masterDB Credentials30 daysProduct, Profile, Review
production/msk/saslSASL Credentials90 daysAll Kafka consumers
production/opensearch/masterService Credentials30 daysSearch, Analytics
production/api/jwt-secretAPI Secret90 daysAPI Gateway

Secret Access Example

# Python - Using 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

# Usage example
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 by Service

ServiceIAM RolePermissions
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 Configuration

# 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

Network Security

Security Groups

Network ACLs

NACLInbound RulesOutbound RulesApplied Subnets
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

Data Subnet NACL

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

# Inbound: Allow only from Private subnets
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
}

# Outbound: Respond only to Private subnets
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 Configuration

SegmentTLS VersionCertificateManagement
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

Application TLS Settings

// Go - TLS configuration example
package main

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

_ "github.com/lib/pq"
)

func connectToAurora() (*sql.DB, error) {
// Load RDS CA certificate
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)
}

Security Monitoring

CloudTrail Logging

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 Enablement

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"
}
}

Security Alerts

# WAF block alert
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 threat detection alert
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
}

Security Checklist

Infrastructure Security

  • VPC 3-tier architecture (Public/Private/Data)
  • Security Groups - Principle of least privilege
  • Network ACLs - Subnet-level filtering
  • VPC Flow Logs enabled
  • VPC Endpoints - Private connectivity

Data Security

  • KMS Customer Managed Keys used
  • All data stores At-Rest encrypted
  • TLS 1.2+ In-Transit encryption
  • Secrets Manager auto-rotation

Access Control

  • IAM IRSA - Pod-level permissions
  • Principle of least privilege applied
  • MFA enabled (console access)
  • CloudTrail audit logging

Application Security

  • WAF v2 - OWASP Top 10 protection
  • Rate Limiting applied
  • Geo Restriction (KR/US/JP)
  • Input validation and sanitizing

Monitoring

  • GuardDuty threat detection
  • CloudWatch alarms
  • Security Hub integration
  • Real-time alerting configured

Next Steps