セキュリティグループでIPやポート制限を実施するための手順

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。

この記事では、EC2インスタンスのセキュリティグループ設定に関するセキュリティポリシーについて解説します。

ポリシーの説明

Amazon EC2 の Security Hub コントロール – AWS Security Hub

このコントロールは、Amazon EC2 セキュリティグループが、許可されていないポートからの無制限の着信トラフィックを許可しているかどうかをチェックします。コントロールのステータスは次のように決定されます。

  • authorizedTcpPorts のデフォルト値を使用する場合、セキュリティグループがポート 80 およびポート 443 以外のポートからの無制限の着信トラフィックを許可すると、コントロールは失敗します。
  • authorizedTcpPorts または authorizedUdpPorts にカスタム値を指定した場合、セキュリティグループがリストにないポートからの無制限の着信トラフィックを許可すると、コントロールは失敗します。
  • パラメータを使用しない場合、無制限のインバウンドトラフィックルールを持つセキュリティグループに対してコントロールが失敗します。

このポリシーは、EC2インスタンスのセキュリティグループにおいて、不要なポートへの無制限な着信トラフィックを禁止することを求めています。無制限な着信トラフィックを許可すると、不正アクセスや攻撃(ブルートフォース攻撃、脆弱性スキャンなど)のリスクが高まります。必要なポートに必要な送信元(特定のIPアドレス、他のセキュリティグループなど)からのみアクセスを許可するように制限し、セキュリティを強化する必要があります。

修復方法

AWSコンソールでの修正手順

  1. EC2 > セキュリティグループ へ移動し、使用しているセキュリティグループを選択
  2. 「インバウンドのルールを編集」をクリック

3. ソースが 0.0.0.0/0 または::/0になっているルールを確認します。そのポートへのアクセスが本当に全てのIPアドレスから必要か検討します。

  1. 不要な場合は、ソースを「マイIP」(現在の接続元IP)、特定のIPアドレス範囲(例: 192.0.2.0/24)、または通信を許可したい別のセキュリティグループIDなどに変更します。あるいは、ルール自体が不要であれば削除します。
  2. 設定完了後、「ルールを保存」をクリックし設定を反映します。

Terraformでの修復手順

セキュリティグループの安全な設定のためのTerraformコードを作成します。

# Webサーバー用セキュリティグループ
resource "aws_security_group" "web" {
  name        = "${var.environment}-web-sg"
  description = "Security group for web servers"
  vpc_id      = var.vpc_id

  # HTTPSアクセス(443)
  ingress {
    description = "HTTPS from trusted sources"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = var.allowed_https_cidrs
  }

  # HTTPアクセス(80)
  ingress {
    description = "HTTP from trusted sources"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = var.allowed_http_cidrs
  }

  # アウトバウンドルール
  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = merge(
    {
      Name        = "${var.environment}-web-sg"
      Environment = var.environment
    },
    var.additional_tags
  )
}

# アプリケーションサーバー用セキュリティグループ
resource "aws_security_group" "app" {
  name        = "${var.environment}-app-sg"
  description = "Security group for application servers"
  vpc_id      = var.vpc_id

  # Webサーバーからのアクセスのみを許可
  ingress {
    description     = "Access from web servers"
    from_port       = var.app_port
    to_port         = var.app_port
    protocol        = "tcp"
    security_groups = [aws_security_group.web.id]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = merge(
    {
      Name        = "${var.environment}-app-sg"
      Environment = var.environment
    },
    var.additional_tags
  )
}

# データベース用セキュリティグループ
resource "aws_security_group" "db" {
  name        = "${var.environment}-db-sg"
  description = "Security group for databases"
  vpc_id      = var.vpc_id

  # アプリケーションサーバーからのアクセスのみを許可
  ingress {
    description     = "Access from application servers"
    from_port       = var.db_port
    to_port         = var.db_port
    protocol        = "tcp"
    security_groups = [aws_security_group.app.id]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = merge(
    {
      Name        = "${var.environment}-db-sg"
      Environment = var.environment
    },
    var.additional_tags
  )
}

# 管理用セキュリティグループ(バスティオンホスト等)
resource "aws_security_group" "management" {
  name        = "${var.environment}-management-sg"
  description = "Security group for management access"
  vpc_id      = var.vpc_id

  # SSH接続(特定のIPのみ)
  ingress {
    description = "SSH from trusted IPs"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = var.allowed_management_cidrs
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  tags = merge(
    {
      Name        = "${var.environment}-management-sg"
      Environment = var.environment
    },
    var.additional_tags
  )
}

# 変数定義
variable "environment" {
  type        = string
  description = "Environment name (e.g., prod, staging, dev)"
}

variable "vpc_id" {
  type        = string
  description = "VPC ID where security groups will be created"
}

variable "allowed_http_cidrs" {
  type        = list(string)
  description = "List of CIDR blocks allowed for HTTP access"
  default     = ["0.0.0.0/0"]  # 本番環境では制限することを推奨
}

variable "allowed_https_cidrs" {
  type        = list(string)
  description = "List of CIDR blocks allowed for HTTPS access"
  default     = ["0.0.0.0/0"]  # 本番環境では制限することを推奨
}

variable "allowed_management_cidrs" {
  type        = list(string)
  description = "List of CIDR blocks allowed for management access"
}

variable "app_port" {
  type        = number
  description = "Port for application servers"
  default     = 8080
}

variable "db_port" {
  type        = number
  description = "Port for database access"
  default     = 3306
}

variable "additional_tags" {
  type        = map(string)
  description = "Additional tags for all security groups"
  default     = {}
}

# 出力
output "web_security_group_id" {
  value = aws_security_group.web.id
}

output "app_security_group_id" {
  value = aws_security_group.app.id
}

output "db_security_group_id" {
  value = aws_security_group.db.id
}

output "management_security_group_id" {
  value = aws_security_group.management.id
}

重要な修正ポイントと実装手順を説明します:

  1. パブリックアクセスの制限(最重要):
# HTTPSアクセスの制限
ingress {
  description = "HTTPS from trusted sources"
  from_port   = 443
  to_port     = 443
  protocol    = "tcp"
  cidr_blocks = var.allowed_https_cidrs  # 信頼できるIP範囲のみ (例: ["198.51.100.0/24"]) や、CloudFrontのPrefix Listなどを指定
}

ingress ルールの cidr_blockssecurity_groups 属性を使用して、アクセス元を限定することが最も重要です。0.0.0.0/0::/0 の使用は必要最小限(通常はWebサーバーのHTTP/HTTPSポートやロードバランサーなど)に留めます。変数 (var.allowed_https_cidrs など) を使用して、許可するCIDRを外部から指定できるようにすると管理しやすくなります。

 2. レイヤー別のセキュリティ分離:

# 各層で異なるセキュリティグループを使用し、参照でアクセスを許可
resource "aws_security_group" "web" {}
resource "aws_security_group" "app" {
  ingress {
    security_groups = [aws_security_group.web.id] # Web SGからのアクセスのみ許可
  }
}
resource "aws_security_group" "db" {
   ingress {
    security_groups = [aws_security_group.app.id] # App SGからのアクセスのみ許可
  }
}
  • Web、App、DBのように層ごとにセキュリティグループを分け、セキュリティグループIDを参照して層間の通信を許可します (security_groups 属性)。これにより、IPアドレスの変更に影響されず、かつ不要なサーバーからのアクセスを防げます。

適用手順:

  1. 変数の設定:
# terraform.tfvars
environment              = "production"
vpc_id                   = "vpc-xxxxx"
allowed_http_cidrs       = ["0.0.0.0/0"] # またはCloudFrontのPrefix Listなど
allowed_https_cidrs      = ["0.0.0.0/0"] # またはCloudFrontのPrefix Listなど
allowed_management_cidrs = ["203.0.113.0/24"] # VPNやオフィスのIP範囲
app_port                 = 8080
db_port                  = 3306
# additional_tags        = { Owner = "team-a" }

2.Terraformの実行:

terraform init
terraform plan
terraform apply

セキュリティベストプラクティス:

  • アクセス制御: 必要最小限のポートのみを開放、信頼できるIP範囲やセキュリティグループのみにアクセスを制限、セキュリティグループの連鎖(参照)を活用。
  • セグメンテーション: レイヤー別のセキュリティグループ、適切なネットワーク分離(サブネット設計と組み合わせる)、最小権限の原則。
  • 管理アクセス: VPNまたはSystems Manager Session Manager、EC2 Instance Connect Endpointなどの利用を検討、管理ポート(SSH/RDP)へのアクセス元IPを厳しく制限。
  • ロギング: VPC Flow Logsの有効化による通信の可視化。

注意点:

  • 既存環境への適用: 現在の通信要件を正確に把握し、影響を評価した上で段階的に適用します。アプリケーションの依存関係を確認してください。
  • 運用上の考慮事項: メンテナンス時のアクセス(一時的なルール追加・削除など)、緊急時の対応手順、変更管理プロセスを定めます。
  • モニタリング設定: AWS Configなどによるセキュリティグループの変更監視、VPC Flow Logsの分析、GuardDutyなどによる異常検知を設定します。

最後に

今回は、EC2インスタンスのセキュリティグループ設定について解説しました。不要なポートへの無制限なアクセスを許可すると、不正アクセスや攻撃のリスクが高まります。必要なポートのみに必要な送信元からのアクセスを許可するように制限し、セキュリティを強化してください。

この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。

運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。

最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです

この記事をシェアする

クラウドセキュリティ対策実践集一覧へ戻る

貴社の利用状況に合わせた見積もりを作成します。

料金プランを詳しく見る