ネットワーク ACLでSSHやRDPなどの管理ポートへのアクセスを禁止するための設定手順

このブログシリーズ 「クラウドセキュリティ 実践集」 では、一般的なセキュリティ課題を取り上げ、「なぜ危険なのか?」 というリスクの解説から、「どうやって直すのか?」 という具体的な修復手順(コンソール、AWS CLI、Terraformなど)まで、分かりやすく解説します。
この記事では、AWS EC2のSecurity Hubコントロール「EC2-21: ネットワーク ACL は、0.0.0.0/0 からポート 22、またはポート 3389 への侵入を許可しないようにする必要があります」について解説します。

ポリシーの説明
[EC2.21] ネットワーク ACL は、0.0.0.0/0 からポート 22、またはポート 3389 への侵入を許可しないようにする必要があります
Amazon EC2 の Security Hub コントロール – AWS Security Hub
このコントロールは、ネットワークアクセスコントロールリスト (ネットワーク ACL) が、 SSH/RDP 入力トラフィックのデフォルト TCP ポートへのアクセスを無制限に許可しているかどうかをチェックします。ネットワーク ACL インバウンドエントリが TCP ポート 22 または 3389 に対して「0.0.0.0/0」または「::/0」の送信元 CIDR ブロックを許可する場合、コントロールは失敗します。コントロールは、デフォルトのネットワーク ACL の検出結果を生成しません。
このポリシーは、EC2インスタンスへの不要なアクセスを制限し、セキュリティリスクを低減するために重要です。ポート22(SSH)とポート3389(RDP)は、それぞれLinuxとWindowsインスタンスへのリモートアクセスに使用されるため、これらのポートが公開されていると、不正アクセスのリスクが高まります。
修復方法
AWSコンソールでの修正手順
- AWSマネジメントコンソールにログインします。
- VPC > セキュリティ > ネットワークACLを開きます。
- 使用しているネットワーク ACLを選択し、「アクション」のプルダウンから「インバウンドルールを編集」をクリック
- SSH接続用の22番ポート、RDP接続用の3389番ポートをallowで設定し、「変更を保存」をクリックします
- 対象インスタンスに対して接続確認し、問題なく接続できることを確認します。


Terraformでの修復手順
Network ACLの安全な設定のためのTerraformコードを作成します。
# VPCのNetwork ACL設定
resource "aws_network_acl" "secure_nacl" {
vpc_id = var.vpc_id
tags = {
Name = "${var.environment}-secure-nacl"
Environment = var.environment
}
}
# プライベートサブネット用のNetwork ACL
resource "aws_network_acl" "private_nacl" {
vpc_id = var.vpc_id
tags = {
Name = "${var.environment}-private-nacl"
Environment = var.environment
}
}
# パブリックサブネット用のNetwork ACL
resource "aws_network_acl" "public_nacl" {
vpc_id = var.vpc_id
tags = {
Name = "${var.environment}-public-nacl"
Environment = var.environment
}
}
# パブリックサブネットのインバウンドルール
resource "aws_network_acl_rule" "public_inbound" {
network_acl_id = aws_network_acl.public_nacl.id
rule_number = 100
protocol = -1
rule_action = "allow"
egress = false
cidr_block = var.vpc_cidr
from_port = 0
to_port = 0
}
# パブリックサブネットの制限付きインバウンドルール
resource "aws_network_acl_rule" "public_restricted_inbound" {
for_each = {
http = { port = 80, cidr = "0.0.0.0/0" }
https = { port = 443, cidr = "0.0.0.0/0" }
}
network_acl_id = aws_network_acl.public_nacl.id
rule_number = 200 + index(keys({ for k, v in each.value : k => v }), each.key)
protocol = "tcp"
rule_action = "allow"
egress = false
cidr_block = each.value.cidr
from_port = each.value.port
to_port = each.value.port
}
# プライベートサブネットのインバウンドルール
resource "aws_network_acl_rule" "private_inbound" {
for_each = {
ssh = { port = 22, cidr = var.management_cidr }
rdp = { port = 3389, cidr = var.management_cidr }
mysql = { port = 3306, cidr = var.app_cidr }
postgres = { port = 5432, cidr = var.app_cidr }
}
network_acl_id = aws_network_acl.private_nacl.id
rule_number = 100 + index(keys({ for k, v in each.value : k => v }), each.key)
protocol = "tcp"
rule_action = "allow"
egress = false
cidr_block = each.value.cidr
from_port = each.value.port
to_port = each.value.port
}
# アウトバウンドルール(戻り通信用)
resource "aws_network_acl_rule" "return_traffic" {
for_each = {
public = aws_network_acl.public_nacl.id
private = aws_network_acl.private_nacl.id
}
network_acl_id = each.value
rule_number = 900
protocol = -1
rule_action = "allow"
egress = true
cidr_block = "0.0.0.0/0"
from_port = 0
to_port = 0
}
# 必要な変数定義
variable "vpc_id" {
type = string
description = "VPC ID"
}
variable "vpc_cidr" {
type = string
description = "VPCのCIDRブロック"
}
variable "environment" {
type = string
description = "環境名(例: prod, staging, dev)"
}
variable "management_cidr" {
type = string
description = "管理用IPアドレス範囲"
}
variable "app_cidr" {
type = string
description = "アプリケーション層のCIDRブロック"
}
# サブネットとNACLの関連付け
resource "aws_network_acl_association" "public" {
for_each = toset(var.public_subnet_ids)
network_acl_id = aws_network_acl.public_nacl.id
subnet_id = each.value
}
resource "aws_network_acl_association" "private" {
for_each = toset(var.private_subnet_ids)
network_acl_id = aws_network_acl.private_nacl.id
subnet_id = each.value
}
# CloudWatchアラート(オプション)
resource "aws_cloudwatch_metric_alarm" "nacl_changes" {
alarm_name = "${var.environment}-nacl-changes"
comparison_operator = "GreaterThanThreshold"
evaluation_periods = "1"
metric_name = "NetworkAclChanges"
namespace = "AWS/CloudTrail"
period = "300"
statistic = "Sum"
threshold = "1"
alarm_description = "Network ACLの変更を検知しました"
alarm_actions = [var.sns_topic_arn]
dimensions = {
Region = data.aws_region.current.name
}
}
# 現在のリージョンを取得
data "aws_region" "current" {}
# 出力
output "public_nacl_id" {
value = aws_network_acl.public_nacl.id
}
output "private_nacl_id" {
value = aws_network_acl.private_nacl.id
}
重要な修正ポイントと実装手順を説明します:
- パブリックアクセスの制限(最重要):
# 管理ポートへのアクセスを特定のCIDRに制限
resource "aws_network_acl_rule" "private_inbound" {
cidr_block = var.management_cidr # 信頼できるIPレンジのみ
}
セグメント分離:
# パブリック/プライベートで異なるNACLを使用
resource "aws_network_acl" "public_nacl" {}
resource "aws_network_acl" "private_nacl" {}
最後に
今回は、ネットワークACLのインバウンドルールを修正し、ポート22と3389へのアクセスを制限する方法についてご紹介しました。これらのポートが公開されていると、不正アクセスのリスクが高まるため、適切なアクセス制御を行うことが重要です。
この問題の検出は弊社が提供するSecurifyのCSPM機能で簡単に検出及び管理する事が可能です。
運用が非常に楽に出来る製品になっていますので、ぜひ興味がある方はお問い合わせお待ちしております。
最後までお読みいただきありがとうございました。この記事が皆さんの役に立てば幸いです。