日本語でメール通知してほしい
こんにちは!AWS事業本部のおつまみです。
みなさん、特定のセキュリティグループが変更されたことを検知して、メール通知してほしいなぁと思ったことはありますか?私はあります。
例えば以下のようなメール通知です。
いい感じの方法がないかなぁと探していると、AWSナレッジセンターと弊社ブログにどんぴしゃな記事を見つけました。
そこで今回はこれらを組み合わせて、特定のセキュリティグループが変更されたことをいい感じの日本語メールで通知させたいと思います!
構成図
今回構築する構成です。
セキュリティグループを変更してから、ユーザーにメール通知するまでの流れ
①Amazon EventBridge:セキュリティグループの変更イベントを検知。EventBridgeの入力トランスフォーマーで、メールの件名と本文を設定。
②AWS StepFunctions:Amazon SNS publish API を呼び出し、件名と本文を指定して通知。※SNSをEventBridgeのターゲットに直接指定するとメール件名の指定ができません。
③Amazon SNS:指定したアドレス宛にメール通知。
前提として、CloudTrailが既に有効化されている環境で構築します。
CloudFormationテンプレートを準備
まずは通知元となるセキュリティグループやその他リソースののテンプレートを用意します。
sg-and-other-resources.yml
sg-and-other-resources.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: "create Security groups and other resources"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
PJPrefix:
Type: String
VPCCIDR:
Type: String
Default: "10.1.0.0/16"
PublicSubnetACIDR:
Type: String
Default: "10.1.10.0/24"
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Resources:
# ------------------------------------------------------------#
# VPC
# ------------------------------------------------------------#
# VPC Create
VPC:
Type: "AWS::EC2::VPC"
Properties:
CidrBlock: !Ref VPCCIDR
EnableDnsSupport: "true"
EnableDnsHostnames: "true"
InstanceTenancy: default
Tags:
- Key: Name
Value: !Sub "${PJPrefix}-vpc"
# InternetGateway Create
InternetGateway:
Type: "AWS::EC2::InternetGateway"
Properties:
Tags:
- Key: Name
Value: !Sub "${PJPrefix}-igw"
# IGW Attach
InternetGatewayAttachment:
Type: "AWS::EC2::VPCGatewayAttachment"
Properties:
InternetGatewayId: !Ref InternetGateway
VpcId: !Ref VPC
# ------------------------------------------------------------#
# PublicSubnet
# ------------------------------------------------------------#
PublicSubnet:
Type: AWS::EC2::Subnet
Properties:
AvailabilityZone: 'ap-northeast-1a'
VpcId: !Ref VPC
CidrBlock: !Ref PublicSubnetACIDR
Tags:
- Key: Name
Value: !Sub "${PJPrefix}-public-subnet-a"
# ------------------------------------------------------------#
# PublicRouteTable
# ------------------------------------------------------------#
PublicRouteTable:
Type: AWS::EC2::RouteTable
Properties:
VpcId: !Ref VPC
Tags:
- Key: Name
Value: !Sub "${PJPrefix}-public-route-a"
# ------------------------------------------------------------#
# SubnetとRoutetableの関連付け
# ------------------------------------------------------------#
PublicRouteTableAssociation1a:
Type: AWS::EC2::SubnetRouteTableAssociation
Properties:
RouteTableId: !Ref PublicRouteTable
SubnetId: !Ref PublicSubnet
# ------------------------------------------------------------#
# Routeの指定
# ------------------------------------------------------------#
PublicRoute:
Type: AWS::EC2::Route
Properties:
DestinationCidrBlock: 0.0.0.0/0
RouteTableId: !Ref PublicRouteTable
GatewayId: !Ref InternetGateway
# ------------------------------------------------------------#
# SecurityGroup
# ------------------------------------------------------------#
SecurityGroup:
Type: AWS::EC2::SecurityGroup
Properties:
GroupName: SecurityGroup
GroupDescription: SecurityGroup
VpcId: !Ref VPC
SGIngress01:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref SecurityGroup
IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 104.28.211.105/32
Description: MyIP
# ------------------------------------------------------------#
# EC2
# ------------------------------------------------------------#
EC2:
Type: AWS::EC2::Instance
Properties:
ImageId: ami-00d101850e971728d
KeyName: !Ref KeyName
InstanceType: t2.micro
NetworkInterfaces:
- AssociatePublicIpAddress: "true"
DeviceIndex: "0"
SubnetId: !Ref PublicSubnet
GroupSet:
- !Ref SecurityGroup
Tags:
- Key: Name
Value: "${PJPrefix}-ec2"
#------------------------------------------------------------------------------#
# Outputs
#------------------------------------------------------------------------------#
Outputs:
SecurityGroup:
Value: !Ref SecurityGroup
Export:
Name: !Sub SecurityGroup
テンプレート作成時のポイント
- 通知用のテンプレートでセキュリティグループを指定できるようにOutputセクションで値を出力しています。
続いて、今回のメインとなる通知用のテンプレートです。
sg-change-alarm.yml
sg-change-alarm.yml
AWSTemplateFormatVersion: '2010-09-09'
Description: "create Security group change notification"
# ------------------------------------------------------------#
# Input Parameters
# ------------------------------------------------------------#
Parameters:
PJPrefix:
Type: String
SNSTopicName:
Type: String
Default: SecurityGroup_Change_Alarm
Email:
Type: String
Default: email_address
Resources:
# ------------------------------------------------------------#
# SNS Topic
# ------------------------------------------------------------#
SNSTopic:
Type: "AWS::SNS::Topic"
Properties:
TopicName: !Ref SNSTopicName
Tags:
-
Key: "Name"
Value: !Sub ${PJPrefix}-${SNSTopicName}
# ------------------------------------------------------------#
# SNS Subscription
# ------------------------------------------------------------#
Subscription:
Type: "AWS::SNS::Subscription"
Properties:
TopicArn: !Ref SNSTopic
Endpoint: !Ref Email
Protocol: "email"
# ------------------------------------------------------------#
# SNS TopicPolicy
# ------------------------------------------------------------#
EventTopicPolicy:
Type: 'AWS::SNS::TopicPolicy'
Properties:
PolicyDocument:
Statement:
- Effect: Allow
Principal:
Service: events.amazonaws.com
Action: 'sns:Publish'
Resource: '*'
Topics:
- !Ref SNSTopic
# ------------------------------------------------------------#
# IAMロール (StepFunction用)
# ------------------------------------------------------------#
PublishRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- states.amazonaws.com
Action: "sts:AssumeRole"
Path: "/"
Policies:
- PolicyName: SNSPublish
PolicyDocument:
Statement:
- Effect: Allow
Resource:
- !Ref SNSTopic
Action:
- sns:Publish
StateMachineExecuteRole:
Type: "AWS::IAM::Role"
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: "Allow"
Principal:
Service:
- events.amazonaws.com
Action: "sts:AssumeRole"
Path: "/"
Policies:
- PolicyName: StateMachineExecute
PolicyDocument:
Statement:
- Effect: Allow
Resource:
- !Ref SNSStateMachine
Action:
- states:StartExecution
# ------------------------------------------------------------#
# EventBridge Rule
# ------------------------------------------------------------#
EventBridgeRule:
Type: AWS::Events::Rule
DependsOn: SNSTopic
Properties:
Description: !Ref SNSTopicName
EventBusName: default
EventPattern:
!Sub
- |-
{
"source": ["aws.ec2"],
"detail-type": ["AWS API Call via CloudTrail"],
"detail": {
"eventSource": ["ec2.amazonaws.com"],
"eventName": ["AuthorizeSecurityGroupIngress", "AuthorizeSecurityGroupEgress", "RevokeSecurityGroupIngress", "RevokeSecurityGroupEgress"],
"requestParameters": {
"groupId": ["${SecurityGroup}"]
}
}
}
- SecurityGroup: {'Fn::ImportValue': !Sub 'SecurityGroup'}
Name: !Ref SNSTopicName
Targets:
- Arn: !Ref SNSStateMachine
Id: step-function
RoleArn: !GetAtt StateMachineExecuteRole.Arn
InputTransformer:
InputPathsMap:
"Account" : "$.account"
"SecuritygroupId" : "$.detail.requestParameters.groupId"
"eventId" : "$.detail.eventID"
"time" : "$.time"
"userName" : "$.detail.userIdentity.sessionContext.sessionIssuer.userName"
"value" : "$.detail"
InputTemplate: |
{
"subject": "セキュリティグループ変更通知",
"message": "セキュリティグループ \"<SecuritygroupId>\" が変更されたことを検知しました。 \n詳細は次のとおりです。 \nAccount ID : \"<Account>\" \n発生時間 : \"<time> \"(UTC) \n変更ユーザー名 : \"<userName>\" \nセキュリティグループID : \"<SecuritygroupId>\" \nCloudTrailイベントID : \"<eventId>\" \n\nセキュリティグループの確認方法 \nEC2コンソール画面の左メニュータグより「セキュリティグループ」を選択します。\n検索ウィンドウで「セキュリティグループID」を入力し、セキュリティグループが正しいことを確認してください。 \n\nCloudTrailイベント履歴の確認方法 \nCloudTrailコンソール画面の左メニュータグより「イベント履歴」を選択します。 \n検索ウィンドウで[イベントID]を選択し、「CloudTrailイベントID」入力します。イベント詳細が確認できます。"
}
# ------------------------------------------------------------#
# StepFunction メール本文カスタマイズ用
# ------------------------------------------------------------#
SNSStateMachine:
Type: "AWS::StepFunctions::StateMachine"
Properties:
DefinitionString: !Sub |-
{
"StartAt": "PublishSns",
"States": {
"PublishSns": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:${AWS::Region}:${AWS::AccountId}:${SNSTopicName}",
"Message.$": "$.message",
"Subject.$": "$.subject"
},
"End": true
}
}
}
RoleArn: !GetAtt PublishRole.Arn
テンプレート作成時のポイント
- EventBridgeのルールの
EventPattern
で監視対象のリソースとイベント内容を指定しています。今回はセキュリティグループの変更通知であるAuthorizeSecurityGroupIngress
,AuthorizeSecurityGroupEgress
,RevokeSecurityGroupIngress
,RevokeSecurityGroupEgress
を指定しています。(114〜128行目) -
EventBridgeのルールの
InputTransformer
でメールに通知する値の取得、およびメール件名・メッセージを設定しています。InputPathsMap
ではメールに通知する値を取得するために、EventBridgeに送られてきたJSONログイベントから特定の値を変数として抽出します。セキュリティグループの変更では以下のようなJSONログイベントが送られてきます。
JSONログイベント
{
"eventVersion": "1.08",
"userIdentity": {
"type": "AssumedRole",
"principalId": "AROAVIM5J54ZWHCLMVYDN:iam-user-name",
"arn": "arn:aws:sts::123456789012:assumed-role/iam-user-name/iam-user-name",
"accountId": "123456789012",
"accessKeyId": "ASIAVIM5J54ZQ2I24HGX",
"sessionContext": {
"sessionIssuer": {
"type": "Role",
"principalId": "AROAVIM5J54ZWHCLMVYDN",
"arn": "arn:aws:iam::123456789012:role/iam-user-name",
"accountId": "123456789012",
"userName": "iam-user-name"
},
"webIdFederationData": {},
"attributes": {
"creationDate": "2023-01-25T05:33:28Z",
"mfaAuthenticated": "true"
}
}
},
"eventTime": "2023-01-25T06:21:53Z",
"eventSource": "ec2.amazonaws.com",
"eventName": "RevokeSecurityGroupIngress",
"awsRegion": "ap-northeast-1",
"sourceIPAddress": "104.28.211.105",
"userAgent": "AWS Internal",
"requestParameters": {
"groupId": "sg-0bcd6fbcb3109febe",
"ipPermissions": {},
"securityGroupRuleIds": {
"items": [
{
"securityGroupRuleId": "sgr-04d6f81151aa5f291"
}
]
}
},
"responseElements": {
"requestId": "c5cbaee6-2c04-49a3-90d4-fc49269da4d0",
"_return": true
},
"requestID": "c5cbaee6-2c04-49a3-90d4-fc49269da4d0",
"eventID": "e714461b-4431-420b-a0f0-b1cb8c068298",
"readOnly": false,
"eventType": "AwsApiCall",
"managementEvent": true,
"recipientAccountId": "123456789012",
"eventCategory": "Management",
"sessionCredentialFromConsole": "true"
}
今回はメール通知に含めたい項目として以下を入力パスに指定しました。(135〜141行目)
"Account" : "$.account"
"SecuritygroupId" : "$.detail.requestParameters.groupId"
"eventId" : "$.detail.eventID"
"time" : "$.time"
"userName" : "$.detail.userIdentity.sessionContext.sessionIssuer.userName"
"value" : "$.detail"
InputTemplate
ではメール件名およびメッセージを設定しています。Step Functionsを挟むと、テンプレートの文字列に\n
を入れることで改行ができます。※EventBridgeのターゲットにSNSを直接指定した場合は改行されません。(142〜146行目)
InputTemplate: |
{
"subject": "セキュリティグループ変更通知",
"message": "セキュリティグループ \"<SecuritygroupId>\" が変更されたことを検知しました。 \n詳細は次のとおりです。 \nAccount ID : \"<Account>\" \n発生時間 : \"<time> \"(UTC) \n変更ユーザー名 : \"<userName>\" \nセキュリティグループID : \"<SecuritygroupId>\" \nCloudTrailイベントID : \"<eventId>\" \n\nセキュリティグループの確認方法 \nEC2コンソール画面の左メニュータグより「セキュリティグループ」を選択します。\n検索ウィンドウで「セキュリティグループID」を入力し、セキュリティグループが正しいことを確認してください。 \n\nCloudTrailイベント履歴の確認方法 \nCloudTrailコンソール画面の左メニュータグより「イベント履歴」を選択します。 \n検索ウィンドウで[イベントID]を選択し、「CloudTrailイベントID」入力します。イベント詳細が確認できます。"
}
- StepFunctionsでAmazon SNS publish API を呼び出すステートマシンを設定します。
Parameters
でSNSトピック、メール件名、本文を指定します。(159~162行目)
いざ検証
1. CloudFromationでデプロイ
CloudFormationで用意したテンプレートをデプロイします。
SNSで設定したメールアドレス宛にサブスクリプションの承認依頼メールが届くため、Confirm subscription
で承認します。
2. セキュリティグループを変更
設定したセキュリティグループのインバウンドルールを編集してみます。
見事メール通知が届きました!
おまけとして、メールに記載されているCloudTrailイベントIDをコンソールで検索してみます。
ここからより詳細な情報が確認できますね。
最後に
今回は特定のセキュリティグループが変更されたことを検知して、メール通知させる方法をご紹介しました。
EventBridgeに連携できるAPIであれば、色々なサービスと連携して通知が出来そうですね!
夢が広がります。
最後までお読みいただきありがとうございました! どなたかのお役に立てれば幸いです。
以上、おつまみ(@AWS11077)でした!
参考
EC2 Linux セキュリティグループに対する変更をモニタリングする
CloudWatchアラームとSNSで日本語の件名・本文のメールを送るためのCloudFormationテンプレートを作ってみた | DevelopersIO
Amazon EventBridge event patterns - Amazon EventBridge