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​