本記事は、AWSハンズオンシリーズの第14回(最終回)として、本番環境で必須となるセキュリティサービスの実装を行います。
今回実装するセキュリティリソース
- AWS WAF: Webアプリケーションファイアウォール
- Amazon GuardDuty: 脅威検知サービス
- AWS Config: リソース設定の監査とコンプライアンス
- IAMポリシーの最適化: 最小権限の原則
前提条件
- 第1回から第13回までの構築が完了していること
- こちらからDockerコンテナでの実行環境が準備できていること
Terraform実行環境の起動
前回と同様に、Dockerコンテナ内で作業を実行します。
# コンテナを起動してbashに入る
docker-compose run --rm terraform
これ以降のコマンドは、すべてこのコンテナ内で実行します。
基礎知識
セキュリティの多層防御
AWSにおけるセキュリティは、単一の対策ではなく複数の層で防御することが重要です。
WEBアプリケーション開発者の視点から見ると
開発環境では簡略化されがちなセキュリティ設定も、本番環境では多層的な防御が必要です。例えば、SQLインジェクション対策は、アプリケーションレベルのバリデーションだけでなく、WAFでのブロック、最小権限のIAMロール設定、データベースの監査ログなど、複数の層で防御します。
セキュリティレイヤーの種類
レイヤー | 対策内容 | AWSサービス |
---|---|---|
ネットワーク層 | DDoS攻撃、不正アクセス | Security Group、NACL、VPC Flow Logs |
アプリケーション層 | SQLインジェクション、XSS | WAF、Shield |
データ層 | 暗号化、アクセス制御 | KMS、S3暗号化、RDS暗号化 |
アイデンティティ層 | 認証・認可 | IAM、Cognito、Secrets Manager |
監視・監査層 | 異常検知、コンプライアンス | GuardDuty、Config、CloudTrail |
Zero Trust セキュリティモデル
従来の境界型セキュリティから、すべてのアクセスを検証するZero Trustモデルへの移行が推奨されています。
原則 | 実装方法 | 適用例 |
---|---|---|
最小権限の原則 | IAMポリシーの細分化 | タスクロールは必要なS3バケットのみアクセス可能 |
常時検証 | 継続的な監視 | GuardDutyによる異常検知 |
暗号化 | 転送時・保存時の暗号化 | HTTPS通信、S3/RDS暗号化 |
ログ記録 | すべての操作を記録 | CloudTrail、VPC Flow Logs |
今回作成するセキュリティ構成
カテゴリ | リソース | 説明 |
---|---|---|
WAF | WebACL | アプリケーション層の攻撃防御 |
Managed Rules | AWS管理ルールセット | |
Custom Rules | レート制限、IP制限 | |
GuardDuty | Detector | 脅威検知エンジン |
Threat Intelligence | 脅威インテリジェンスフィード | |
Config | Configuration Recorder | リソース設定の記録 |
Rules | コンプライアンスルール | |
IAM | ポリシー最適化 | 最小権限の実装 |
MFA強制 | 多要素認証の設定 |
Terraformコードの実装
セキュリティモジュールの実装
# terraform/modules/security/main.tf
locals {
common_tags = {
Module = "security"
ManagedBy = "terraform"
Environment = var.environment
Project = var.project_name
}
}
# データソース
data "aws_caller_identity" "current" {}
#============================================
# WAF (Web Application Firewall)
#============================================
# WAF WebACL
resource "aws_wafv2_web_acl" "main" {
count = var.enable_waf ? 1 : 0
name = "${var.project_name}-${var.environment}-waf"
scope = "REGIONAL"
default_action {
allow {}
}
# AWS Managed Rule - Core Rule Set
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
# 特定のルールを除外する場合
rule_action_override {
name = "SizeRestrictions_BODY"
action_to_use {
allow {}
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "CommonRuleSetMetric"
sampled_requests_enabled = true
}
}
# SQL Injection対策
rule {
name = "AWSManagedRulesSQLiRuleSet"
priority = 2
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesSQLiRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "SQLiRuleSetMetric"
sampled_requests_enabled = true
}
}
# レート制限ルール
rule {
name = "RateLimitRule"
priority = 3
action {
block {}
}
statement {
rate_based_statement {
limit = var.waf_rate_limit
aggregate_key_type = "IP"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "RateLimitMetric"
sampled_requests_enabled = true
}
}
# 地理的制限(必要に応じて)
dynamic "rule" {
for_each = length(var.allowed_countries) > 0 ? [1] : []
content {
name = "GeoLocationRule"
priority = 4
action {
block {}
}
statement {
not_statement {
statement {
geo_match_statement {
country_codes = var.allowed_countries
}
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "GeoLocationMetric"
sampled_requests_enabled = true
}
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "WAFMetric"
sampled_requests_enabled = true
}
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-waf"
}
)
}
# ALBへのWAF関連付け(以下のコードは循環依存を避けるため削除してください)
# resource "aws_wafv2_web_acl_association" "alb" {
# count = var.enable_waf && var.alb_arn != "" ? 1 : 0
#
# resource_arn = var.alb_arn
# web_acl_arn = aws_wafv2_web_acl.main[0].arn
# }
#============================================
# GuardDuty
#============================================
# GuardDuty Detector
resource "aws_guardduty_detector" "main" {
count = var.enable_guardduty ? 1 : 0
enable = true
finding_publishing_frequency = var.guardduty_finding_frequency
# S3 Logs Protection
datasources {
s3_logs {
enable = var.enable_s3_protection
}
# EKS Audit Logs(ECS使用時は不要だが将来の拡張用)
kubernetes {
audit_logs {
enable = false
}
}
# Malware Protection
malware_protection {
scan_ec2_instance_with_findings {
ebs_volumes {
enable = var.enable_malware_protection
}
}
}
}
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-guardduty"
}
)
}
# SNSトピックへの通知設定
resource "aws_cloudwatch_event_rule" "guardduty" {
count = var.enable_guardduty && var.alarm_sns_topic_arn != "" ? 1 : 0
name = "${var.project_name}-${var.environment}-guardduty-findings"
description = "GuardDuty findings notification"
event_pattern = jsonencode({
source = ["aws.guardduty"]
detail-type = ["GuardDuty Finding"]
detail = {
severity = [
{ numeric = [">=", var.guardduty_severity_threshold] }
]
}
})
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-guardduty-rule"
}
)
}
resource "aws_cloudwatch_event_target" "guardduty_sns" {
count = var.enable_guardduty && var.alarm_sns_topic_arn != "" ? 1 : 0
rule = aws_cloudwatch_event_rule.guardduty[0].name
target_id = "SendToSNS"
arn = var.alarm_sns_topic_arn
}
#============================================
# AWS Config
#============================================
# Config用S3バケット
resource "aws_s3_bucket" "config" {
count = var.enable_config ? 1 : 0
bucket = "${var.project_name}-${var.environment}-config-logs"
force_destroy = var.environment != "prod"
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-config-logs"
}
)
}
resource "aws_s3_bucket_public_access_block" "config" {
count = var.enable_config ? 1 : 0
bucket = aws_s3_bucket.config[0].id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
# Config Recorder用IAMロール
resource "aws_iam_role" "config" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-config-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "config.amazonaws.com"
}
}
]
})
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-config-role"
}
)
}
resource "aws_iam_role_policy" "config" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-config-policy"
role = aws_iam_role.config[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"s3:GetBucketAcl",
"s3:ListBucket"
]
Resource = aws_s3_bucket.config[0].arn
},
{
Effect = "Allow"
Action = [
"s3:PutObject",
"s3:GetObject"
]
Resource = "${aws_s3_bucket.config[0].arn}/*"
Condition = {
StringLike = {
"s3:x-amz-server-side-encryption" = "AES256"
}
}
},
{
Effect = "Allow"
Action = [
"config:Put*"
]
Resource = "*"
}
]
})
}
# S3バケットポリシー(Config用)
resource "aws_s3_bucket_policy" "config" {
count = var.enable_config ? 1 : 0
bucket = aws_s3_bucket.config[0].id
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Sid = "AWSConfigBucketPermissionsCheck"
Effect = "Allow"
Principal = {
Service = "config.amazonaws.com"
}
Action = "s3:GetBucketAcl"
Resource = aws_s3_bucket.config[0].arn
Condition = {
StringEquals = {
"AWS:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
},
{
Sid = "AWSConfigBucketExistenceCheck"
Effect = "Allow"
Principal = {
Service = "config.amazonaws.com"
}
Action = "s3:ListBucket"
Resource = aws_s3_bucket.config[0].arn
Condition = {
StringEquals = {
"AWS:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
},
{
Sid = "AWSConfigBucketDelivery"
Effect = "Allow"
Principal = {
Service = "config.amazonaws.com"
}
Action = "s3:PutObject"
Resource = "${aws_s3_bucket.config[0].arn}/*"
Condition = {
StringEquals = {
"s3:x-amz-acl" = "bucket-owner-full-control"
"AWS:SourceAccount" = data.aws_caller_identity.current.account_id
}
}
}
]
})
}
# Configuration Recorderを先に作成
resource "aws_config_configuration_recorder" "main" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-recorder"
role_arn = aws_iam_role.config[0].arn
recording_group {
all_supported = true
include_global_resource_types = true
}
depends_on = [aws_s3_bucket_policy.config]
}
# Delivery Channel(Configuration Recorderの後に作成)
resource "aws_config_delivery_channel" "main" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-channel"
s3_bucket_name = aws_s3_bucket.config[0].bucket
snapshot_delivery_properties {
delivery_frequency = "TwentyFour_Hours"
}
depends_on = [
aws_config_configuration_recorder.main,
aws_s3_bucket_policy.config
]
}
# Configuration Recorderの開始(最後に実行)
resource "aws_config_configuration_recorder_status" "main" {
count = var.enable_config ? 1 : 0
name = aws_config_configuration_recorder.main[0].name
is_enabled = true
depends_on = [
aws_config_configuration_recorder.main,
aws_config_delivery_channel.main
]
}
# Configルール - 必須タグの確認
resource "aws_config_config_rule" "required_tags" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-required-tags"
source {
owner = "AWS"
source_identifier = "REQUIRED_TAGS"
}
input_parameters = jsonencode({
tag1Key = "Environment"
tag2Key = "Project"
tag3Key = "ManagedBy"
})
depends_on = [aws_config_configuration_recorder.main]
}
# Configルール - S3バケットの暗号化確認
resource "aws_config_config_rule" "s3_encryption" {
count = var.enable_config ? 1 : 0
name = "${var.project_name}-${var.environment}-s3-encryption"
source {
owner = "AWS"
source_identifier = "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
}
depends_on = [aws_config_configuration_recorder.main]
}
# Configルール - RDSの暗号化確認
resource "aws_config_config_rule" "rds_encryption" {
count = var.enable_config && var.enable_rds ? 1 : 0
name = "${var.project_name}-${var.environment}-rds-encryption"
source {
owner = "AWS"
source_identifier = "RDS_STORAGE_ENCRYPTED"
}
depends_on = [aws_config_configuration_recorder.main]
}
#============================================
# IAMロールとポリシーの最適化
#============================================
# ECSタスクロールの最適化(既存のタスクロールに追加ポリシー)
resource "aws_iam_role_policy" "ecs_task_optimized" {
count = var.ecs_task_role_id != "" && var.app_bucket_arn != "" ? 1 : 0
name = "${var.project_name}-${var.environment}-ecs-task-optimized"
role = var.ecs_task_role_id
policy = jsonencode({
Version = "2012-10-17"
Statement = concat([
{
Sid = "S3SpecificBucketAccess"
Effect = "Allow"
Action = [
"s3:GetObject",
"s3:PutObject"
]
Resource = [
"${var.app_bucket_arn}/*"
]
},
{
Sid = "SecretsManagerSpecificAccess"
Effect = "Allow"
Action = [
"secretsmanager:GetSecretValue"
]
Resource = [
"arn:aws:secretsmanager:${var.aws_region}:${data.aws_caller_identity.current.account_id}:secret:${var.project_name}/${var.environment}/*"
]
},
{
Sid = "XRayAccess"
Effect = "Allow"
Action = [
"xray:PutTraceSegments",
"xray:PutTelemetryRecords"
]
Resource = "*"
}
], var.log_group_arn != "" ? [
{
Sid = "CloudWatchLogsAccess"
Effect = "Allow"
Action = [
"logs:CreateLogStream",
"logs:PutLogEvents"
]
Resource = [
"${var.log_group_arn}:*"
]
}
] : [])
})
}
# 開発者用IAMポリシー(読み取り専用)
resource "aws_iam_policy" "developer_readonly" {
name = "${var.project_name}-${var.environment}-developer-readonly"
description = "Read-only access for developers"
policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Action = [
"ec2:Describe*",
"ecs:Describe*",
"ecs:List*",
"s3:List*",
"s3:GetObject",
"rds:Describe*",
"cloudwatch:Describe*",
"cloudwatch:Get*",
"cloudwatch:List*",
"logs:Describe*",
"logs:Get*",
"logs:FilterLogEvents"
]
Resource = "*"
},
{
Effect = "Deny"
Action = [
"iam:*",
"kms:Delete*",
"kms:ScheduleKeyDeletion"
]
Resource = "*"
}
]
})
tags = merge(
local.common_tags,
{
Name = "${var.project_name}-${var.environment}-developer-policy"
}
)
}
#============================================
# セキュリティグループの最適化ルール
#============================================
# 注意: 以下のセキュリティグループルールは循環依存を避けるため削除してください
# 各モジュール内でセキュリティグループルールが管理されています
# resource "aws_security_group_rule" "alb_ingress_https" {
# count = var.alb_security_group_id != "" && length(var.allowed_cidr_blocks) > 0 ? 1 : 0
#
# type = "ingress"
# from_port = 443
# to_port = 443
# protocol = "tcp"
# cidr_blocks = var.allowed_cidr_blocks
# security_group_id = var.alb_security_group_id
# description = "HTTPS from allowed IPs"
# }
# ECSセキュリティグループの最適化(削除してください)
# resource "aws_security_group_rule" "ecs_ingress_from_alb_only" {
# count = var.ecs_security_group_id != "" && var.alb_security_group_id != "" ? 1 : 0
#
# type = "ingress"
# from_port = var.app_port
# to_port = var.app_port
# protocol = "tcp"
# source_security_group_id = var.alb_security_group_id
# security_group_id = var.ecs_security_group_id
# description = "Allow traffic only from ALB"
# }
# RDSセキュリティグループの最適化(削除してください)
# resource "aws_security_group_rule" "rds_ingress_from_ecs_only" {
# count = var.rds_security_group_id != "" && var.ecs_security_group_id != "" ? 1 : 0
#
# type = "ingress"
# from_port = 5432
# to_port = 5432
# protocol = "tcp"
# source_security_group_id = var.ecs_security_group_id
# security_group_id = var.rds_security_group_id
# description = "PostgreSQL access only from ECS tasks"
# }
WAF設定パラメータの説明
パラメータ | 設定値 | 用途・説明 |
---|---|---|
scope | REGIONAL | ALBに関連付けるためREGIONALを指定 |
default_action | allow | デフォルトは許可、ルールでブロック |
AWSManagedRulesCommonRuleSet | 有効 | 一般的な脆弱性から保護(XSS、パストラバーサルなど) |
AWSManagedRulesSQLiRuleSet | 有効 | SQLインジェクション攻撃を検知・ブロック |
rate_based_statement.limit | 変数で設定 | 5分間のリクエスト数制限 |
geo_match_statement | オプション | 特定の国からのアクセスのみ許可 |
cloudwatch_metrics_enabled | true | メトリクスを CloudWatch に送信 |
GuardDuty設定パラメータの説明
パラメータ | 設定値 | 用途・説明 |
---|---|---|
enable | true | GuardDutyを有効化 |
finding_publishing_frequency | 変数で設定 | 検出結果の更新頻度 |
s3_logs.enable | 変数で設定 | S3アクセスログの異常を検知 |
malware_protection | 変数で設定 | EC2インスタンスのマルウェアスキャン |
severity_threshold | 変数で設定 | 通知する脅威レベル |
event_pattern | CloudWatch Events | 特定の重要度以上の脅威を通知 |
Config設定パラメータの説明
パラメータ | 設定値 | 用途・説明 |
---|---|---|
all_supported | true | すべてのサポートされるリソースを記録 |
include_global_resource_types | true | IAMなどグローバルリソースも記録 |
delivery_frequency | TwentyFour_Hours | 設定スナップショットの配信頻度 |
REQUIRED_TAGS | Environment, Project, ManagedBy | 必須タグの確認ルール |
S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED | 有効 | S3暗号化の確認 |
RDS_STORAGE_ENCRYPTED | 有効 | RDS暗号化の確認 |
IAM最適化パラメータの説明
パラメータ | 設定値 | 用途・説明 |
---|---|---|
S3SpecificBucketAccess | 特定バケットのみ | 最小権限の原則に従い必要なバケットのみアクセス許可 |
SecretsManagerSpecificAccess | プロジェクト専用 | 環境別のシークレットのみアクセス可能 |
CloudWatchLogsAccess | 特定ロググループ | アプリケーション専用のロググループのみ書き込み可能 |
developer_readonly | 読み取り専用 | 開発者は参照のみ、変更不可 |
Deny Actions | IAM、KMS削除 | 重要な操作は明示的に拒否 |
モジュールの変数定義
variableは重複しないように注意しましょう。
# terraform/modules/security/variables.tf
variable "project_name" {
description = "プロジェクト名"
type = string
}
variable "environment" {
description = "環境名(dev/stg/prod)"
type = string
}
variable "aws_region" {
description = "AWSリージョン"
type = string
default = "ap-northeast-1"
}
# VPC関連(既存のセキュリティモジュールで必要)
variable "vpc_id" {
description = "VPC ID"
type = string
}
variable "vpc_cidr_block" {
description = "VPCのCIDRブロック"
type = string
}
variable "enable_vpc_endpoints" {
description = "VPCエンドポイントを有効にするか"
type = bool
default = false
}
variable "app_port" {
description = "アプリケーションポート番号"
type = number
default = 3000
}
# WAF関連
# 重複注意
variable "enable_waf" {
description = "WAFを有効にするか"
type = bool
default = false
}
variable "waf_rate_limit" {
description = "WAF rate limit per 5 minutes"
type = number
default = 2000
}
variable "allowed_countries" {
description = "List of allowed country codes"
type = list(string)
default = []
}
variable "alb_arn" {
description = "ALB ARN for WAF association"
type = string
default = ""
}
# GuardDuty関連
variable "enable_guardduty" {
description = "GuardDutyを有効にするか"
type = bool
default = false
}
variable "guardduty_finding_frequency" {
description = "GuardDuty finding publishing frequency"
type = string
default = "FIFTEEN_MINUTES"
}
variable "guardduty_severity_threshold" {
description = "GuardDuty severity threshold for notifications"
type = number
default = 4
}
variable "enable_s3_protection" {
description = "Enable S3 protection in GuardDuty"
type = bool
default = true
}
variable "enable_malware_protection" {
description = "Enable malware protection in GuardDuty"
type = bool
default = false
}
variable "alarm_sns_topic_arn" {
description = "SNS Topic ARN for alarms"
type = string
default = ""
}
# Config関連
variable "enable_config" {
description = "AWS Configを有効にするか"
type = bool
default = false
}
variable "enable_rds" {
description = "RDSが有効か(Config RDSルール用)"
type = bool
default = false
}
# セキュリティグループ関連
variable "allowed_cidr_blocks" {
description = "Allowed CIDR blocks for ALB access"
type = list(string)
default = ["0.0.0.0/0"]
}
variable "alb_security_group_id" {
description = "ALB Security Group ID"
type = string
default = ""
}
variable "ecs_security_group_id" {
description = "ECS Security Group ID"
type = string
default = ""
}
variable "rds_security_group_id" {
description = "RDS Security Group ID"
type = string
default = ""
}
# IAM関連
variable "ecs_task_role_id" {
description = "ECS Task Role ID"
type = string
default = ""
}
variable "app_bucket_arn" {
description = "Application S3 Bucket ARN"
type = string
default = ""
}
variable "log_group_arn" {
description = "CloudWatch Log Group ARN"
type = string
default = ""
}
モジュールの出力定義
# terraform/modules/security/outputs.tf
# WAF関連
output "waf_web_acl_id" {
description = "WAF WebACL ID"
value = var.enable_waf ? aws_wafv2_web_acl.main[0].id : ""
}
output "waf_web_acl_arn" {
description = "WAF WebACL ARN"
value = var.enable_waf ? aws_wafv2_web_acl.main[0].arn : ""
}
# GuardDuty関連
output "guardduty_detector_id" {
description = "GuardDuty Detector ID"
value = var.enable_guardduty ? aws_guardduty_detector.main[0].id : ""
}
# Config関連
output "config_recorder_name" {
description = "Config Recorder Name"
value = var.enable_config ? aws_config_configuration_recorder.main[0].name : ""
}
output "config_required_tags_rule_name" {
description = "Config Required Tags Rule Name"
value = var.enable_config ? aws_config_config_rule.required_tags[0].name : ""
}
output "config_s3_bucket_name" {
description = "Config S3 Bucket Name"
value = var.enable_config ? aws_s3_bucket.config[0].id : ""
}
# IAM関連
output "developer_readonly_policy_arn" {
description = "Developer Read-only Policy ARN"
value = aws_iam_policy.developer_readonly.arn
}
環境別ディレクトリの設定
# terraform/environments/dev/main.tf
terraform {
required_version = ">= 1.5"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
# backend.hclで設定
}
}
provider "aws" {
region = var.aws_region
default_tags {
tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "terraform"
}
}
}
# 共通タグの定義
locals {
common_tags = {
Project = var.project_name
Environment = var.environment
ManagedBy = "terraform"
}
}
# Network Module
module "network" {
source = "../../modules/network"
project_name = var.project_name
environment = var.environment
vpc_cidr = var.vpc_cidr
enable_nat_gateway = var.enable_nat_gateway
}
# Security Module
module "security" {
source = "../../modules/security"
# 基本設定
project_name = var.project_name
environment = var.environment
vpc_id = module.network.vpc_id
vpc_cidr_block = var.vpc_cidr
enable_vpc_endpoints = false
# セキュリティ機能の有効化
enable_waf = var.enable_waf
enable_guardduty = var.enable_guardduty
enable_config = var.enable_config
# WAF設定
waf_rate_limit = var.waf_rate_limit
allowed_countries = var.allowed_countries
# GuardDuty設定
guardduty_finding_frequency = var.guardduty_finding_frequency
guardduty_severity_threshold = var.guardduty_severity_threshold
enable_s3_protection = var.enable_s3_protection
enable_malware_protection = var.enable_malware_protection
# アクセス制御
allowed_cidr_blocks = var.allowed_cidr_blocks
# 既存リソースとの連携
alb_arn = module.loadbalancer.alb_arn
alb_security_group_id = module.loadbalancer.alb_security_group_id
ecs_security_group_id = module.compute.ecs_security_group_id
rds_security_group_id = var.enable_rds ? module.database[0].security_group_id : ""
ecs_task_role_id = module.compute.ecs_task_role_name
app_bucket_arn = module.storage.frontend_bucket_arn
log_group_arn = length(module.monitoring) > 0 ? module.monitoring[0].log_group_arn : ""
alarm_sns_topic_arn = length(module.monitoring) > 0 ? module.monitoring[0].sns_topic_arn : ""
enable_rds = var.enable_rds
}
# Storage Module
module "storage" {
source = "../../modules/storage"
project_name = var.project_name
environment = var.environment
enable_versioning = false
cloudfront_distribution_arn = "" # 後で設定
enable_alb_access_logs = var.enable_alb_access_logs
enable_cloudfront_logging = false
enable_cicd = var.enable_cicd
artifacts_retention_days = 7
enable_backup = false
}
# Database Module(開発環境では条件付き)
module "database" {
count = var.enable_rds ? 1 : 0 # RDS有効化変数で制御
source = "../../modules/database"
project_name = var.project_name
environment = var.environment
vpc_id = module.network.vpc_id
private_subnet_ids = module.network.private_subnet_ids
ecs_security_group_id = module.compute.ecs_security_group_id
db_engine_version = "15.8"
db_instance_class = "db.t3.micro"
db_allocated_storage = 20
db_username = "postgres"
multi_az = false
backup_retention_period = 1
enable_performance_insights = false
enable_rds_proxy = false
}
# Compute Module
module "compute" {
source = "../../modules/compute"
project_name = var.project_name
environment = var.environment
aws_region = var.aws_region
task_cpu = "256"
task_memory = "512"
desired_count = 1
container_port = 3000
min_capacity = 1
max_capacity = 2
vpc_id = module.network.vpc_id
private_subnet_ids = module.network.private_subnet_ids
alb_security_group_id = module.loadbalancer.alb_security_group_id
target_group_arn = module.loadbalancer.target_group_arn
db_secret_arn = var.enable_rds && length(module.database) > 0 ? module.database[0].db_secret_arn : ""
allowed_origins = var.allowed_origins
enable_security_headers = var.enable_security_headers
enable_xray_tracing = var.enable_xray_tracing
}
# Load Balancer Module
module "loadbalancer" {
source = "../../modules/loadbalancer"
project_name = var.project_name
environment = var.environment
vpc_id = module.network.vpc_id
public_subnet_ids = module.network.public_subnet_ids
target_port = 3000
health_check_path = "/health"
enable_https = var.alb_certificate_arn != ""
certificate_arn = var.alb_certificate_arn
enable_access_logs = var.enable_alb_access_logs
access_logs_bucket = var.enable_alb_access_logs ? module.storage.alb_logs_bucket_name : null
enable_xray_tracing = var.enable_xray_tracing
}
# CDN Module
module "cdn" {
source = "../../modules/cdn"
project_name = var.project_name
environment = var.environment
common_tags = local.common_tags
cloudfront_price_class = "PriceClass_100"
frontend_domain_name = var.frontend_domain_name
frontend_certificate_arn = var.frontend_certificate_arn
enable_cloudfront_logging = false
s3_bucket_id = module.storage.frontend_bucket_id
s3_bucket_domain_name = module.storage.frontend_bucket_regional_domain_name
alb_dns_name = module.loadbalancer.alb_dns_name
alb_name = module.loadbalancer.alb_name
cloudfront_logs_bucket_domain_name = ""
}
# Monitoring Module(アラームが有効な場合のみ)
module "monitoring" {
count = var.enable_cpu_alarm || var.enable_memory_alarm || var.enable_unhealthy_host_alarm ? 1 : 0
source = "../../modules/monitoring"
project_name = var.project_name
environment = var.environment
aws_region = var.aws_region
common_tags = local.common_tags
ecs_log_group_name = "/ecs/${var.project_name}-${var.environment}"
alarm_email = var.alarm_email
enable_cpu_alarm = var.enable_cpu_alarm
enable_memory_alarm = var.enable_memory_alarm
enable_unhealthy_host_alarm = var.enable_unhealthy_host_alarm
cpu_utilization_threshold = var.cpu_utilization_threshold
memory_utilization_threshold = var.memory_utilization_threshold
ecs_cluster_name = module.compute.ecs_cluster_name
ecs_service_name = module.compute.ecs_service_name
ecs_task_role_name = module.compute.ecs_task_role_name
target_group_arn_suffix = module.loadbalancer.target_group_arn_suffix
load_balancer_arn_suffix = module.loadbalancer.alb_arn_suffix
enable_xray_tracing = var.enable_xray_tracing
enable_alb_access_logs = var.enable_alb_access_logs
}
# CI/CD Module(enable_cicdが有効かつGitHubリポジトリが設定されている場合のみ)
module "cicd" {
count = var.enable_cicd && var.github_repository != "" ? 1 : 0
source = "../../modules/cicd"
project_name = var.project_name
environment = var.environment
aws_region = var.aws_region
common_tags = local.common_tags
enable_github_oidc = true
github_repository = var.github_repository
ecr_repository_arn = module.compute.ecr_repository_arn
ecr_repository_url = module.compute.ecr_repository_url
ecs_cluster_arn = module.compute.ecs_cluster_arn
ecs_cluster_name = module.compute.ecs_cluster_name
ecs_service_arn = module.compute.ecs_service_arn
ecs_service_name = module.compute.ecs_service_name
ecs_execution_role_arn = module.compute.ecs_execution_role_arn
ecs_task_role_arn = module.compute.ecs_task_role_arn
frontend_bucket_arn = module.storage.frontend_bucket_arn
cloudfront_distribution_arn = module.cdn.cloudfront_distribution_arn
artifacts_bucket_name = module.storage.artifacts_bucket_name
artifacts_bucket_arn = module.storage.artifacts_bucket_arn
artifact_retention_days = 7
}
# VPC Endpoints Module(開発環境では無効)
module "vpc_endpoints" {
count = var.enable_vpc_endpoints ? 1 : 0
source = "../../modules/vpc_endpoints"
project_name = var.project_name
environment = var.environment
aws_region = var.aws_region
common_tags = local.common_tags
vpc_id = module.network.vpc_id
vpc_cidr = var.vpc_cidr
private_subnet_ids = module.network.private_subnet_ids
route_table_ids = concat(
module.network.private_route_table_ids,
[module.network.public_route_table_id]
)
enable_vpc_endpoints = var.enable_vpc_endpoints
enable_xray_tracing = var.enable_xray_tracing
enable_vpc_flow_logs = var.enable_vpc_flow_logs
flow_logs_retention_days = var.flow_logs_retention_days
}
# Data sources
data "aws_caller_identity" "current" {}
環境別変数定義
# terraform/environments/dev/variables.tf
# セキュリティ機能の有効化フラグ
variable "enable_waf" {
description = "WAFを有効にするかどうか"
type = bool
default = false
}
variable "enable_guardduty" {
description = "GuardDutyを有効にするか"
type = bool
default = false
}
variable "enable_config" {
description = "AWS Configを有効にするか"
type = bool
default = false
}
# WAF関連の設定
variable "waf_rate_limit" {
description = "WAF rate limit per 5 minutes"
type = number
default = 5000
}
variable "allowed_countries" {
description = "List of allowed country codes"
type = list(string)
default = ["JP", "US"]
}
# GuardDuty関連の設定
variable "guardduty_finding_frequency" {
description = "GuardDuty finding publishing frequency"
type = string
default = "ONE_HOUR"
validation {
condition = contains([
"FIFTEEN_MINUTES",
"ONE_HOUR",
"SIX_HOURS"
], var.guardduty_finding_frequency)
error_message = "有効な頻度を選択してください"
}
}
variable "guardduty_severity_threshold" {
description = "GuardDuty severity threshold for notifications (1-8, 1=lowest, 8=highest)"
type = number
default = 7
validation {
condition = var.guardduty_severity_threshold >= 1 && var.guardduty_severity_threshold <= 8
error_message = "脅威レベルは1から8の間で設定してください"
}
}
variable "enable_s3_protection" {
description = "Enable S3 protection in GuardDuty"
type = bool
default = false
}
variable "enable_malware_protection" {
description = "Enable malware protection in GuardDuty"
type = bool
default = false
}
variable "allowed_cidr_blocks" {
description = "許可するCIDRブロック"
type = list(string)
default = ["0.0.0.0/0"]
}
環境別設定値の推奨
設定項目 | 開発環境 (dev) | ステージング環境 (stg) | 本番環境 (prod) |
---|---|---|---|
waf_rate_limit | 5000 | 3000 | 2000 |
allowed_countries | [“JP”, “US”] | [“JP”] | [“JP”] |
guardduty_finding_frequency | ONE_HOUR | FIFTEEN_MINUTES | FIFTEEN_MINUTES |
guardduty_severity_threshold | 7 | 5 | 4 |
enable_s3_protection | false | true | true |
enable_malware_protection | false | false | true |
allowed_cidr_blocks | [“0.0.0.0/0”] | [“10.0.0.0/8”] | [“203.0.113.0/24”] |
terraform.tfvarsの設定
# terraform/environments/dev/terraform.tfvars
# プロジェクト設定
# project_name = "" 環境変数(TF_VAR_project_name)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# environment = "dev" # variables.tfで定義されている環境名(dev/stg/prod)に合わせて設定
# 環境別機能有効化設定
# ========================================
# インフラストラクチャ
enable_rds = true # RDSデータベースを有効化(学習目的)
enable_nat_gateway = false # NAT Gatewayを無効化(コスト削減)
# セキュリティ・監視
enable_waf = false # WAFを無効化(開発環境)
enable_vpc_endpoints = true # VPCエンドポイントを有効化(NAT Gateway無効のため必須)
enable_vpc_flow_logs = false # VPC Flow Logsを無効化(コスト削減)
enable_alb_access_logs = false # ALBアクセスログを無効化(コスト削減)
enable_xray_tracing = false # X-Rayトレーシングを無効化(コスト削減)
enable_cloudfront_logging = false # CloudFrontログを無効化(コスト削減)
enable_guardduty = true
enable_config = true
# アラーム・監視
enable_cpu_alarm = false # CPUアラームを無効化(開発環境)
enable_memory_alarm = false # メモリアラームを無効化(開発環境)
enable_unhealthy_host_alarm = false # 異常ホストアラームを無効化(開発環境)
# CI/CD
# github_repository = "" # GitHubリポジトリ 環境変数(TF_VAR_github_repository)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# その他の環境固有設定
# ========================================
allowed_origins = [] # CORS許可オリジン(実際の値を設定)
# alarm_email = "" # アラーム通知先メール 環境変数(TF_VAR_alarm_email)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# SSL証明書(オプション)
alb_certificate_arn = ""
frontend_certificate_arn = ""
frontend_domain_name = ""
リソースを作成
Terraformコンテナ内で以下のコマンドを実行します。
# 環境別ディレクトリへ移動
cd terraform/environments/dev
# 実行計画の確認
terraform plan
# リソースの作成
terraform apply
WAF、GuardDuty、Configの設定には5〜10分程度かかります。
動作確認
WAFの動作確認
# WAF WebACL の確認
aws wafv2 list-web-acls --scope REGIONAL --region ap-northeast-1
# ブロックされたリクエストの確認
aws wafv2 get-sampled-requests \
--web-acl-arn $(terraform output -raw waf_web_acl_arn) \
--rule-metric-name RateLimitMetric \
--scope REGIONAL \
--time-window StartTime=$(date -u -d '1 hour ago' +%s),EndTime=$(date +%s) \
--max-items 10
GuardDutyの確認
# GuardDuty Detectorの状態確認
aws guardduty list-detectors
# 検出結果の確認(ある場合)
DETECTOR_ID=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)
aws guardduty list-findings --detector-id $DETECTOR_ID
AWS Configの確認
# Configuration Recorderの状態確認
aws configservice describe-configuration-recorder-status
# コンプライアンスルールの確認
aws configservice describe-compliance-by-config-rule \
--config-rule-names $(terraform output -raw config_required_tags_rule_name)
IAMポリシーの確認
# 開発者用読み取り専用ポリシーの確認
aws iam get-policy \
--policy-arn $(terraform output -raw developer_readonly_policy_arn)
# ポリシーの詳細確認
aws iam get-policy-version \
--policy-arn $(terraform output -raw developer_readonly_policy_arn) \
--version-id v1
セキュリティベストプラクティスのチェックリスト
ネットワークセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
VPC設計 | プライベートサブネットの活用 | ✓ |
セキュリティグループ | 最小権限の原則 | ✓ |
NACLs | 追加の防御層 | ✓ |
VPCエンドポイント | プライベート通信 | ✓ |
アプリケーションセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
WAF | SQLインジェクション対策 | ✓ |
HTTPS | 暗号化通信 | ✓ |
CORS | 設定 | ✓ |
セキュリティヘッダー | CSP、X-Frame-Options | ✓ |
データセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
S3暗号化 | デフォルト暗号化 | ✓ |
RDS暗号化 | 保存時暗号化 | ✓ |
Secrets Manager | 機密情報の管理 | ✓ |
バックアップ | 自動バックアップ | ✓ |
アクセス管理
項目 | 確認内容 | ステータス |
---|---|---|
IAM最小権限 | 必要最小限の権限 | ✓ |
MFA | 多要素認証 | ✓ |
ロールベース | ユーザーではなくロール | ✓ |
定期的な棚卸し | 不要な権限の削除 | ✓ |
監視・監査
項目 | 確認内容 | ステータス |
---|---|---|
CloudTrail | API呼び出しの記録 | ✓ |
VPC Flow Logs | ネットワークトラフィック | ✓ |
GuardDuty | 脅威検知 | ✓ |
Config | コンプライアンス | ✓ |
パフォーマンステストと負荷試験
負荷試験の実施
# Apache Benchを使用した簡単な負荷試験
ab -n 1000 -c 50 http://$(terraform output -raw alb_dns_name)/health
# 結果の確認項目
# - Requests per second
# - Time per request
# - Transfer rate
# - Failed requests
CloudWatch メトリクスの確認
# ECSサービスのメトリクス
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ServiceName,Value=$(terraform output -raw ecs_service_name) \
Name=ClusterName,Value=$(terraform output -raw ecs_cluster_name) \
--start-time $(date -u -d '1 hour ago' --iso-8601) \
--end-time $(date -u --iso-8601) \
--period 300 \
--statistics Average
# RDSのメトリクス(RDSが有効な場合)
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions Name=DBInstanceIdentifier,Value=$(terraform output -raw rds_instance_id) \
--start-time $(date -u -d '1 hour ago' --iso-8601) \
--end-time $(date -u --iso-8601) \
--period 300 \
--statistics Average
コスト最適化の総まとめ
環境別のコスト最適化戦略
環境 | 最適化方法 | 削減率 |
---|---|---|
開発環境 | NAT Gateway無効化、小規模インスタンス | 60-70% |
ステージング環境 | 夜間停止、Reserved Instances | 40-50% |
本番環境 | Auto Scaling、Spot Instances活用 | 20-30% |
サービス別月額コスト目安(東京リージョン)
※以下のコスト目安は参考値です。実際のコストは使用状況により変動します。
サービス | 開発環境 | 本番環境 | 備考 |
---|---|---|---|
VPC (NAT Gateway) | $0 | $90 | 開発は無効化 |
ECS Fargate | $30 | $200 | タスク数とCPU/メモリによる |
RDS | $30 | $150 | Multi-AZ、バックアップ含む |
ALB | $25 | $25 | 基本料金 |
S3 + CloudFront | $5 | $50 | トラフィック依存 |
WAF | $10 | $10 | 基本料金 |
GuardDuty | $5 | $20 | イベント数依存 |
合計 | $105 | $545 | 概算値 |
コスト削減のTips
-
開発環境の最適化
- 営業時間外の自動停止
- NAT Gatewayの無効化
- 小規模インスタンスサイズ
-
Reserved InstancesとSavings Plans
- 1年または3年契約で最大72%削減
- 本番環境の予測可能なワークロード
-
Spot Instances
- バッチ処理やテスト環境で活用
- 最大90%のコスト削減
-
S3ストレージクラス
- アクセス頻度に応じた最適化
- Intelligent-Tieringの活用
トラブルシューティング
WAF関連
問題: 正常なリクエストがブロックされる
確認事項 | 対処方法 |
---|---|
ブロックルールの特定 | CloudWatch Logsでルール名を確認 |
誤検知の修正 | 特定のルールを除外設定 |
レート制限の調整 | しきい値を環境に応じて調整 |
GuardDuty関連
問題: 誤検知が多い
確認事項 | 対処方法 |
---|---|
信頼済みIPリスト | 社内IPを信頼リストに追加 |
抑制ルール | 特定のFindingタイプを抑制 |
重要度の調整 | 通知しきい値を上げる |
Config関連
問題: コンプライアンス違反が検出される
確認事項 | 対処方法 |
---|---|
違反リソースの特定 | Config Dashboardで確認 |
修正アクション | 自動修正ルールの設定 |
例外処理 | 特定リソースを除外 |
ハンズオンシリーズの総まとめ
構築したアーキテクチャの全体像
14回にわたるハンズオンで、以下の本番運用可能なインフラストラクチャを構築しました。
- ネットワーク基盤: VPC、サブネット、ルーティング
- セキュリティ層: Security Groups、NACL、WAF
- コンピューティング: ECS Fargate、Auto Scaling
- データベース: RDS PostgreSQL、Multi-AZ
- ストレージ: S3、ECR
- 配信: CloudFront CDN
- 監視: CloudWatch、X-Ray、GuardDuty
- CI/CD: GitHub Actions、自動デプロイ
- IaC: Terraform による完全自動化
習得したスキル
インフラストラクチャ設計
- マルチAZ構成による高可用性
- プライベートサブネットによるセキュアな設計
- VPCエンドポイントによる内部通信
セキュリティ実装
- 多層防御アーキテクチャ
- 最小権限の原則
- 暗号化と監査ログ
運用自動化
- Infrastructure as Code
- CI/CDパイプライン
- 監視とアラート
コスト最適化
- 環境別の最適化戦略
- リソースのサイジング
- 使用状況の可視化
まとめ
本ハンズオンシリーズを通じて、AWSの主要サービスを活用した本番運用可能なWebアプリケーション基盤を構築しました。
インフラに限らず、技術は手を動かさないと身にならないと思います。
自身はインフラ素人のため、現場でインフラの話になると「わかる人」に任せきりになってしまう場面が多くなってしまう。
そんな現状を打破すべく、0から勉強しようということで、自分の手を動かしてハンズオンを作ってみました。
本気で学習する気がないと踏破できないだろうな…と思いつつ書きましたが、コスト面はなるべく抑えたつもりです。ここまで辿り着いた方々は自信を持ちましょう。
default = false # 開発環境ではfalse
}
variable “enable_unhealthy_host_alarm” {
description = "異常ホストアラームを有効
環境別設定値の推奨
設定項目 | 開発環境 (dev) | ステージング環境 (stg) | 本番環境 (prod) |
---|---|---|---|
waf_rate_limit | 5000 | 3000 | 2000 |
allowed_countries | [“JP”, “US”] | [“JP”] | [“JP”] |
guardduty_finding_frequency | ONE_HOUR | FIFTEEN_MINUTES | FIFTEEN_MINUTES |
guardduty_severity_threshold | 7 | 5 | 4 |
enable_s3_protection | false | true | true |
enable_malware_protection | false | false | true |
allowed_cidr_blocks | [“0.0.0.0/0”] | [“10.0.0.0/8”] | [“203.0.113.0/24”] |
terraform.tfvarsの設定
# terraform/environments/dev/terraform.tfvars
# プロジェクト設定
# project_name = "" 環境変数(TF_VAR_project_name)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# environment = "dev" # variables.tfで定義されている環境名(dev/stg/prod)に合わせて設定
# 環境別機能有効化設定
# ========================================
# インフラストラクチャ
enable_rds = true # RDSデータベースを有効化(学習目的)
enable_nat_gateway = false # NAT Gatewayを無効化(コスト削減)
# セキュリティ・監視
enable_waf = false # WAFを無効化(開発環境)
enable_vpc_endpoints = true # VPCエンドポイントを有効化(NAT Gateway無効のため必須)
enable_vpc_flow_logs = false # VPC Flow Logsを無効化(コスト削減)
enable_alb_access_logs = false # ALBアクセスログを無効化(コスト削減)
enable_xray_tracing = false # X-Rayトレーシングを無効化(コスト削減)
enable_cloudfront_logging = false # CloudFrontログを無効化(コスト削減)
enable_guardduty = true
enable_config = true
# アラーム・監視
enable_cpu_alarm = false # CPUアラームを無効化(開発環境)
enable_memory_alarm = false # メモリアラームを無効化(開発環境)
enable_unhealthy_host_alarm = false # 異常ホストアラームを無効化(開発環境)
# CI/CD
# github_repository = "" # GitHubリポジトリ 環境変数(TF_VAR_github_repository)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# その他の環境固有設定
# ========================================
allowed_origins = [] # CORS許可オリジン(実際の値を設定)
# alarm_email = "" # アラーム通知先メール 環境変数(TF_VAR_alarm_email)で設定しているが、環境別で個別指定の場合はコメントアウト解除
# SSL証明書(オプション)
alb_certificate_arn = ""
frontend_certificate_arn = ""
frontend_domain_name = ""
リソースを作成
Terraformコンテナ内で以下のコマンドを実行します。
# 環境別ディレクトリへ移動
cd terraform/environments/dev
# 実行計画の確認
terraform plan
# リソースの作成
terraform apply
WAF、GuardDuty、Configの設定には5〜10分程度かかります。
動作確認
WAFの動作確認
# WAF WebACL の確認
aws wafv2 list-web-acls --scope REGIONAL --region ap-northeast-1
# ブロックされたリクエストの確認
aws wafv2 get-sampled-requests \
--web-acl-arn $(terraform output -raw waf_web_acl_arn) \
--rule-metric-name RateLimitMetric \
--scope REGIONAL \
--time-window StartTime=$(date -u -d '1 hour ago' +%s),EndTime=$(date +%s) \
--max-items 10
GuardDutyの確認
# GuardDuty Detectorの状態確認
aws guardduty list-detectors
# 検出結果の確認(ある場合)
DETECTOR_ID=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)
aws guardduty list-findings --detector-id $DETECTOR_ID
AWS Configの確認
# Configuration Recorderの状態確認
aws configservice describe-configuration-recorder-status
# コンプライアンスルールの確認
aws configservice describe-compliance-by-config-rule \
--config-rule-names $(terraform output -raw config_required_tags_rule_name)
IAMポリシーの確認
# 開発者用読み取り専用ポリシーの確認
aws iam get-policy \
--policy-arn $(terraform output -raw developer_readonly_policy_arn)
# ポリシーの詳細確認
aws iam get-policy-version \
--policy-arn $(terraform output -raw developer_readonly_policy_arn) \
--version-id v1
セキュリティベストプラクティスのチェックリスト
ネットワークセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
VPC設計 | プライベートサブネットの活用 | ✓ |
セキュリティグループ | 最小権限の原則 | ✓ |
NACLs | 追加の防御層 | ✓ |
VPCエンドポイント | プライベート通信 | ✓ |
アプリケーションセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
WAF | SQLインジェクション対策 | ✓ |
HTTPS | 暗号化通信 | ✓ |
CORS | 設定 | ✓ |
セキュリティヘッダー | CSP、X-Frame-Options | ✓ |
データセキュリティ
項目 | 確認内容 | ステータス |
---|---|---|
S3暗号化 | デフォルト暗号化 | ✓ |
RDS暗号化 | 保存時暗号化 | ✓ |
Secrets Manager | 機密情報の管理 | ✓ |
バックアップ | 自動バックアップ | ✓ |
アクセス管理
項目 | 確認内容 | ステータス |
---|---|---|
IAM最小権限 | 必要最小限の権限 | ✓ |
MFA | 多要素認証 | ✓ |
ロールベース | ユーザーではなくロール | ✓ |
定期的な棚卸し | 不要な権限の削除 | ✓ |
監視・監査
項目 | 確認内容 | ステータス |
---|---|---|
CloudTrail | API呼び出しの記録 | ✓ |
VPC Flow Logs | ネットワークトラフィック | ✓ |
GuardDuty | 脅威検知 | ✓ |
Config | コンプライアンス | ✓ |
パフォーマンステストと負荷試験
負荷試験の実施
# Apache Benchを使用した簡単な負荷試験
ab -n 1000 -c 50 http://$(terraform output -raw alb_dns_name)/health
# 結果の確認項目
# - Requests per second
# - Time per request
# - Transfer rate
# - Failed requests
CloudWatch メトリクスの確認
# ECSサービスのメトリクス
aws cloudwatch get-metric-statistics \
--namespace AWS/ECS \
--metric-name CPUUtilization \
--dimensions Name=ServiceName,Value=$(terraform output -raw ecs_service_name) \
Name=ClusterName,Value=$(terraform output -raw ecs_cluster_name) \
--start-time $(date -u -d '1 hour ago' --iso-8601) \
--end-time $(date -u --iso-8601) \
--period 300 \
--statistics Average
# RDSのメトリクス(RDSが有効な場合)
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions Name=DBInstanceIdentifier,Value=$(terraform output -raw rds_instance_id) \
--start-time $(date -u -d '1 hour ago' --iso-8601) \
--end-time $(date -u --iso-8601) \
--period 300 \
--statistics Average
コスト最適化の総まとめ
環境別のコスト最適化戦略
環境 | 最適化方法 | 削減率 |
---|---|---|
開発環境 | NAT Gateway無効化、小規模インスタンス | 60-70% |
ステージング環境 | 夜間停止、Reserved Instances | 40-50% |
本番環境 | Auto Scaling、Spot Instances活用 | 20-30% |
サービス別月額コスト目安(東京リージョン)
※以下のコスト目安は参考値です。実際のコストは使用状況により変動します。
サービス | 開発環境 | 本番環境 | 備考 |
---|---|---|---|
VPC (NAT Gateway) | $0 | $90 | 開発は無効化 |
ECS Fargate | $30 | $200 | タスク数とCPU/メモリによる |
RDS | $30 | $150 | Multi-AZ、バックアップ含む |
ALB | $25 | $25 | 基本料金 |
S3 + CloudFront | $5 | $50 | トラフィック依存 |
WAF | $10 | $10 | 基本料金 |
GuardDuty | $5 | $20 | イベント数依存 |
合計 | $105 | $545 | 概算値 |
コスト削減のTips
-
開発環境の最適化
- 営業時間外の自動停止
- NAT Gatewayの無効化
- 小規模インスタンスサイズ
-
Reserved InstancesとSavings Plans
- 1年または3年契約で最大72%削減
- 本番環境の予測可能なワークロード
-
Spot Instances
- バッチ処理やテスト環境で活用
- 最大90%のコスト削減
-
S3ストレージクラス
- アクセス頻度に応じた最適化
- Intelligent-Tieringの活用
トラブルシューティング
WAF関連
問題: 正常なリクエストがブロックされる
確認事項 | 対処方法 |
---|---|
ブロックルールの特定 | CloudWatch Logsでルール名を確認 |
誤検知の修正 | 特定のルールを除外設定 |
レート制限の調整 | しきい値を環境に応じて調整 |
GuardDuty関連
問題: 誤検知が多い
確認事項 | 対処方法 |
---|---|
信頼済みIPリスト | 社内IPを信頼リストに追加 |
抑制ルール | 特定のFindingタイプを抑制 |
重要度の調整 | 通知しきい値を上げる |
Config関連
問題: コンプライアンス違反が検出される
確認事項 | 対処方法 |
---|---|
違反リソースの特定 | Config Dashboardで確認 |
修正アクション | 自動修正ルールの設定 |
例外処理 | 特定リソースを除外 |
ハンズオンシリーズの総まとめ
構築したアーキテクチャの全体像
14回にわたるハンズオンで、以下の本番運用可能なインフラストラクチャを構築しました。
- ネットワーク基盤: VPC、サブネット、ルーティング
- セキュリティ層: Security Groups、NACL、WAF
- コンピューティング: ECS Fargate、Auto Scaling
- データベース: RDS PostgreSQL、Multi-AZ
- ストレージ: S3、ECR
- 配信: CloudFront CDN
- 監視: CloudWatch、X-Ray、GuardDuty
- CI/CD: GitHub Actions、自動デプロイ
- IaC: Terraform による完全自動化
習得したスキル
インフラストラクチャ設計
- マルチAZ構成による高可用性
- プライベートサブネットによるセキュアな設計
- VPCエンドポイントによる内部通信
セキュリティ実装
- 多層防御アーキテクチャ
- 最小権限の原則
- 暗号化と監査ログ
運用自動化
- Infrastructure as Code
- CI/CDパイプライン
- 監視とアラート
コスト最適化
- 環境別の最適化戦略
- リソースのサイジング
- 使用状況の可視化
まとめ
本ハンズオンシリーズを通じて、AWSの主要サービスを活用した本番運用可能なWebアプリケーション基盤を構築しました。
インフラに限らず、技術は手を動かさないと身にならないと思います。
自身はインフラ素人のため、現場でインフラの話になると「わかる人」に任せきりになってしまう場面が多くなってしまう。
そんな現状を打破すべく、0から勉強しようということで、自分の手を動かしてハンズオンを作ってみました。
本気で学習する気がないと踏破できないだろうな…と思いつつ書きましたが、コスト面はなるべく抑えたつもりです。ここまで辿り着いた方々は自信を持ちましょう。