はじめに
前回コンソールから作成した、S3の不正アクセスを検出する環境を、CloudFormationで記述しました。
構成
複数の環境を想定して、「共通で使うリソース」と「個別の環境で使うリソース」で分けて作成しました。
「共通で使うリソース」のCloudTrailは、監視対象バケットが増えるたびに設定(監視するバケット)を変更する想定です。
- 共通で使うリソース
- CloudTrail
- 監視対象が増えるたびに設定変更します。
- 初回作成時は監視対象バケットが無いので、作成しないようにします。
- 監視対象が増えるたびに設定変更します。
- 証跡ログバケット
- S3アクセスログ用Log Group
- SNS
- CloudTrail
- 個別の環境で使うリソース
- 監視対象バケット
- メトリクスとアラート
- ロググループは一つなので、監視対象バケットの名前などをフィルターに入れて分別しています。
ClooudFormation
コードは以下になります、説明等に使われている英語の拙さはご容赦ください。
共通で使うリソース
01_createBaseEnvForTrail.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Create Base Env for Trail on S3. And Add target Bucket for trail.
Parameters:
TrailBuckets:
Description: Set S3Bucket Arn(arn:aws:s3:::xxxxxxxxxxx/) for trail. delimiter is connma(,). If bukcet don't exists, set empty (Don't Create CloudTrail) .
Type: CommaDelimitedList
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: User Setting.
Parameters:
- TrailBuckets
# パラメータのリストを連結させて空文字なら、CloudTrailを作らない
Conditions:
CreateTrail:
!Not [!Equals [ !Join [ '', !Ref TrailBuckets ] , '']]
Resources:
##################################################
# SNS
##################################################
IllegalTraficSnsTopic:
Type: AWS::SNS::Topic
Properties:
TopicName: illegal-trafic-sns-topic
Tags:
- Key: "usefor"
Value: "send e-mail detection of illegal trafic"
##################################################
# Cloudwatch log Group
##################################################
S3AccesslogLogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: /custom/s3accesslog
RetentionInDays: 3653 # 未指定時は「失効しない」
##################################################
# Bucket for CloudTrail
##################################################
OutCloudTrailBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "out-cloudtrail-s3accesslogs-${AWS::AccountId}"
BucketEncryption:
ServerSideEncryptionConfiguration:
-
ServerSideEncryptionByDefault:
SSEAlgorithm: "AES256"
BucketKeyEnabled: false
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
# SSLがないのはNG
OutCloudTrailBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref OutCloudTrailBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: DenyNotSecureTransport
Effect: Deny
Principal: "*"
Resource:
- !Sub "arn:aws:s3:::${OutCloudTrailBucket}"
- !Sub "arn:aws:s3:::${OutCloudTrailBucket}/*"
Action: "*"
Condition:
Bool:
aws:SecureTransport: false
- Sid: AWSCloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:GetBucketAcl
Resource: !Sub "arn:aws:s3:::${OutCloudTrailBucket}"
- Sid: AWSCloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: s3:PutObject
Resource: !Sub "arn:aws:s3:::${OutCloudTrailBucket}/s3AccessLogTrail/AWSLogs/${AWS::AccountId}/*"
Condition:
StringEquals:
s3:x-amz-acl: bucket-owner-full-control
##################################################
# Role
##################################################
## CloudTrailにアタッチする、S3Accesslogをロググループに出力可能なロール作成
S3AccesslogOutRole:
Type: AWS::IAM::Role
Condition: CreateTrail
Properties:
RoleName: for-s3accesslog-trail
Path: /
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- "cloudtrail.amazonaws.com"
Action: 'sts:AssumeRole'
Policies:
# IAMの参照などを追加
- PolicyName: out-s3accesslog
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- "logs:CreateLogStream"
- "logs:PutLogEvents"
Resource: !GetAtt S3AccesslogLogGroup.Arn
Tags:
- Key: "usefor"
Value: "S3 Accesslog"
##################################################
# CloudTrail
##################################################
Trail:
Type: AWS::CloudTrail::Trail
Condition: CreateTrail
Properties:
S3BucketName: !Ref OutCloudTrailBucket
S3KeyPrefix: "s3AccessLogTrail"
CloudWatchLogsLogGroupArn: !GetAtt S3AccesslogLogGroup.Arn
CloudWatchLogsRoleArn: !GetAtt S3AccesslogOutRole.Arn
EnableLogFileValidation: true
IsLogging: true
TrailName: S3AccessLogTrail
IncludeGlobalServiceEvents: true
EventSelectors:
- DataResources:
- Type: AWS::S3::Object
Values: !Ref TrailBuckets
ReadWriteType: All
IncludeManagementEvents: false
Outputs:
IllegalTraficSnsTopic:
Value: !Ref IllegalTraficSnsTopic
Export:
Name: IllegalTraficSnsTopic
S3AccesslogLogGroup:
Value: !Ref S3AccesslogLogGroup
Export:
Name: S3AccesslogLogGroup
- Parameters
- 監視対象のバケットのArnを、カンマ区切りで複数指定し、リスト形式として扱うようにしています。
- Conditions
- "CreateTrail"という条件キーに対して、「パラメータがセットされていればTrue」というようにしています。
-
!Not [!Equals [ !Join [ '', !Ref TrailBuckets ] , '']]
について- ”パラメータの部分で生成されたリストを連結した文字列”が、”空文字と一致した場合”、”の否定”をセットしています。
- CloudFormationでは、リストをEqualsで比較することはできないようなのでこういう風にしています。
- ”パラメータの部分で生成されたリストを連結した文字列”が、”空文字と一致した場合”、”の否定”をセットしています。
- 証跡ログバケット
- 以下のバケットポリシーを設定しています。
- SSL暗号化通信していないアクセスを禁止
- CloudTrailの書き込み
- コンソールから作成した際の自動生成時に倣い、ACLの取得可と、オブジェクト出力可、の2つを付けています。
- 以下のバケットポリシーを設定しています。
- CloudTrailと、CloudTrail用ロール
- 条件キー"CreateTrail"がTrueの場合に作成するようにしました。
個別の環境で使うリソース
02_createEachEnvForTrail.yml
AWSTemplateFormatVersion: 2010-09-09
Description: Create Each Env for Trail on S3
Parameters:
EnvId:
Type: String
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
-
Label:
default: User Setting.
Parameters:
- EnvId
Resources:
##################################################
# S3
##################################################
PrivateBucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Sub "for-${EnvId}-${AWS::AccountId}"
BucketEncryption:
ServerSideEncryptionConfiguration:
-
ServerSideEncryptionByDefault:
SSEAlgorithm: "AES256"
BucketKeyEnabled: false
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
# SSLがないのはNG
PrivateBucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket: !Ref PrivateBucket
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: DenyNotSecureTransport
Effect: Deny
Principal: "*"
Resource:
- !Sub "arn:aws:s3:::${PrivateBucket}"
- !Sub "arn:aws:s3:::${PrivateBucket}/*"
Action: "*"
Condition:
Bool:
aws:SecureTransport: false
##################################################
# MetricFilter
##################################################
MetricFilter:
# フィルタ名は [CFnのスタック名]-[リソース名]-[ランダム文字列]が付く
Type: AWS::Logs::MetricFilter
Properties:
LogGroupName: !ImportValue S3AccesslogLogGroup
FilterPattern: !Sub '{($.eventName="GetObject") && (($.sourceIPAddress!="123.123.*") && ($.sourceIPAddress!="234.234.*") ) && ($.requestParameters.bucketName="${PrivateBucket}") }'
MetricTransformations:
- MetricValue: 1
MetricNamespace: Custom/S3
MetricName: !Sub ${EnvId}-s3-IllegalTrafic
DefaultValue: 0
##################################################
# Alarm
##################################################
LogFilterAlarm:
Type: AWS::CloudWatch::Alarm
Properties:
Namespace: Custom/S3
MetricName: !Sub ${EnvId}-s3-IllegalTrafic
Statistic: Sum
Period: 300 # の間に
ComparisonOperator: GreaterThanOrEqualToThreshold # の状態を
Threshold: 1 # 回超えることが
EvaluationPeriods: 1 # 周期中
DatapointsToAlarm: 1 # 回なら
TreatMissingData: notBreaching
AlarmActions:
- !ImportValue IllegalTraficSnsTopic
AlarmName: !Sub ${EnvId}-s3-IllegalTraficAlarm
AlarmDescription: !Sub "arn:aws:s3:::${PrivateBucket} にて、不正トラフィック検出"
# CloudTrailを作成するCFnのパラメータに渡す文字列を出力
# (指定時に末尾スラッシュ必要)
Outputs:
PrivateBucket:
Value: !Sub "arn:aws:s3:::${PrivateBucket}"
Export:
Name: !Sub "PrivateBucket-${EnvId}"
- S3は、SSL暗号化通信されている通信のみ可としました。
- フィルターは、「対象のバケットに対しての、特定のIP以外からの、取得アクセス」としました。
- アラームの設定値の説明は推測です。わかる方はご指摘いただけると助かります。
- 出力するS3のArnは、"01_createBaseEnvForTrail.yml"のパラメータにセットします。
環境作成手順
共通で使うリソース作成
- 01_createBaseEnvForTrail.ymlを実行。
- パラメータには、この時点では何も指定しません。
個別の環境で使うリソース
- 02_createEachEnvForTrail.ymlを、構築したい環境分だけ、EnvIdを指定して実行。今回は「gamagama」と「tingara」という環境を作りました。
- 出力されたS3のArnを控えておきます。
共通で使うリソースの、CloudTrail更新
- ”共通で使うリソース作成”のスタックを更新。"現在のテンプレートの使用"を選びます。
- パラメータに、"個別の環境で使うリソース"で出力されたS3のArnを、末尾にスラッシュ(/)を付与し、カンマ区切りでセットし更新します。
確認
対象のバケットに何か置いて、フィルターで指定したIP範囲外から取得すると、以下のようになります。
今回は5分程度でCloudTrailからログが配信されましたが、20分近くかかることもありました。
片づけ
- 各環境のS3バケットを空にします。
- CloudFormationで、各環境のスタックを削除します。
- 証跡ログバケットを空にします。
- CloudFormationで、共通環境のスタックを削除します。
まとめ
「S3の不正アクセスを検出できる環境」を、CloudFormationで作成しました。
次は以前作成した、VPCの不正トラフィック検出と合わせて、VPC&S3バケットの不正トラフィックの検出環境の構築をCloudFormation化~~したいと思います。~~したものを記事にしました。