Traffic Mirroring のセッション作成を cfn 化
やりたいこと
Traffic Mirroring を展開して、ミラーリングセッションを張るまでを自動化する
いきなり結論
以下の CloudFormation テンプレートでスタックを作成する
ミラーリングソース
EC2インスタンスのタグに "Mirroring" キーが設定されていて、値が"true"となっているインスタンスへのすべてのトラフィック
ミラーリングターゲット
ターゲットにする NLB の ARN を展開時に指定
CloudFormation テンプレート
AWSTemplateFormatVersion: 2010-09-09
Description: Create Traffic Mirror
Parameters:
NLBArn:
Description: Enter the NLB ARN
Type: String
Resources:
TrafficMirrorFilter:
Type: AWS::EC2::TrafficMirrorFilter
Properties:
Description: traffic mirror filter
NetworkServices:
- amazon-dns
Tags:
- Key: Name
Value: TrafficMirroringFilter
InboundTrafficMirrorFilterRule:
Type: AWS::EC2::TrafficMirrorFilterRule
Properties:
Description: mirror all inbound traffic
TrafficMirrorFilterId: !Ref TrafficMirrorFilter
TrafficDirection: ingress
RuleNumber: 10
DestinationCidrBlock: 0.0.0.0/0
SourceCidrBlock: 0.0.0.0/0
RuleAction: accept
OutboundTrafficMirrorFilterRule:
Type: AWS::EC2::TrafficMirrorFilterRule
Properties:
Description: mirror all outbound traffic
TrafficMirrorFilterId: !Ref TrafficMirrorFilter
TrafficDirection: egress
RuleNumber: 10
DestinationCidrBlock: 0.0.0.0/0
SourceCidrBlock: 0.0.0.0/0
RuleAction: accept
NLBTrafficMirrorTarget:
Type: AWS::EC2::TrafficMirrorTarget
Properties:
Description: traffic mirror target associated with a network load balancer
NetworkLoadBalancerArn: !Ref NLBArn
Tags:
- Key: Name
Value: NLBTarget
# Creating Mirroring Sessions
CustomLambdaCreateMirroringSession:
Type: Custom::CreateMirroringSessionLambda
Properties:
ServiceToken: !GetAtt LambdaFunctionCreateMirroringSession.Arn
LambdaFunctionCreateMirroringSession:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Environment:
Variables:
TARGET_ID: !Ref NLBTrafficMirrorTarget
FILTER_ID: !Ref TrafficMirrorFilter
Role: !GetAtt LambdaFunctionExecutionCreateMirroringSessionRole.Arn
Runtime: python3.9
Timeout: 10
Code:
ZipFile: !Sub |
import boto3
import base64
import os
import cfnresponse
client = boto3.client('ec2')
target_id = os.getenv('TARGET_ID', '')
filter_id = os.getenv('FILTER_ID', '')
def lambda_handler(event, context):
tag_key = "Mirroring"
tag_value = "true"
try:
if event['RequestType'] == 'Create':
response = client.describe_instances(
Filters=[
{
'Name': 'tag:' + tag_key,
'Values': [
tag_value,
]
}
]
)
eni_ids = []
for resp in response['Reservations']:
for instance in resp['Instances']:
for eni in instance['NetworkInterfaces']:
eni_ids.append(eni['NetworkInterfaceId'])
print(eni_ids)
session_num = 1
for eni_id in eni_ids:
response = client.create_traffic_mirror_session(
NetworkInterfaceId=eni_id,
TrafficMirrorTargetId=target_id,
TrafficMirrorFilterId=filter_id,
SessionNumber=session_num
)
session_num += 1
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {})
print(e)
try:
if event['RequestType'] == 'Delete':
response = client.describe_traffic_mirror_sessions(
Filters=[
{
'Name': 'traffic-mirror-target-id',
'Values': [
target_id,
]
},
],
)
mirroring_session_ids = [ mirroring_session['TrafficMirrorSessionId'] for mirroring_session in response['TrafficMirrorSessions']]
for session_id in mirroring_session_ids:
response = client.delete_traffic_mirror_session(
TrafficMirrorSessionId=session_id
)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {})
print(e)
LambdaFunctionExecutionCreateMirroringSessionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: "mirroring-lambda-policy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:CreateTrafficMirrorSession
- ec2:DeleteTrafficMirrorSession
- ec2:DescribeTrafficMirrorSessions
- ec2:DescribeInstances
Resource:
- "*"
そもそもトラフィックミラーリング #とは
今日、AWS では VPC トラフィックミラーリングの利用を開始します。これは既存の Virtual Private Clouds (VPC) を使用する新機能で、ネットワークトラフィックを捕捉し、検査します。また、この機能はスケール可能です。次のことを実行できます。
◆ネットワークおよびセキュリティ上の異常を検出 – VPC 上の任意のワークロードから関心のあるトラフィックを抽出し、指定した検出ツールにルーティングできます。従来のログベースのツールと比較して、より迅速に攻撃を検出し、対応できます。
◆運用上のインサイトを取得 – VPC トラフィックミラーリングを使用することで、ネットワークを可視化し、コントロールを得ることができます。それは後に、より詳細な情報を得たうえでセキュリティの意思決定を下すのに役立ちます。
◆コンプライアンスとセキュリティコントロールを実装 – モニタリング、ログ作成、その他を必要とする法令およびコンプライアンスの要件に準拠できるようになります。
◆問題のトラブルシューティング – テストやトラブルシューティングの目的で、アプリケーションのトラフィックを社内的にミラーリングできます。トラフィックパターンを分析し、事前にアプリケーションのパフォーマンスを損なう「渋滞」ポイントを見つけることができます。
シンプルに書くと ENI を指定してその ENI で行われる通信をフィルタリングして宛先の ENI/NLB/GWLB に送信するサービス
※2022/05/12 から 送信先の GWLB が対応
利用用途としては IDS に対してミラーリングトラフィックを送信することでトラフィックの検査をしたり、本番環境の通信のチェック、テストを行うことが考えられます。
参考ブログ
以下のんピさんのブログは検証まで丁寧に記載していただけてるのでとても参考になります~(素敵)
CloudFormation で作成されるものの詳細
作成されるものリスト
以下5つのコンポーネントが作成されます。
- トラフィックミラーフィルタ
- インバウンドトラフィックミラーフィルタールール
- アウトバウンドトラフィックミラーフィルタールール
- トラフィックミラーターゲット
- トラフィックミラーセッション(カスタムリソース)
トラフィックミラーフィルタ + In/Outルール
トラフィックミラーフィルタを作成します。
→これはミラーリングセッションを張った時にどのトラフィックをミラーリングするかの指定を行うものです。
この時すべてのインバウンド/アウトバウンドですべてのトラフィックをミラーリング対象とするように設定します。
※プロトコルを明示的に指定しない場合、CloudFormation では"すべてのプロトコル"が対象になります。(Security Groupだと-1指定でこれとは違うのがまた、、←)
TrafficMirrorFilter:
Type: AWS::EC2::TrafficMirrorFilter
Properties:
Description: traffic mirror filter
NetworkServices:
- amazon-dns
Tags:
- Key: Name
Value: TrafficMirroringFilter
InboundTrafficMirrorFilterRule:
Type: AWS::EC2::TrafficMirrorFilterRule
Properties:
Description: mirror all inbound traffic
TrafficMirrorFilterId: !Ref TrafficMirrorFilter
TrafficDirection: ingress
RuleNumber: 10
DestinationCidrBlock: 0.0.0.0/0
SourceCidrBlock: 0.0.0.0/0
RuleAction: accept
OutboundTrafficMirrorFilterRule:
Type: AWS::EC2::TrafficMirrorFilterRule
Properties:
Description: mirror all outbound traffic
TrafficMirrorFilterId: !Ref TrafficMirrorFilter
TrafficDirection: egress
RuleNumber: 10
DestinationCidrBlock: 0.0.0.0/0
SourceCidrBlock: 0.0.0.0/0
RuleAction: accept
トラフィックミラーターゲット
トラフィックミラーターゲットを作成します。
→ミラーリングしたトラフィックを送るあて先を指定して作成します。
今回は NLB を指定しています
NLBTrafficMirrorTarget:
Type: AWS::EC2::TrafficMirrorTarget
Properties:
Description: traffic mirror target associated with a network load balancer
NetworkLoadBalancerArn: !Ref NLBArn
Tags:
- Key: Name
Value: NLBTarget
トラフィックミラーセッション(カスタムリソース)
実際にミラーリングを行うセッションを作成します。
この時通常であれば CloudFormation のタイプで "AWS::EC2::TrafficMirrorSession" を指定して作成しますが今回はカスタムリソースで作成しています。
今回は手動で一つ一つ送信元 ENI を指定するのが手間だったため、タグベースでミラーリング対象の ENI を特定しすべてに一括でミラーリングセッションを作成したかったためカスタムリソースでの作成にしています。
注意点としてはミラーリングセッションはセッションIDを一意で持たなければいけない点、削除時に既存のミラーリングセッションを削除しないとほかのミラーフィルタ系が削除できない点です。
そのため、カスタムリソースではリクエストタイプにて上記を解決しています。
LambdaFunctionCreateMirroringSession:
Type: AWS::Lambda::Function
Properties:
Handler: index.lambda_handler
Environment:
Variables:
TARGET_ID: !Ref NLBTrafficMirrorTarget
FILTER_ID: !Ref TrafficMirrorFilter
Role: !GetAtt LambdaFunctionExecutionCreateMirroringSessionRole.Arn
Runtime: python3.9
Timeout: 10
Code:
ZipFile: !Sub |
import boto3
import base64
import os
import cfnresponse
client = boto3.client('ec2')
target_id = os.getenv('TARGET_ID', '')
filter_id = os.getenv('FILTER_ID', '')
def lambda_handler(event, context):
tag_key = "Mirroring"
tag_value = "true"
try:
if event['RequestType'] == 'Create':
response = client.describe_instances(
Filters=[
{
'Name': 'tag:' + tag_key,
'Values': [
tag_value,
]
}
]
)
eni_ids = []
for resp in response['Reservations']:
for instance in resp['Instances']:
for eni in instance['NetworkInterfaces']:
eni_ids.append(eni['NetworkInterfaceId'])
print(eni_ids)
session_num = 1
for eni_id in eni_ids:
response = client.create_traffic_mirror_session(
NetworkInterfaceId=eni_id,
TrafficMirrorTargetId=target_id,
TrafficMirrorFilterId=filter_id,
SessionNumber=session_num
)
session_num += 1
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {})
print(e)
try:
if event['RequestType'] == 'Delete':
response = client.describe_traffic_mirror_sessions(
Filters=[
{
'Name': 'traffic-mirror-target-id',
'Values': [
target_id,
]
},
],
)
mirroring_session_ids = [ mirroring_session['TrafficMirrorSessionId'] for mirroring_session in response['TrafficMirrorSessions']]
for session_id in mirroring_session_ids:
response = client.delete_traffic_mirror_session(
TrafficMirrorSessionId=session_id
)
cfnresponse.send(event, context, cfnresponse.SUCCESS, {})
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {})
print(e)
LambdaFunctionExecutionCreateMirroringSessionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Policies:
- PolicyName: "mirroring-lambda-policy"
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- ec2:CreateTags
- ec2:CreateTrafficMirrorSession
- ec2:DeleteTrafficMirrorSession
- ec2:DescribeTrafficMirrorSessions
- ec2:DescribeInstances
Resource:
- "*"
最後に
Traffic Mirroring を展開して、EC2インスタンスのタグに "Mirroring" キーが設定されていて、値が"true"となっているインスタンスへのすべてのトラフィックのミラーリングセッションを張るまでを自動化するテンプレートを作成してみました。
どなたかの参考になれば幸いです~
Discussion