5
2

More than 3 years have passed since last update.

セッションマネージャのEC2操作ログのリアルタイム分析

Last updated at Posted at 2021-02-13

はじめに

皆さん、EC2インスタンスの操作はどのように行ってますか?

2018年9月に登場したSystemsManagerの1つの機能であるSessionManagerを使い
踏み台サーバを用意せず、EC2を操作しているインフラ管理者の方も増えてきたのではないでしょうか。
image.png

【参考】
AWS Systems Manager Session Manager

本投稿の内容

2020年11月17日にアップデートがありました。
ストリーミングで出力されるログをAmazon Elasticsearch Service(以降、Amazon ES)に取り込み
Kibanaで可視化してみました!

お客様は、セッションが終了するまで待つのではなく、セッション期間中、セッションログをCloudWatchに継続的にストリーミングできるようになりました。ログはJSONメッセージとして構造化され、セッションを開始するユーザー、インスタンスIDとセッションID、およびセッションからのコマンドと出力を識別します。セッションの期間中、構造化ログを継続的に受信して処理する機能により、ユーザーアクティビティの可視性が向上します。構造化ログを使用すると、セッションの開始や特定のコマンドの使用などの条件を簡単に検索して、セッションアクティビティの分析とトラブルシューティングに役立てることができます。

このアップデートでかなりログの完全性や信頼性が上がっているはず!!

【参考】
Amazon CloudWatch Logs を使用してセッションデータをストリーミングする

利用環境

項目 内容
logstash (OSS版) 7.9.3
Java (Corretto) 11.0.10+9-LTS
OS(EC2) Amazon Linux2 (t3.small)
AMI ID ami-0e999cbd62129e3b1
Elasticsearch 7.9 (latest)
Region us-west-2

※投稿時点における最新版を採用しています。

【構成図】
・ CloudWatch Logsにリアルタイム出力されたログをLogstashがTailしています。
image.png

【補足】
・ Amazon ESへのログ格納にはElastic社のETLツールであるLogstashを利用します。
・ Amazon ESはOSS版のため、LogstashもOSS版とし、バージョンも合わせています。
・ Amazon ESはパブリックアクセスとし、IPアドレス制御でセキュリティを確保しています。

【参考】
Logstashとは

実施内容

  1. IAM Roleの作成
  2. ロググループの作成
  3. セッションアクティビティログの出力
  4. SSM Agentバージョンアップ
  5. Amazon ESのドメイン作成
  6. Logstashの構築
  7. スキーマ定義とデータ取り込み
  8. ダッシュボードの作成

1. IAM Roleの作成

  • EC2として構築するLogstashに割り当てるIAM Roleを作成します。
  • RoleForLogstashを作成し、以下の2つのIAM Policyを割り当てます。
CloudWatchLogsReadOnlyAccess
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "logs:Describe*",
                "logs:Get*",
                "logs:List*",
                "logs:StartQuery",
                "logs:StopQuery",
                "logs:TestMetricFilter",
                "logs:FilterLogEvents"
            ],
            "Effect": "Allow",
            "Resource": "*"
        }
    ]
}
AmazonEC2RoleforSSM
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ssm:DescribeAssociation",
                "ssm:GetDeployablePatchSnapshotForInstance",
                "ssm:GetDocument",
                "ssm:DescribeDocument",
                "ssm:GetManifest",
                "ssm:GetParameters",
                "ssm:ListAssociations",
                "ssm:ListInstanceAssociations",
                "ssm:PutInventory",
                "ssm:PutComplianceItems",
                "ssm:PutConfigurePackageResult",
                "ssm:UpdateAssociationStatus",
                "ssm:UpdateInstanceAssociationStatus",
                "ssm:UpdateInstanceInformation"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ssmmessages:CreateControlChannel",
                "ssmmessages:CreateDataChannel",
                "ssmmessages:OpenControlChannel",
                "ssmmessages:OpenDataChannel"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2messages:AcknowledgeMessage",
                "ec2messages:DeleteMessage",
                "ec2messages:FailMessage",
                "ec2messages:GetEndpoint",
                "ec2messages:GetMessages",
                "ec2messages:SendReply"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "cloudwatch:PutMetricData"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstanceStatus"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ds:CreateComputer",
                "ds:DescribeDirectories"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:DescribeLogGroups",
                "logs:DescribeLogStreams",
                "logs:PutLogEvents"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetBucketLocation",
                "s3:PutObject",
                "s3:GetObject",
                "s3:GetEncryptionConfiguration",
                "s3:AbortMultipartUpload",
                "s3:ListMultipartUploadParts",
                "s3:ListBucket",
                "s3:ListBucketMultipartUploads"
            ],
            "Resource": "*"
        }
    ]
}

【補足】
・ 1つ目はLogstashがCloudWatch Logsのログを取得するための権限です。
・ 2つ目はSessionManager経由でLogstashを操作したり、ログ出力するための権限です。
・ ロググループを限定する場合は、Resoureceを限定するポリシーを別途作成します。

2. ロググループの作成

  • CloudWatch > CloudWatch Logs > Log groupsでロググループの作成をおこないます。
  • ロググループ名は好きな名前をつけましょう。(今回はSessionActivityLogsとしています) image.png

【補足】
・ 保持期間はデフォルト無期限ですが、必要に応じて変更してください。
・ ログの暗号化が必要な場合は、KMS鍵オプションを利用してください。

3. セッションアクティビティログの出力

  • Systems Manager > Session Manager > 設定で設定画面に移動します。
  • 編集ボタンを押して、設定をおこないます。
  • CloudWatch loggingEnableとします。
  • Choose your preferred logging optionStream session logs(デフォルト)とします。
  • CloudWatchのロググループから作成したロググループ(SessionActivityLogs)を選択します。
    image.png

  • 最下部の保存ボタンを押して設定を保存します。

【補足】
・ ログを暗号化している場合は、Allow only encrypted CloudWatch log groupにチェックを入れてください。

4.SSM Agentバージョンアップ

  • ログがCloudWatch Logsに出るか、試しに適当なEC2に接続してみましょう。
    image.png

  • すると以下のエラーが出て、接続に失敗します。(SSM Agentのバージョンアップが必要とのこと)

The SSM Agent version installed on this instance doesn't support streaming logs to CloudWatch. Either update the SSM Agent to the latest version, or disable the streaming logs option in your preferences.

  • 2021年2月13日時点のデフォルトのSSM Agentバージョンは3.0.161.0になっています。
    image.png

  • Systems Manager > Run Commandからバージョンをあげます。

  • AWS-UpdateSSMAgentを選択します。
    image.png

  • インスタンスを手動で選択するでアップデートしたいEC2インスタンスを選択します。
    image.png

  • 最下部の実行ボタンを押してバージョンアップを開始します。(数分で完了します)

  • 再度接続にチャレンジします。(バージョンが3.0.655.0になりました)
    image.png

  • 適当にsudo suしたり、改行してみたり、lsしてみたりします。
    image.png

  • なんと、すぐにCloudWatch Logsのロググループに打ったコマンド単位でログが出力されています!!

  • 下記のログはlsした時のものになります。
    image.png

  • CloudWatch Logsに出力されるセッションアクティビティログのフォーマットは以下のようになってました。

項目 値(サンプル) 説明
eventVersion 1.0 ログイベント形式のバージョン
eventTime 2021-02-13T13:23:56Z イベントの発生日時(UTC)
awsRegion us-west-2 EC2の存在するAWSリージョン
target.id i-0e111111111222xxx 操作対象EC2のインスタンスID
userIdentity.arn arn:aws:iam::AWSアカウント:user/IAMユーザ 操作しているユーザのARN
runAsUser ssm-user EC2のOSへログインしたユーザ名
sessionId hibino-0d95217a3276b8641 SessionManagerのセッションID
sessionData "sh-4.2$ sudo su" OSで実行したコマンドとその戻り値
  • Amazon ESを使って、sessionDataを全文検索することでユーザ操作の監査がかなりの精度で出来そうですね!

5. Amazon ESのドメイン作成

  • 下記内容でAmazon ESのドメインを作成します。
項目
リージョン us-west-2
デプロイタイプ 開発およびテスト
Elasticsearchのバージョン 7.9 (latest)
Elasticsearchのドメイン test-es
インスタンスタイプ t3.small.elasticsearch
ノードの数 1
データノードのストレージタイプ EBS
EBS ボリュームタイプ 汎用(SSD)
EBS ボリュームサイズ 10 GiB
自動スナップショットの開始時間 00:00 UTC (デフォルト)
ネットワークアクセス パブリックアクセス
細かいアクセスコントロールを有効化 無効
KibanaのSAML認証 無効
Amazon Cognito認証を有効化 無効
ドメインアクセスポリシー カスタムアクセスポリシー (IPv4アドレス)
ドメインへのすべてのトラフィックにHTTPSを要求 有効
ノード間の暗号化 有効
保管時のデータの暗号化の有効化 有効
KMSマスターキー (デフォルト) aws/es
  • アクセスポリシーに追加するIPは、Amazon ESにアクセスする自宅IPとLogstashのIPになります。
ドメインアクセスポリシー
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "*"
      },
      "Action": "es:*",
      "Resource": "arn:aws:es:us-west-2:<AWSアカウント>:domain/test-es/*",
      "Condition": {
        "IpAddress": {
          "aws:SourceIp": [
            "<自宅IP>/32",
            "<LogstasのEIP>/32"
          ]
        }
      }
    }
  ]
}

6. Logstashの構築

  • Logstashの構築手順は、Amazon Corretto 11で構築するLogstashになります。
  • 下記のパイプライン構成ファイル(/etc/logstash/conf.d/logstash.conf)を作成します。
    (このタイミングではLogstashのプロセスは起動しません)
$ sudo vi /etc/logstash/conf.d/logstash.conf
  • 下記の内容を上記のパイプライン構成ファイルに記述します。
logstash.conf
input {
  ## CloudWatch LogsのSessionActivityLogsのロググループから5秒間隔でログ取得
  cloudwatch_logs {
    log_group => [ "SessionActivityLogs" ]
    region => "us-west-2"
    interval => 5
  }
}

filter {
  ### JSONパーサーによる解析
  json {
    source => "message"
  }
  ### dateフィールドから@timestampを抽出
  date {
    match => [ "eventTime", "ISO8601" ]
    timezone => "UTC"
    target => "@timestamp"
  }
  ### @timstampから日本時間を抽出
  ruby {
    code => "event.set('[@metadata][local_time]',event.get('[@timestamp]').time.localtime.strftime('%Y-%m-%d'))"
  }
  ### document_idに利用する一意のIDを作成
  fingerprint {
    source => "message"
    target => "[@metadata][fingerprint]"
    method => "MURMUR3"
  }
  ### フィールドの追加と削除
  mutate {
    ### typeフィールドを追加
    add_field => { "type" => "log-aws-ssm-sessionlog" }    
    ### 不要なフィールドを削除
    remove_field => [ "eventTime", "message" ]
  }
}

output {
  ### 出力先のAmazonESのIndexを指定
  elasticsearch {
    hosts => [ "https://search-test-es-xxxxxxxxxxxxxxxxxxxxxx.us-west-2.es.amazonaws.com:443" ]
    index => "%{type}-%{[@metadata][local_time]}"
    document_id => "%{[@metadata][fingerprint]}"
    ilm_enabled => false
  }
}

【参考】
cloudwatch-logs input
json filter
date filter
ruby filter
fingerprint filter
mutate filter
elasticsearch output

7. スキーマ定義とデータ取り込み

  • Amazon ESのKibanaのURLをクリックします。
    image.png

  • Kibanaの初回アクセス時に下記の画面が表示されます。

  • サンプルデータを使用しないので、Explore on my ownをクリックします。
    image.png

  • Consoleをクリックします。
    image.png

  • Consoleからセッションアクティビティログ用のIndex Templateを追加します。

  • "acknowledged": trueとなれば、Index Templeteの登録はOKです。
    image.png

  • 上記で張り付けたIndex Templateは以下の通りです。

index_templete_log-aws-ssm-sessionlog
PUT _template/log-aws-ssm-sessionlog
{
  "index_patterns": ["log-aws-ssm-sessionlog-*"],
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas" : 1
  },
  "mappings": {
    "properties": {
      "@timestamp": {
        "type": "date"
      },
      "@version" : {
        "type" : "keyword"
      },
      "eventVersion" : {
        "type" : "keyword"
      },
      "awsRegion" : {
        "type" : "keyword"
      },
      "target.id" : {
        "type" : "keyword"
      },
      "userIdentity.arn" : {
        "type" : "keyword"
      },
      "runAsUser" : {
        "type" : "keyword"
      },
      "sessionId" : {
        "type" : "keyword"
      },
      "sessionData" : {
        "type": "text",
        "fields": {
          "keyword": {
            "type": "keyword",
            "ignore_above": 8191
          }
        }
      },
      "type" : {
        "type" : "keyword"
      },
      "tags" : {
        "type" : "keyword"
      },
      "cloudwatch_logs" : {
        "properties" : {
          "event_id" : {
            "type" : "keyword"
          },
          "ingestion_time" : {
            "type" : "date"
          },
          "log_group" : {
            "type" : "keyword"
          },
          "log_stream" : {
            "type" : "keyword"
          }
        }
      }
    }
  }
}

【補足】
・ sessionDataフィールドは全文検索することが想定されるためtext型としています。
・ Kibanaで時系列データとして扱うための時刻フィールドは@timestampとしています。
・ 他フィールドは完全一致検索やKibanaの可視化に利用するためkeyword型としています。

  • ここでLogstashを起動します。
logstash_start
$ sudo systemctl start logstash
$ sudo systemctl status logstash
● logstash.service - logstash
   Loaded: loaded (/etc/systemd/system/logstash.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2021-02-13 14:54:43 UTC; 1s ago
 Main PID: 32506 (java)
   CGroup: /system.slice/logstash.service
           └─32506 /bin/java -Xms1g -Xmx1g -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=75 -XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true -Dfile.encoding=UTF-8 -Djruby.compile.invok...

Feb 13 14:54:43 ip-172-31-12-56.us-west-2.compute.internal systemd[1]: Started logstash.
Feb 13 14:54:43 ip-172-31-12-56.us-west-2.compute.internal systemd[1]: Starting logstash...
Feb 13 14:54:43 ip-172-31-12-56.us-west-2.compute.internal logstash[32506]: OpenJDK 64-Bit Server VM warning: Option UseConcMarkSweepGC was deprecated in version 9.0 and will likely be removed in a...re release.
Hint: Some lines were ellipsized, use -l to show in full.
  • GET _cat/indices/log-aws-ssm-sessionlog*を実施し、インデックスが生成されていることを確認します。
    image.png

  • [Stack Management] > [Index Patterns]でCreate index patternをクリックします。
    image.png

  • log-aws-ssm-sessionlog-*という名前でIndex Patternを作成します。
    image.png

  • @timestampを指定し、Create index patternをクリックします。。
    image.png

  • [Discover]を開き、Index Patternにlog-aws-ssm-sessionlog-*を指定します。

  • デフォルトでは直近15分間のログが表示されます。15分で表示されない場合はTodayとかにしてみましょう。
    image.png

  • 取り込んだログは以下のような感じです。

{
  "_index": "log-aws-ssm-sessionlog-2021-02-13",
  "_type": "_doc",
  "_id": "2962024441",
  "_version": 1,
  "_score": null,
  "_source": {
    "runAsUser": "ssm-user",
    "cloudwatch_logs": {
      "ingestion_time": "2021-02-13T15:15:50.046Z",
      "log_group": "SessionActivityLogs",
      "log_stream": "hibino-07ffa10afb273b008",
      "event_id": "35976216682055508680804654953801519308351663213472448512"
    },
    "eventVersion": "1.0",
    "@version": "1",
    "type": "log-aws-ssm-sessionlog",
    "sessionData": [
      "\u001b]0;root@ip-172-31-12-56:/usr/bin\u0007[root@ip-172-31-12-56 bin]# ",
      "\u001b]0;root@ip-172-31-12-56:/usr/bin\u0007[root@ip-172-31-12-56 bin]# "
    ],
    "target": {
      "id": "i-0e111111111222xxx"
    },
    "sessionId": "hibino-07ffa10afb273b008",
    "awsRegion": "us-west-2",
    "@timestamp": "2021-02-13T15:15:50.000Z",
    "userIdentity": {
      "arn": "arn:aws:iam::123456789012:user/hibino"
    }
  },
  "fields": {
    "@timestamp": [
      "2021-02-13T15:15:50.000Z"
    ],
    "cloudwatch_logs.ingestion_time": [
      "2021-02-13T15:15:50.046Z"
    ]
  },
  "sort": [
    1613229350000
  ]
}

8. ダッシュボードの作成

  • まず、[Discover]で下記のフィールドをAddをします。
  • awsRegionrunAsUsersessionIdtarget.iduserIdentity.arnSelected fieldsにあることを確認します。
  • 以下のようにDiscoverで表示されるログの表示項目が変わったことが分かります。
    image.png

  • 画面上部のSaveボタンでこの検索条件を保存します。
    image.png

  • 好きな名前(今回はsessionlog_search)をつけて保存します。
    image.png

  • 次は、[Visualize]を開き、Create new visualizationをクリックします。
    image.png

  • グラフの中から棒グラフ(Vertical Bar)を指定します。
    image.png

  • log-aws-ssm-sessionlog-*を指定します。
    image.png

  • Custom labelログ件数と入力します。(Y軸の名前)
    image.png

  • BucketsのAddをクリックし、X-axisを選択します。(X軸を設定します)
    image.png

  • 順に選択し、Updateボタンをクリックします。(X軸を@timestampの値を時単位で設定)
    image.png

  • 適当にLast 24 hoursを指定し、棒グラフの見栄えを整えます。
    image.png

  • 上部のSaveボタンをクリックし、適当な名前をつけて棒グラフを保存します。
    image.png

  • 次は、同じ要領で表グラフ(Data Table)を作ります。
    image.png

  • Custom labelログ件数と入力します。(表のカラム名)
    image.png

  • BucketsのAddをクリックし、Split rowsを選択します。(特定のフィールドでグループ化する)
    image.png

  • 順に選択し、Updateボタンをクリックします。(awsRegionフィールド値の上位10位を表示)
    image.png

  • AWSリージョンごとの表グラフになったことを確認し、Saveボタンをクリックして保存します。(適当な名前をつける)
    image.png

  • FieldCustom labelを変更し、Updateボタンをクリックします。
    image.png

  • Save as new visualizationのチェックをオンにし、別名をつけて表グラフを保存します。
    image.png

  • 上記と同様にsessionIdtarget.iduserIdentity.arnで表グラフを作成し、別名で保存します。

  • 次は、同じ要領で円グラフ(Pie)を作ります。
    image.png

  • Custom labelログ件数と入力します。
    image.png

  • BucketsのAddをクリックし、Split rowsを選択します。(特定のフィールドでグループ化する)
    image.png

  • 順に選択し、Updateボタンをクリックします。(awsRegionフィールド値の上位10位を表示)
    image.png

  • AWSリージョンごとの円グラフになったことを確認し、Saveボタンをクリックして保存します。(適当な名前をつける)
    image.png

  • 同様にrunAsUsersessionIdtarget.iduserIdentity.arnで表グラフを作成し、別名で保存します。

  • 最後に、[Dashboard]を開き、Create new dashboardをクリックします。
    image.png

  • Add an existingをクリックします。
    image.png

  • これまで作成した検索条件とグラフを選択します。
    image.png

  • 上部に棒グラフ、次に円グラフ、その下に表グラフ、最下部に検索条件を配置し、Saveボタンで保存します。
    image.png
    image.png

  • 適当な名前をつけて、ダッシュボードを保存します。

  • Store time with dashboardをオンにすると指定していた時間の範囲も合わせて保存します。
    image.png

まとめ

とてつもなく長い投稿になってしまいましたが、いかがでしたでしょうか?

私がKibanaでよく利用するグラフは、棒グラフ、表グラフ、円グラフです。
それ以外は、ログ監査ではあまり利用していないのが実態です。

このダッシュボードの作りだと、各フィールドの値でフィルタリング(+または-)することが出来ます。
フィルタリングすることで再検索がかかり、見つけたいデータや情報を絞り込んでいくことが可能です。

これは使える!!と思われた方、ぜひLGTMを^^v

5
2
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
5
2