AWSIaC

CloudTrailログの可視化をElasticSearch+KibanaでCloudFormationでやってみる

AWS

以前に以下の記事でCloudTrailの取得をCloudFormationで有効化しました。 S3にCloudTrailログがどんどん溜まってきているところまで作成しています。

AWSのCloudFormationでIaCを実践(6) – CloudTrail 
https://syachiku.net/awscloudformationiac6-trail/

今回はCloudTrailログを溜めるだけではなく、せっかくなのでグラフィカルに可視化をしていきたいと思います。

可視化する方法としてはEC2インスタンスに可視化ツールをインストールすることもできますが、今回はAWSのマネジメントサービスである「Amazon Elasticsearch Service」を使います。

さらに、できる限り(全部ではないです)CloudFormationによるコード化をするようにしたいと思います。

1. 前提条件

  • AWSアカウントが取得できていること
  • 適切な権限(=Admin相当)をもったIAMユーザーを作成していること
  • ルーティングなどの最低限のネットワークの知識があること
  • YAMLの書きかたを知っていること(CloudFormationはjsonもしくはyamlで書けますが、今回はyamlで書いていきます。)
  • kibanaレポート(ElasticSeachのクラスタ)がVPCのプライベートサブネットに配置されるので、ブラウザでアクセスできる環境があること(WindowsのEC2などでもOK)
    • 今回は自宅からVPNで接続してProxyを経由してアクセスします。

※今回実施するElasticseachについては無料枠の範囲ではないため、少なからず利用料金が発生します、あくまで自己責任でお願いします。

2. 構成図

構成図としては以下の様な感じになるかと思います。CloudWatchLogsのリソースはCroudTrailと同じスタックにしました。

3. 今回作成するAWS環境と作業手順

大阪リージョンの場合にはElasticSeachの最小インスタンスが結構大きいものしか選択できなかったことと、CWLogSubscriptionでElasticseachの選択肢がなかったので、オレゴンリージョンで構築しています。

それでは、以下の手順で構築を行っていきます。

  1. ElasticSearchのドメイン作成時に必要となる「EsServiceLinkedRole」を作成
  2. CloudTrailのログをS3バケットへ保管するのに追加し、Cloudwach Loggroupにも転送するように設定(以下は前回との変更点です)
    1. CloudTrailログ保管するCWLogGroupを新規作成
    2. LogGroupのSubscription Filterで利用するロールを作成
  3. ElasticSeachドメインを作成
    1. 2台のクラスタ構成(PrivateSubnet)でドメイン構成
    2. Elasticsearch用のSecurityGroupを作成

ここまではCloufFormationで構築しますが、以下の作業は手動でマネジメントコンソールで構築しました。 マネジメントコンソール経由だと自動でLambdaなどを作成してくれるのでこっちにしてます。

  1. LogGroupへサブスクリプションフィルター作成(+Lambda関数も自動で作成)
  2. kibanaのレポートを手動で作成

4. CloudFormation Template

最初にEsServiceLinkedRoleの構築です。 ES以外も想定してsystem-roleとしてスタックを分離して構築してます。

AWSTemplateFormatVersion: "2010-09-09"
Description:
  system role

Resources:
  EsServiceLinkedRole:
    Type: "AWS::IAM::ServiceLinkedRole"
    Properties:
      AWSServiceName: es.amazonaws.com
      Description: "Service Linked Role for Amazon Elasticsearch Service"

Outputs:
  EsServiceLinkedRole:
    Value: !Ref EsServiceLinkedRole
    Export:
      Name: es-service-linked-role

次にCloudTrailのテンプレートとなります。

AWSTemplateFormatVersion: 2010-09-09
Description: CloudTrail

Parameters:
  LogGroupName:
    Type: String
    Description: Enter LogGroupName
    Default: "aws-infra-croudtrail-loggroup"
  LogStreamName:
    Type: String
    Description: Enter LogStreamName
    Default: "aws-infra-croudtrail-logstream"

Resources:
  CloudTrailLogsBucket:
    Type: AWS::S3::Bucket
    #    DeletionPolicy: Retain
    UpdateReplacePolicy: Retain
    Properties:
      BucketName: !Sub ${AWS::Region}-cloudtrail-logs
      AccessControl: LogDeliveryWrite
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
              SSEAlgorithm: AES256
      LifecycleConfiguration:
        Rules:
          - Id: AutoDelete
            Status: Enabled
            ExpirationInDays: 14
          - Id: NoncurrentVersionExpiration
            Status: Enabled
            NoncurrentVersionExpirationInDays: 7
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      VersioningConfiguration:
        Status: Enabled

  # CloudTrailログ格納用バケットポリシー
  CloudTrailLogsBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
      Bucket: !Ref CloudTrailLogsBucket
      PolicyDocument:
        Version: 2012-10-17
        Statement:
          - Sid: CloudTrailAclCheck
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:GetBucketAcl
            Resource: !Sub arn:aws:s3:::${CloudTrailLogsBucket}
          - Sid: CloudTrailWrite
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: s3:PutObject
            Resource: !Sub arn:aws:s3:::${CloudTrailLogsBucket}/AWSLogs/${AWS::AccountId}/*
            Condition:
              StringEquals:
                s3:x-amz-acl: bucket-owner-full-control

  # Create CWLog group
  CloudTrailLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Ref LogGroupName
      RetentionInDays: 3

  # Create CloudWTrailRole for CWLogs and ElasticSearch
  CloudTrailLogRole:
    Type: "AWS::IAM::Role"
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - !Sub "cloudtrail.${AWS::URLSuffix}"
                - !Sub "lambda.${AWS::URLSuffix}"
            Action:
              - "sts:AssumeRole"
      Path: "/"
      Policies:
        - PolicyName: aws-infra-policy-cloudtrail-es
          PolicyDocument:
            Version: 2012-10-17
            Statement:
              - Effect: Allow
                Action:
                  - 'es:ESHttpPost'
                Resource: "arn:aws:es:*:*:*"
              - Effect: Allow
                Action:
                  - "logs:CreateLogGroup"
                  - "logs:CreateLogStream"
                  - "logs:DescribeLogStreams"
                  - "logs:PutLogEvents"
                  - "ec2:CreateNetworkInterface"
                  - "ec2:DescribeNetworkInterfaces"
                  - "ec2:DeleteNetworkInterface"
                Resource: "*"
              - Effect: Allow
                Action:
                  - 'lambda:InvokeFunction'
                Resource: "arn:aws:logs:*:*:*"

  # CloudTrailから配信されるログの暗号化キー
  # https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/create-kms-key-policy-for-cloudtrail.html
  KmsKey:
    Type: AWS::KMS::Key
    Properties:
      KeyPolicy:
        Version: 2012-10-17
        Id: DefaultKeyPolicy
        Statement:
          - Sid: EnableIAMUserPermissions
            Effect: Allow
            Principal:
              AWS: !Sub arn:aws:iam::${AWS::AccountId}:root
            Action: kms:*
            Resource: '*'
          - Sid: AllowCloudTrailToEncryptLogs
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: kms:GenerateDataKey*
            Resource: '*'
            Condition:
              StringLike:
                kms:EncryptionContext:aws:cloudtrail:arn:
                  - !Sub arn:aws:cloudtrail:*:${AWS::AccountId}:trail/*
          - Sid: AllowCloudTrailToDescribeKey
            Effect: Allow
            Principal:
              Service: cloudtrail.amazonaws.com
            Action: kms:DescribeKey
            Resource: '*'
      KeyUsage: ENCRYPT_DECRYPT

  CloudTrail:
    Type: AWS::CloudTrail::Trail
    Properties:
      CloudWatchLogsLogGroupArn: !GetAtt CloudTrailLogGroup.Arn
      CloudWatchLogsRoleArn: !GetAtt CloudTrailLogRole.Arn
      S3BucketName: !Ref CloudTrailLogsBucket
      KMSKeyId: !Ref KmsKey
      TrailName: CloudTrailLog
      IncludeGlobalServiceEvents: true
      IsLogging: true
      IsMultiRegionTrail: true
      EnableLogFileValidation: true
      EventSelectors:
        - DataResources:
            - Type: AWS::S3::Object
              Values:
                - arn:aws:s3

ElasticSearchを構築するテンプレートです。

AWSTemplateFormatVersion: "2010-09-09"
Description:
  elastic search

Parameters:
  ESDomainName:
    Description: "Your Elasticsearch Domain Name"
    Type: String
    MinLength: 3
    MaxLength: 28
    AllowedPattern: "^[a-z0-9+-]*$"
    Default: "aws-infra-es-domain"
  MyVpcId:
    Type: String
    Default: "vpc-id"
  MyVpcCidrBlock:
    Type: String
    Default: "10.1.0.0/16"
  MyEsSubnetId01:
    Type: String
    Default: "subnet-private01"
  MyEsSubnetId02:
    Type: String
    Default: "subnet-private02"
  InstanceType:
    Type: String
    Default: "t3.small.elasticsearch"
    AllowedValues:
      - t3.small.elasticsearch
      - t3.medium.elasticsearch
      - r5.large.elasticsearch

Resources:
  # Security Group
  # For Amazon Elasticsearch Service
  ElasticsearchSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: elasticsearch-sg
      VpcId: !Ref MyVpcId
      Tags:
        - Key: Name
          Value: aws-infra-elasticsearch-sg
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: '443'
          ToPort: '443'
          CidrIp: !Ref MyVpcCidrBlock

  # Elasticsearch on VPC
  MyElasticsearch:
    Type: AWS::Elasticsearch::Domain
    Properties:
      AccessPolicies:
        Version: 2012-10-17
        Statement:
          - Effect: Allow
            Principal:
              AWS: '*'
            Action: 'es:*'
            Resource: !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${ESDomainName}/*
      DomainName: !Ref ESDomainName
      EBSOptions:
        EBSEnabled: true
        VolumeSize: 10
        VolumeType: gp2
      ElasticsearchClusterConfig:
        InstanceCount: 2
        InstanceType: !Ref InstanceType
        ZoneAwarenessEnabled: true
      ElasticsearchVersion: 7.4
      SnapshotOptions:
        AutomatedSnapshotStartHour: 17
      VPCOptions:
        SubnetIds:
          - !Ref MyEsSubnetId01
          - !Ref MyEsSubnetId02
        SecurityGroupIds:
          - !Ref ElasticsearchSecurityGroup
    UpdatePolicy:
      EnableVersionUpgrade: true

5. スタックの作成

それでは先ほど作成したコードを使って、リソース(CloudFormationで作成できる部分)を作成していきます。

マネージメントコンソールにログオンして、リージョンをオレゴンに切り替えたのち、CloudFormationを開きます。

「スタックの作成」→「新しいリソースを使用」を選択します。

「テンプレートファイルをアップロード」からコードのファイルを選択してアップします。

スタックの名称やネットワーク設定値を入力(デフォルトから変更したい場合には変更してください)してスタック作成をしてください。

※「今回作成するAWS環境と作業手順」に記載している順番通りにスタックを作成してください。

5.1. IAM Role

5.2. セキュリティグループ

5.3. CloudWatchロググループ

5.4. Elasticsearchドメイン

Elasticsearchドメイン構築はかなり時間がかかります。20分くらいかかりました。

6. ロググループのサブスクリプションフィルター作成

次に手動操作になります。マネコン経由でロググループのサブスクリプションフィルター作成をします。

Elasticsearch 用のsubscription Filterのメニューが存在します(大阪リージョンでは存在しませんでした2021年8月時点)

送信先に先ほど作成したElasticsearchを選択します。

ログ形式としてCloudTrailの項目が用意されています。これを選択するだけでLambdaも自動的に作成してくれるようです。なんて便利なんだ・・・

7. kibanaレポートの作成

最後にkibanaのレポート作成です。

プライベートサブネットにアクセスできるマシンのブラウザ経由で実施してください。

まずはkibanaのURLをElasticsearchのドメインの概要から確認します。

kibanaのURLへアクセスします。

インデックスを作成します。cloudtrailのログインデックスがcwl-xxxxという日付単位の形式なので、cws-20*とかで作成します。

timestampを指定するだけですね。

これだけです。簡単に可視化レポートができました。

あとは左側の項目でフィルタかければ項目単位で検索することができますね。

8. まとめ

CloudTrailのログをCWLogsのサブスクリプションフィルター経由でElasticsearchに流してkibanaでのレポート表示までをおこないました。

ログを可視化する流れとしては「CWLogsのサブスクリプションフィルター→Elasticsearch」というのは一番使われている気がします。

他のログなども応用できるかと思います。

できれば全てCloudFormationで行いたかったのですが、思ったよりもマネコン経由での手間がなかったので一部手動での作業手順となりました。

時間がある時(もしくは機会があれば)全てのリソースのCloudFormation化を行いたいと考えています。

AWSを効率的に学習する方法

私がAWSを学習するために使った学習材料は「Udemy」だけです。

まずは座学を行ってAWSプラクティショナー もしくは ソリューションアーキテクト試験を取得することを目標にしました。

具体的にはUdemyで以下の2つの商材を購入して学習しました。AWS公式が提供している模擬試験も受けていません。

Udemyは世界最大級のオンライン学習プラットフォームで、世界中の学びたい人と教えたい人をオンラインでつなぐサービスです。 Udemyは米国Udemy,Inc.が運営するプラットフォームで日本ではベネッセが事業パートナーとして協業をしています。 C2C(consumer to consumer)により生まれる豊富で多彩な講座により個人の学習ニーズに応じた学びをみつけることができます。

これだけでOK! AWS 認定ソリューションアーキテクト – アソシエイト試験突破講座(SAA-C02試験対応版)
【SAA-C02版】AWS 認定ソリューションアーキテクト アソシエイト模擬試験問題集(6回分390問)

今回は以上となります。

コメント

タイトルとURLをコピーしました