AWS公式ドキュメントでは、セキュリティグループを以下のように定義しています。
セキュリティグループは、関連付けられたリソースに到達することを許可されるトラフィックと、リソースから離れることを許可されるトラフィックを制御します。
つまり、EC2インスタンスなどのリソースに対して「誰からの通信を受け入れるか」「どこへの通信を許可するか」を制御する仮想ファイアウォールです。
主な特徴は以下のとおりです。
- 許可ルールのみ設定可能
- ステートフル(トラフィックの状態を追跡して各接続のセッション情報を保持する)
- 一度許可した接続に対する応答トラフィックは自動的に許可される
- インスタンス単位で適用される
- 1つのインスタンスに複数のセキュリティグループを割り当て可能
公式ドキュメントではベストプラクティスとして以下が挙げられています。
– SSH(22番ポート)やRDP(3389番ポート)のインバウンドルールには特定のIPアドレス範囲のみを許可すること。0.0.0.0/0(IPv4)や::/0(IPv6)を指定した場合、誰でもアクセス可能になってしまう
– 大きなポート範囲を開放しないこと
– 必要最小限のセキュリティグループを作成し、エラーのリスクを低減すること
セキュリティグループと混同されやすいのがネットワークACL(NACL)です。簡単に整理しておきます。
【セキュリティグループ】
・適用単位:インスタンス(ENI)
・ステートフル(戻りのトラフィックは自動許可)
・許可ルールのみ
【ネットワークACL】
・適用単位:サブネット
・ステートレス(戻りのトラフィックも明示的に許可が必要)
・許可ルール+拒否ルール
基本的にはセキュリティグループで通信制御を行い、サブネットレベルの追加防御が必要な場合にネットワークACLを併用する、という使い分けが一般的です。
本コラムではセキュリティグループの設計に絞ってご説明します。
最も危険で、最もよく見かけるパターンです。
構築時に「とりあえず接続できるようにしよう」とインバウンドルールでSSH(22番ポート)やRDP(3389番ポート)を 0.0.0.0/0(全世界)に開放してしまうケースです。
これは玄関のドアを開けっ放しにしているのと同じ状態です。
インターネット上のどこからでもログイン試行ができてしまうため、ブルートフォース攻撃(総当たり攻撃)の標的になります。
AWS公式のベストプラクティスでも「0.0.0.0/0を指定した場合、指定されたプロトコルを使用して任意のIPアドレスからインスタンスにアクセスできるようになる」と明確に警告されています。
※AWS Security Hubでもこの設定は重要度「High」の検出項目(EC2.18、EC2.19)として自動検知されます。
「管理が面倒だから」という理由で、Web用もDB用も管理用も、すべてのルールを1つのセキュリティグループにまとめてしまうパターンです。
AWS公式ベストプラクティスでは次のようなことが推奨されています。
必要最小限のセキュリティグループを作成し、エラーのリスクを低減する。
各セキュリティグループは、類似の機能とセキュリティ要件を持つリソースへのアクセスを管理するために使用する。
1つに詰め込む設計の問題点は以下です。
・どのルールがどの用途なのか分からなくなる
・不要なポートが意図せず開放されたままになる
・インスタンスの役割が変わったときにルールの整理ができない
例えば、Webサーバー用に80/443を開放したセキュリティグループをデータベースサーバーにも流用すると、DBサーバーに対しても直接HTTPアクセスが可能な状態になってしまいます。
運用が長くなると、以下のような状態になりがちです。
- 検証時に追加したルールが本番環境に残っている
- 退職したメンバーのIPアドレスが許可されたまま
- 使われなくなったインスタンスのセキュリティグループが残存している
- ルールの説明(Description)が空白で、何のために追加したか誰にも分からない
セキュリティグループは作成・追加は簡単ですが、削除の判断が難しいため、どんどん肥大化していく傾向があります。
ルールには説明を追加できますので追加時に必ず目的を記載する運用を徹底しましょう。
VPCを作成すると自動的に「default」というセキュリティグループが作成されます。
AWS公式ドキュメントでは明確に以下のように推奨しています。
デフォルトのセキュリティグループを使用するのではなく、特定のリソースまたはリソースグループ用にセキュリティグループを作成することをお勧めします。
デフォルトセキュリティグループの初期ルールは以下です。
【インバウンド】
・ソース:自分自身(同じSGのID)
・プロトコル:すべて
・ポート:すべて
→ 同じデフォルトSGに属するリソース間の全通信が許可される
【アウトバウンド】
・宛先:0.0.0.0/0
・プロトコル:すべて
・ポート:すべて
→ すべての送信トラフィックが許可される
「とりあえずdefaultを使う」運用では、デフォルトSGに紐づけたインスタンス同士が意図せず全ポートで通信可能になってしまいます。
なお、デフォルトセキュリティグループは削除できません。ルールを空にして使用しない運用が推奨です。
セキュリティグループは「役割」単位で作成するのが基本です。
Webサーバー、データベースサーバー、ロードバランサーなど用途別のセキュリティグループルールの設定例が紹介されています。
ALBを使った一般的なWebシステムであれば以下のように分けます。
・sg-alb:インターネットからHTTP/HTTPS(80/443)を受け付ける
・sg-web:ALB(sg-alb)からのHTTP(80)のみ受け付ける
・sg-db:EC2(sg-web)からのDB接続ポート(3306など)のみ受け付ける
このように分離することで、各リソースに必要最小限のルールだけが適用されます。
なお、運用管理のためのSSH/RDP接続にはSession Manager(パターン③で後述)を使うことで、管理用セキュリティグループ自体を不要にできます。
セキュリティグループの強力な機能が「ソースにセキュリティグループIDを指定する」ことです。
公式ドキュメントでは「Security group referencing」と呼ばれています。
公式の説明を引用します。
セキュリティグループをルールのソースまたは宛先として指定すると、そのルールはそのセキュリティグループに関連付けられたすべてのインスタンスに影響します。インスタンスは、指定された方向に、インスタンスのプライベートIPアドレスを使用して通信できます。
例えば、DBサーバーのインバウンドルールを以下のように設定します。
・タイプ:MySQL/Aurora
・ポート:3306
こうすることで「sg-webが割り当てられたインスタンスからのみDB接続を許可する」というルールになります。
この設計のメリットは以下です。
- IPアドレスの変更に影響されない
- Auto Scalingでインスタンスが増減しても対応不要
- 通信経路が明確になる
- ALBはEC2にだけ、EC2はRDSにだけアクセス可能
- AWSが推奨する三層アーキテクチャのベストプラクティスに沿っている
SSH(22番)やRDP(3389番)のポートを開放しないための解決策が、AWS Systems ManagerのSession Managerです。
Session Managerを使えば、インスタンス上のSSM Agent経由で接続できるため、セキュリティグループでSSH/RDPポートを開放する必要がありません。
導入のメリットは以下です。
- セキュリティグループからSSH/RDPルールを削除できる
- 踏み台サーバー(Bastion Host)が不要になる
- 接続ログがCloudTrailに記録される(監査対応)
- IAMポリシーで接続権限を管理できる
公式ベストプラクティスの「SSH/RDPには特定IPのみ許可」を超えて、そもそもポートを開けないという最も安全なアプローチです。
複数のセキュリティグループで共通する接続元(社内IPなど)がある場合は「マネージドプレフィックスリスト」の活用をおすすめします。
プレフィックスリストとは、CIDRブロック(IPアドレス範囲)のセットに名前を付けて一元管理できる機能です。
プレフィックスリストには「カスタマーマネージドプレフィックスリスト」と「AWSマネージドプレフィックスリスト」の2種類があります。
【カスタマーマネージドプレフィックスリスト】
自分で作成・管理するプレフィックスリスト。
例えば「社内拠点のIP一覧」をプレフィックスリストとして作成しておけば、
各セキュリティグループのソース欄にプレフィックスリストIDを指定するだけで済みます。
拠点のIPアドレスが変わった場合も、プレフィックスリストを1箇所更新するだけで、参照しているすべてのセキュリティグループに自動的に反映されます。
【AWSマネージドプレフィックスリスト】
AWSが提供・管理するプレフィックスリスト。
IPアドレスが動的なAWSサービスで提供されており、管理はAWSが行います。
AWSマネージドプレフィックスリストは、AWSサービスのIPアドレス範囲のセットです。
これらのプレフィックスリストはAWSによって管理され、さまざまなAWSサービスで使用されるIPアドレスを参照する方法を提供します。
主なAWSマネージドプレフィックスリストは以下です。
・com.amazonaws.global.cloudfront.origin-facing:Amazon CloudFrontのIPアドレス範囲(重み:55)
・com.amazonaws.<リージョン>.s3:Amazon S3のIPアドレス範囲(重み:1)
・com.amazonaws.<リージョン>.dynamodb:Amazon DynamoDBのIPアドレス範囲(重み:1)
・com.amazonaws.<リージョン>.ec2-instance-connect:EC2 Instance ConnectのIPアドレス範囲(重み:2)
例えばALBのセキュリティグループでソースにCloudFrontのAWSマネージドプレフィックスリストを指定すれば、「CloudFront経由のアクセスのみ許可する」設定が簡単に実現できます。CloudFrontのIPアドレスはAWSによって自動更新されるため、手動でのメンテナンスは不要です。
なお注意点として、プレフィックスリストを参照するルールは「プレフィックスリストの最大サイズ」分のルール数としてカウントされます。例えばプレフィックスリストの最大エントリ数が20の場合、そのルール1つで20ルール分を消費します。セキュリティグループあたりのルール上限(デフォルト60)に注意してください。
以下の項目を確認してみてください。現状把握だけでもセキュリティ改善の第一歩になります。
□ 0.0.0.0/0 でSSH(22)/ RDP(3389)が開放されていないか
□ 使われていないセキュリティグループが残っていないか
□ デフォルトセキュリティグループ(default)にインスタンスが紐づいていないか
□ 1つのセキュリティグループに10個以上のルールが詰め込まれていないか
□ 説明欄(Description)が空白のルールがないか
□ アウトバウンドが全開放(0.0.0.0/0 全ポート)のまま放置されていないか
確認方法としては、EC2コンソールの左メニュー「ネットワーク&セキュリティ」配下の「セキュリティグループ」から一覧を確認できます。
また、AWS Security Hubを有効にしている場合は上記の問題を自動的に検出してくれます。
セキュリティグループの設計について、よくある失敗パターンと正しい設計パターンをご紹介しました。
ポイントを5つまとめます。
- 0.0.0.0/0 でのSSH/RDP開放は絶対に避ける
- 役割ごとにセキュリティグループを分離する
- ソースにセキュリティグループIDを指定して階層化する
- Session Managerを活用してSSH/RDPポート自体を不要にする
- 定期的な棚卸しで不要ルールを削除する
「とりあえず動く」設定から「正しく守る」設計に見直すことで、セキュリティインシデントのリスクを大幅に低減できます。
まずはチェックリストで自社環境の現状を確認するところから始めてみてください。
今回は以上です。