LoginSignup
0
0

More than 1 year has passed since last update.

VPC-S3の環境で、不正なデータ送信をAlarmする環境を、CloudFormationで構築する

Posted at

はじめに

以前作成した、VPCの内部から不正なデータ送信のアラームを送信する環境と、S3の不正アクセスを検出する環境を、一つにしたCloudFormationを作成しました。

構成

今回も、複数の環境を想定して、「共通で使うリソース」と「個別の環境で使うリソース」で分けて作成しました。

image.png

以前と少し異なる点は、VPCとCloudTrailにアタッチさせるIAMロール(CloudWatch Logs出力権限)を共通にしました。
それ以外のリソース等は、以前(VPCS3)のものと同じです。

CloudFormation

コードは以下になります、説明等に使われている英語の拙さはご容赦ください。

共通で使うリソース

01_createBaseEnvForTrail.yml

AWSTemplateFormatVersion: 2010-09-09
Description: Create Base Env for Trail on VPC and 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: 
##################################################
# Common Resource
##################################################
# SNS
  IllegalTraficSnsTopic:
    Type: AWS::SNS::Topic
    Properties:
      TopicName: illegal-trafic-sns-topic
      Tags:
          - Key: "usefor"
            Value: "send e-mail detection of illegal trafic"
# Role for Output to Log Group
  ## ロググループに出力可能なロール作成
  OutputRoleToLogGroup:
    Type: AWS::IAM::Role
    Properties:
      RoleName: for-outputto-loggroup
      Path: /
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "vpc-flow-logs.amazonaws.com"
                - "cloudtrail.amazonaws.com"
            Action: 'sts:AssumeRole'
      Policies:
        # IAMの参照などを追加
        - PolicyName: outputto-loggroup
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:PutLogEvents"
                  - "logs:DescribeLogGroups"
                  - "logs:DescribeLogStreams"
                Resource: '*'
      Tags:
          - Key: "usefor"
            Value: "output to LogGroup"

##################################################
# VPC Resource
##################################################
# Cloudwatch log Group
  VpcFlowlogsLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: /custom/vpcflowlogs
      RetentionInDays: 3653   # 未指定時は「失効しない」

##################################################
# S3 Resource
##################################################
# 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
# CloudTrail
  Trail:
    Type: AWS::CloudTrail::Trail
    Condition: CreateTrail
    Properties:
      S3BucketName: !Ref OutCloudTrailBucket
      S3KeyPrefix: "s3AccessLogTrail"
      CloudWatchLogsLogGroupArn: !GetAtt S3AccesslogLogGroup.Arn
      CloudWatchLogsRoleArn: !GetAtt OutputRoleToLogGroup.Arn
      EnableLogFileValidation: true
      IsLogging: true
      TrailName: S3AccessLogTrail
      IncludeGlobalServiceEvents: true
      EventSelectors:
        - DataResources:
          - Type: AWS::S3::Object
            Values: !Ref TrailBuckets
          ReadWriteType: All
          IncludeManagementEvents: false

Outputs:
##################################################
# Common Resource
##################################################
  IllegalTraficSnsTopic:
    Value: !Ref IllegalTraficSnsTopic
    Export:
      Name: IllegalTraficSnsTopic
  OutputRoleArnToLogGroup:
    Value: !GetAtt OutputRoleToLogGroup.Arn
    Export:
      Name: OutputRoleArnToLogGroup
##################################################
# VPC Resource
##################################################
  VpcFlowlogsLogGroup:
    Value: !Ref VpcFlowlogsLogGroup
    Export:
      Name: VpcFlowlogsLogGroup
##################################################
# S3 Resource
##################################################
  S3AccesslogLogGroup:
    Value: !Ref  S3AccesslogLogGroup
    Export:
      Name: S3AccesslogLogGroup

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

02_createEachEnvForTrail.yml

AWSTemplateFormatVersion: 2010-09-09
Description: Create Each Env for Trail on VPC and S3.
Parameters:
  EnvId:
    Type: String
  VpcCidr:
    Type: String
    Default: 192.168.209.0/24
  SubnetACidr:
    Type: String
    Default: 192.168.209.0/26
  SubnetCCidr:
    Type: String
    Default: 192.168.209.64/26
  SubnetDCidr:
    Type: String
    Default: 192.168.209.128/26
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      -
        Label:
          default: User Setting.
        Parameters:
          - EnvId
          - VpcCidr
          - SubnetACidr
          - SubnetCCidr
          - SubnetDCidr

Resources:
##################################################
# VPC
##################################################
  Vpc:
    Type: AWS::EC2::VPC
    Properties: 
      CidrBlock: !Ref VpcCidr
      # EnableDnsHostnames: true
      # EnableDnsSupport: true
      Tags: 
        - Key: Name
          Value: !Sub "${EnvId}-vpc"
##################################################
# Security Group
##################################################
  SecGrpOnlyInternal:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Enable access from EC2 Instance Connect IP
      GroupName: !Sub "${EnvId}-sg"
      VpcId: !Ref Vpc
      # For EC2 Instance Connect
      SecurityGroupIngress:
        -
          IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: 3.112.23.0/29
  SGBaseIngress:
    Type: AWS::EC2::SecurityGroupIngress
    Properties:
      GroupId: !Ref SecGrpOnlyInternal
      IpProtocol: -1
      SourceSecurityGroupId: !GetAtt SecGrpOnlyInternal.GroupId
##################################################
# IGW
##################################################
  Igw:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-igw
  IgwAttachment:
    Type: AWS::EC2::VPCGatewayAttachment
    Properties: 
      InternetGatewayId: !Ref Igw
      VpcId: !Ref Vpc
##################################################
# Subnets
##################################################
  SubnetA:
    Type: AWS::EC2::Subnet
    Properties: 
      VpcId: !Ref Vpc
      AvailabilityZone: ap-northeast-1a
      CidrBlock: !Ref SubnetACidr
      MapPublicIpOnLaunch: false
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-subnet-a
  SubnetC:
    Type: AWS::EC2::Subnet
    Properties: 
      VpcId: !Ref Vpc
      AvailabilityZone: ap-northeast-1c
      CidrBlock: !Ref SubnetCCidr
      MapPublicIpOnLaunch: false
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-subnet-c
  SubnetD:
    Type: AWS::EC2::Subnet
    Properties: 
      VpcId: !Ref Vpc
      AvailabilityZone: ap-northeast-1d
      CidrBlock: !Ref SubnetDCidr
      MapPublicIpOnLaunch: false
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-subnet-d
##################################################
# Routing
##################################################
  RouteTable:
    Type: AWS::EC2::RouteTable
    Properties: 
      VpcId: !Ref Vpc
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-public-rtb
  RouteTableAssocA:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref SubnetA
  RouteTableAssocC:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref SubnetC
  RouteTableAssocD:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties: 
      RouteTableId: !Ref RouteTable
      SubnetId: !Ref SubnetD
  Route:
    Type: AWS::EC2::Route
    Properties: 
      RouteTableId: !Ref RouteTable
      DestinationCidrBlock: 0.0.0.0/0
      GatewayId: !Ref Igw

##################################################
# 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

##################################################
# VPC Endpoint to S3
##################################################
  VPCS3Endpoint:
    Type: AWS::EC2::VPCEndpoint
    Properties:
      VpcEndpointType: Gateway
      RouteTableIds: 
        - !Ref RouteTable
      ServiceName: !Sub "com.amazonaws.${AWS::Region}.s3"
      VpcId: !Ref Vpc
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal: '*'
            Action:
              - "s3:*"
            Resource:
              - !Sub "arn:aws:s3:::${PrivateBucket}"
              - !Sub "arn:aws:s3:::${PrivateBucket}/*"

##################################################
# VPC FlowLogs
##################################################
  VpcFlowLogs:
    Type: AWS::EC2::FlowLog
    Properties:
      ResourceType: VPC
      ResourceId: !Ref Vpc
      TrafficType: ALL
      DeliverLogsPermissionArn: !ImportValue OutputRoleArnToLogGroup
      LogGroupName: !ImportValue VpcFlowlogsLogGroup
      LogFormat: '${account-id} ${action} ${az-id} ${bytes} ${dstaddr} ${dstport} ${end} ${flow-direction} ${instance-id} ${interface-id} ${log-status} ${packets} ${pkt-dst-aws-service} ${pkt-dstaddr} ${pkt-src-aws-service} ${pkt-srcaddr} ${protocol} ${region} ${srcaddr} ${srcport} ${start} ${sublocation-id} ${sublocation-type} ${subnet-id} ${tcp-flags} ${traffic-path} ${type} ${version} ${vpc-id}'
      Tags: 
        - Key: Name
          Value: !Sub ${EnvId}-flowlog
##################################################
# Role
##################################################
  # EC2アタッチ用ロール作成
  Ec2Role:
    Type: AWS::IAM::Role
    Properties:
      RoleName: !Sub for-ec2-on-${EnvId}
      Path: /
      AssumeRolePolicyDocument: 
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - "ec2.amazonaws.com"
            Action: 'sts:AssumeRole'
      Policies:
        - PolicyName: s3fullaccess
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - "s3:*"
                Resource:
                  - !Sub "arn:aws:s3:::${PrivateBucket}"
                  - !Sub "arn:aws:s3:::${PrivateBucket}/*"
  ## インスタンスプロファイル作成
  MyInstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: /
      Roles:
        - !Ref Ec2Role

##################################################
# MetricFilter
# フィルタ名は [CFnのスタック名]-[リソース名]-[ランダム文字列]が付く
##################################################
  # VPC FlowLogs
  MetricFilterOfVpcFlowLogs:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !ImportValue VpcFlowlogsLogGroup
      FilterPattern: !Sub "[account_id, action, az_id, bytes > 1000000, dstaddr!=52.219.* && dstaddr!=3.5.* && dstaddr!=35.72.164.* && dstaddr!=35.73.115.* , dstport, end, flow_direction = egress, instance_id, interface_id, log_status, packets, pkt_dst_aws_service!=EC2_INSTANCE_CONNECT, ..., vpc_id=${Vpc}]"
      MetricTransformations: 
        - MetricValue: 1
          MetricNamespace: Custom/VPC
          MetricName: !Sub ${EnvId}-vpc-IllegalTrafic
          DefaultValue: 0
  # S3 AccessLog
  MetricFilterOfS3AccessLog:
    Type: AWS::Logs::MetricFilter
    Properties:
      LogGroupName: !ImportValue S3AccesslogLogGroup
      FilterPattern: !Sub '{($.eventName="GetObject") && (($.sourceIPAddress!="123.123.123.123") && ($.sourceIPAddress!="234.234.*") && ($.vpcEndpointId!="${VPCS3Endpoint}" || $.vpcEndpointId NOT EXISTS)) && ($.requestParameters.bucketName="${PrivateBucket}")}'
      MetricTransformations: 
        - MetricValue: 1
          MetricNamespace: Custom/S3
          MetricName: !Sub ${EnvId}-s3-IllegalTrafic
          DefaultValue: 0
##################################################
# Alarm
##################################################
  # VPC FlowLogs
  LogFilterAlarmOfVpcFlowLogs:
    Type: AWS::CloudWatch::Alarm
    Properties:
      Namespace: Custom/VPC
      MetricName: !Sub ${EnvId}-vpc-IllegalTrafic
      Statistic: Sum
      Period: 300                                           # の間に
      ComparisonOperator: GreaterThanOrEqualToThreshold     # の状態を
      Threshold: 1                                          # 回超えることが
      EvaluationPeriods: 1                                  # 周期中
      DatapointsToAlarm: 1                                  # 回なら
      TreatMissingData: notBreaching
      AlarmActions: 
        - !ImportValue IllegalTraficSnsTopic
      AlarmName: !Sub ${EnvId}-vpc-IllegalTraficAlarm
      AlarmDescription: !Sub "${EnvId} vpc にて、不正トラフィック検出"
  # S3 AccessLog
  LogFilterAlarmOfS3AccessLog:
    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} にて、不正トラフィック検出"

##################################################
# Output
##################################################
# CloudTrailを作成するCFnのパラメータに渡す文字列を出力
# (指定時に末尾スラッシュ必要)
Outputs:
  PrivateBucket:
    Value: !Sub "arn:aws:s3:::${PrivateBucket}"
    Export:
      Name: !Sub "PrivateBucket-${EnvId}"

以前と異なる点は以下になります。

  • VPCフローログのフィルタ
    • サイズを"1MBを超える"にしました。
    • "EC2_INSTANCE_CONNECT経由のトラフィック"を除外
      • pkt_dst_aws_service!=EC2_INSTANCE_CONNECT,
      • 使ってみると、ここのトラフィックに流れるデータのサイズが大きかったため除外。
  • S3アクセスログのフィルタ
    • 同じスタックで作成したVPCのエンドポイント経由を除外
      • ($.vpcEndpointId!="${VPCS3Endpoint}" || $.vpcEndpointId NOT EXISTS)
      • $.vpcEndpointId!=xxxxだけだと、VPCエンドポイントを経由しないアクセスにもFalseとなるようだったので、$.vpcEndpointId NOT EXISTSをORで結合しました。

環境作成手順、片づけ手順

作成の仕方はS3の時と、片づけの仕方もS3の時と同様です。

まとめ

PC&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