Amazon EFSをマウントしているのは誰だ?

2021.02.25

NFS ファイルシステムのマネージド・サービス Amazon EFS は EC2/Lambda/Fargate など複数のサービスから利用することができます。

Amazon EFSが活用が広まるにつれ、EFSをマウントしているクライアントを把握したことがあります。

そのようなときは、VPC フローログを有効化し、ログからEFS(NFS)用ポート(2049)に通信しているクライアントを特定します。

本ブログでは、次のナレッジベースを参考に、VPCフローログを CloudWatch Logs・S3 それぞれに出力したケースに置いて、クライアントを絞り込む方法を紹介します。

Amazon EFS に接続されている EC2 インスタンスを確認する

構成図

VPC フローログの有効化

次のドキュメントを参考に、VPC フローログを有効化し、CloudWatch Logs または S3 に出力します。

VPC フローログ - Amazon Virtual Private Cloud

Log record formatは次の2種類から選べます。

  • AWS default format
  • Custom format

まずはシンプルなデフォルトフォーマットを検討し、要件を満たせない場合は、カスタムフォーマットを検討することをおすすめします。

以下の探索では、デフォルトフォーマットを前提とします。

CloudWatch Logs のフローログを探索

CloudWatch コンソールから、フローログが CloudWatch Logs に出力されていることを確認します。

コンソールから確認

CloudWatch Logs Insights で ポート 2049 へのフローログを探索します。

ソースIPの件数ごとにソートする場合、次のクエリーを実行します。

filter dstPort="2049"
 | stats count(*) as FlowLogEntries by srcAddr
 | sort FlowLogEntries desc

コマンドラインから確認

コマンドラインから探索する場合、Logs::StartQuery API を利用します。

クエリーは非同期に実行されるため、クエリーをキューイング後、少し間をおいてから Logs::GetQueryResults でクエリー結果を取得します。

クエリーのキューイングからクエリー結果の取得までをまとめたのが、次のコマンドです。

$ aws logs start-query \
  --log-group-name LOG-GROUP-NAME \
  --start-time 1614174045 \ 
  --end-time 1614174645 \ 
  --query-string 'filter dstPort="2049" | stats count(*) as FlowLogEntries by srcAddr| sort FlowLogEntries desc' > test.json && \
  sleep 10 && \
  jq .queryId test.json | xargs aws logs get-query-results --query-id

{
    "status": "Complete",
    "statistics": {
        "recordsMatched": 37.0,
        "recordsScanned": 874.0,
        "bytesScanned": 122012.0
    },
    "results": [
        [
            {
                "field": "srcAddr",
                "value": "172.31.33.180"
            },
            {
                "field": "FlowLogEntries",
                "value": "19"
            }
        ],
        [
            {
                "field": "srcAddr",
                "value": "172.31.41.13"
            },
            {
                "field": "FlowLogEntries",
                "value": "18"
            }
        ]
    ]
}
  • ログ探索範囲は --start-time/--end-time 引数に UNIX TIME で指定します。
  • --query-string にはコンソールと同じクエリーを指定します
  • クエリーの実行は非同期に実行されます。完了までの待ち時間は sleep 10 で調整しています。

S3 のフローログを探索

S3 コンソールから、フローログが S3 バケット の次のパス以下に出力されていることを確認します。

s3://BUCKET-NAME/prefix/AWSLogs/{account_id}/vpcflowlogs/{region_code}/YYYY/MM/dd/

Athena テーブルの作成

次のドキュメントを参考に、VPC Flow Logs用のAthenaテーブルを作成します。

Amazon VPC フローログのクエリ - Amazon Athena

Athena でS3のフローログを探索

テーブルを定義したあとは、SQL で問い合わせるだけです。

先程の CloudWatch Insights のクエリーを Athena に置き換えます。

SELECT sourceaddress,
         count(*) AS FlowLogEntries
FROM vpc_flow_logs
WHERE destinationport = 2049
        AND starttime >= 1614174045
        AND endtime <= 1614174645
        AND date = DATE('2021-02-24')
GROUP BY  sourceaddress
ORDER BY  FlowLogEntries desc;

startdate/enddate だけを指定するとフルスキャンになるため、パーティション化されたdate も WHERE 条件に渡すと良いでしょう。

インスタンスIDを特定したいときはカスタムフィールドを利用

Log record formatにAWSデフォルトフォーマットを指定すると、VPC Flow Logs からは Amazon Elastic Network Interfaces(ENI)単位のフローログしかわかりません。

調査目的によっては、ENI(ソースIP)を利用しているインスタンスIDを特定したいこともあるでしょう。

そのようなときは、VPC Flow Logs作成時にカスタムフォーマットを選択し、${interface-id}${dstport} を出力させます。

filter @message like /2049/
 | stats count(*) as FlowLogEntries by @message

のようにすれば、NFS 通信しているクライアントをインスタンスIDで類別できます。

ただし、Lambda など非EC2インスタンスはインスタンスIDが「-」で出力され、識別できないことにご留意ください。

まとめ

EFS を利用しているクライアントを特定するにはVPC Flow Logsを利用します。 CloudWatch Logs に出力されたログは CloudWatch Insightsで探索し、S3に出力されたログは Athena で探索します。

インスタンスIDなど、デフォルトフォーマットに存在しないフィールドが必要な場合は、ログフォーマットをカスタマイズしてください。

それでは。

参考