0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

S3のログにCloudWatchLogsのAlarmを設定する(CloudFormation編)

Last updated at Posted at 2022-01-03

はじめに

前回コンソールから作成した、S3の不正アクセスを検出する環境を、CloudFormationで記述しました。

構成

複数の環境を想定して、「共通で使うリソース」と「個別の環境で使うリソース」で分けて作成しました。
「共通で使うリソース」のCloudTrailは、監視対象バケットが増えるたびに設定(監視するバケット)を変更する想定です。

image.png

  • 共通で使うリソース
    • CloudTrail
      • 監視対象が増えるたびに設定変更します。
        • 初回作成時は監視対象バケットが無いので、作成しないようにします。
    • 証跡ログバケット
    • S3アクセスログ用Log Group
    • SNS
  • 個別の環境で使うリソース
    • 監視対象バケット
    • メトリクスとアラート
      • ロググループは一つなので、監視対象バケットの名前などをフィルターに入れて分別しています。

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 ] , '']]について
      • ”パラメータの部分で生成されたリストを連結した文字列”が、”空文字と一致した場合”、”の否定”をセットしています。
  • 証跡ログバケット
    • 以下のバケットポリシーを設定しています。
      • 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"のパラメータにセットします。

環境作成手順

共通で使うリソース作成

  1. 01_createBaseEnvForTrail.ymlを実行。
  2. パラメータには、この時点では何も指定しません。

image.png

個別の環境で使うリソース

  1. 02_createEachEnvForTrail.ymlを、構築したい環境分だけ、EnvIdを指定して実行。今回は「gamagama」と「tingara」という環境を作りました。
  2. 出力されたS3のArnを控えておきます。

共通で使うリソースの、CloudTrail更新

  1. ”共通で使うリソース作成”のスタックを更新。"現在のテンプレートの使用"を選びます。
  2. パラメータに、"個別の環境で使うリソース"で出力されたS3のArnを、末尾にスラッシュ(/)を付与しカンマ区切りでセットし更新します。

image.png

確認

対象のバケットに何か置いて、フィルターで指定したIP範囲外から取得すると、以下のようになります。
今回は5分程度でCloudTrailからログが配信されましたが、20分近くかかることもありました。

image.png

片づけ

  1. 各環境のS3バケットを空にします。
  2. CloudFormationで、各環境のスタックを削除します。
  3. 証跡ログバケットを空にします。
  4. CloudFormationで、共通環境のスタックを削除します。

まとめ

「S3の不正アクセスを検出できる環境」を、CloudFormationで作成しました。
次は以前作成した、VPCの不正トラフィック検出と合わせて、VPC&S3バケットの不正トラフィックの検出環境の構築をCloudFormation化~~したいと思います。~~したものを記事にしました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?