記事の目的
この記事では、セキュリティグループ(SG)のインバウンドルールを適切に管理し、全開放のルールが設定された場合にそれを自動的に削除する仕組みを検討します。
セキュリティグループの管理はクラウドインフラストラクチャのセキュリティにおいて極めて重要であり、この仕組みを導入することでセキュリティの向上と誤った設定による問題の最小化を目指します。
背景
近年、クラウド環境においてセキュリティの適切な管理がますます重要となっています。
ある日、私の配属先PJで、パブリックサブネットに配置されたプロキシ用EC2インスタンスを介して複数のシステムに接続する際、予期せぬ問題が発生しました。
システムへの接続ができなくなったため、問題の切り分けを行うために、プロキシ用EC2のセキュリティグループ(SG)のインバウンドルールを変更しました。
具体的には、全ポートからのアクセスを全ソースから許可する設定を行いました。
問題はその後解決し、システムへの接続が復旧しましたが、セキュリティグループのインバウンドルールを元の設定に戻す作業が行われませんでした。
これにより、外部からのアクセスが全開放されている状態が続き、誰かによってプロキシを経由して様々なWebページへのアクセスが行われていました。
この事例から、全開放のルールが設定されていたにも関わらず、それを検知して対応する仕組みが不足していたことが明らかになりました。
問題点
この事例から浮かび上がった問題点は、以下の通りです。
・全開放のルールが設定されたにも関わらず、それを検知して対応する仕組みが不足していた。
これらの問題に対処するため、全開放のインバウンドルールが設定されたら、それを検知し、自動的にそのルールを削除する仕組みを検討しました。
アーキテクチャ
インバウンドルールの自動削除のアーキテクチャは、以下の図に示す通りです。
各サービスの役割は以下の通りです。
AWS Security Hub
AWS Security HubはAWSのセキュリティサービスであり、クラウド環境全体のセキュリティとコンプライアンスの状態を監視し、脅威や違反事例を検出するためのツールです。
AWS Security Hubには監視用のルールが設定されており、これらのルールに違反している設定があれば、それをセキュリティ違反として検知します。
今回の仕組みでは、インバウンドルールの全開放などのセキュリティ上の問題を検出する役割を果たします。
また、Security HubはAWS Configと統合されており、Configで記録したリソース設定変更に対して、その変更がセキュリティ上問題がないかを評価します。
そのため、Security Hubを使用するには、AWS Configも有効化する必要があります。
EventBridge
EventBridgeは、AWS Security Hubがセキュリティ違反を検知した際に、事後処理を実行するトリガーとして機能します。
具体的には、インバウンドルールの全開放をAWS Security Hubが検知した場合、それをトリガーに後述のAWS Lambda関数を起動します。
AWS Lambda
全開放のインバウンドルールを削除します。
以下に、この仕組みの実装手順を記載します。
実装手順
AWS Config、Security Hub有効化
まずはAWS ConfigとSecurity Hubを有効化します。
手順は以下の記事をご参照ください。
https://qiita.com/sugimount-a/items/69726f6600591f437f3f
IAMポリシー作成
LambdaのIAMロールに付与するIAMポリシーを作成します。
Lambdaでセキュリティグループのインバウンドルールを削除するので、この操作権限をIAMポリシーで設定します。
IAMコンソールに移動し、ポリシー→ポリシーの作成をクリックします。
編集モードをJSONに変更し、以下のJSONを入力し、画面下部の「次へ」をクリックします。
{
"Version": "2012-10-17",
"Statement": [
{
"Action": "ec2:RevokeSecurityGroupIngress",
"Resource": "*",
"Effect": "Allow"
}
]
}
Lambda作成
セキュリティグループのインバウンドルールを削除するLambdaを作成します。
コンソール画面でLambdaに移動し、Lambdaの作成をクリックします。
「一から作成」を選択し、適当な名前を入力します。
ランタイムはPython 3.11を選択します。
作成後、Lambdaに付与されているIAMロールの権限設定を変更します。
設定タブ→アクセス権限に移動し、IAMロールをクリックします。
許可タブの「許可を追加」→「ポリシーをアタッチ」をクリックします。
↑で作成したポリシー名で検索し、「許可を追加」をクリックします。
IAMロールにポリシーがアタッチされていることを確認します。
次にEventBridgeの作成を行います。
(Lambdaの実装はEventBridgeの作成後に行います)
EventBridge作成
EventBridgeのコンソールに移動し、ルール→「ルールを作成」をクリックします。
ルール名を入力し、ルールタイプに「イベントパターンを持つルール」を選択します。
「次へ」をクリックします。
イベントソースに「AWS イベントまたは EventBridge パートナーイベント」を選択します。
イベントパターンに、以下のJSONを入力します。
{
"detail-type": ["Security Hub Findings - Imported"],
"source": ["aws.securityhub"],
"detail": {
"findings": {
"ProductArn": ["arn:aws:securityhub:ap-northeast-1::product/aws/securityhub"],
"Compliance": {
"Status": [{
"anything-but": "PASSED"
}]
},
"GeneratorId": ["security-control/EC2.19"],
"RecordState": ["ACTIVE"],
"Workflow": {
"Status": ["NEW"]
},
"Severity": {
"Label": ["CRITICAL"]
}
}
}
}
JSONの内容について簡単に説明します。
まず、detail-typeとsourceには以下を入力します。
これがSecurity Hubでの検知をトリガーとすることを意味します。
"detail-type": ["Security Hub Findings - Imported"],
"source": ["aws.securityhub"]
detailのfindings以下でSecurity Hubのどのセキュリティルールを対象とするかを設定します。
GeneratorIdに対象ルール名を入力します。
セキュリティグループの、ポート全開放・全ソースからの疎通を許可するインバウンドルールを検知するルール名は以下なので、これを入力します。
security-control/EC2.19
"GeneratorId": ["security-control/EC2.19"],
また、今回はルール違反が対象となるので、Complianceは以下を指定します。
(StatusがPASSED以外=Security Hubのルールに反している)
"Compliance": {
"Status": [{
"anything-but": "PASSED"
}]
},
今回のルール違反のSevirityはCRITICALになるので、Sevirity以下にCRITICALを指定します。
"Severity": {
"Label": ["CRITICAL"]
}
detali以下の最終的な形は以下となります。
"detail": {
"findings": {
"ProductArn": ["arn:aws:securityhub:ap-northeast-1::product/aws/securityhub"],
"Compliance": {
"Status": [{
"anything-but": "PASSED"
}]
},
"GeneratorId": ["security-control/EC2.19"],
"RecordState": ["ACTIVE"],
"Workflow": {
"Status": ["NEW"]
},
"Severity": {
"Label": ["CRITICAL"]
}
}
}
ターゲットタイプにAWSのサービスを選択します。
サービスにLambda関数を指定し、作成したLambda関数を選択します。
タグ設定はスキップします。
(任意のタグを入れたい場合はここで入力してください)
設定内容を確認し、EventBridgeルールの作成を実行します。
Lambda実装
Lambdaの画面に戻り、インバウンドルール内の全開放ルールを削除するコードを実装します。
以下のソースコードをindex.pyに貼り付け、Lambdaをデプロイします。
mport boto3
import json
from botocore.exceptions import ClientError
import logging
ec2 = boto3.client("ec2")
logger = logging.getLogger()
level = logging.INFO
logger.setLevel(level)
def handler(event, context):
logger.info("Start processing of this function.")
logger.info(f"event: {event}")
resources = event["detail"]["findings"][0]["Resources"]
#セキュリティグループIDを取得
sgId = resources[0]["Details"]["AwsEc2SecurityGroup"]["GroupId"]
#セキュリティグループの削除を実行
try:
response = ec2.revoke_security_group_ingress(
CidrIp = '0.0.0.0/0',
GroupId = sgId,
FromPort = 0,
ToPort = 65535,
IpProtocol = 'tcp'
)
logger.info("Program completed successfully.")
except ClientError as e:
logger.error(e.response["Error"])
動作確認
試しにポート全開放、全ソースからのアクセスを許可するインバウンドルールをセキュリティグループに追加します。
※注意
今回は動作確認としてインバウンドルールに全開放のルールを追加しますが、当然セキュリティ上やったらだめです。
絶対にこのセキュリティグループをEC2にアタッチしないでください。
EventBridgeが動き、後続のLambdaが実行されると、CloudWatch Logsでログが記録されます。
まとめ
AWSに限った話ではないですが、セキュリティを疎かにすると、やがて攻撃を食らって、損害を被ることになります。
また、今回のケースでは、インターネットのどこからもアクセスできる状態になるので、不正アクセスの踏み台にされました。
この場合、不正アクセス元は自分たちのサーバーになるので、仮にそれが原因で他社が損害を被れば、会社の信用を失うことにもなりかねません。
人間が注意を払うだけでは限界があり、システムでカバーする仕組みを作るのが1つのアプローチと私は考えます。
今回考えた仕組みが1つの参考になれば幸いです。