はじめに
AWSアカウント内のリソースの操作について過去ログを辿りたい時、皆さんCloudTrailを使っていると思います。
しかしCloudTrailのコンソール画面からだと検索できるのは過去3か月であり、一部の操作ログはそこから検索できなかったりもします。
そんな時はAthenaを使用して、CloudTrailのログを検索しましょう。
CloudTrailのログの実体はS3に保存されているので、Athenaでテーブルを作成することでS3に保存されたログを検索することができます。
テーブル作成
以下のDDLを、虫食い部分を埋めてAthenaで実行すればOKです。
CREATE EXTERNAL TABLE IF NOT EXISTS `default`.`cloudtrail_logs` (
`eventversion` string,
`useridentity` STRUCT <
type: STRING,
principalid: STRING,
arn: STRING,
accountid: STRING,
invokedby: STRING,
accesskeyid: STRING,
userName: STRING,
sessioncontext: STRUCT <
attributes: STRUCT <
mfaauthenticated: STRING,
creationdate: STRING
>,
sessionissuer: STRUCT <
type: STRING,
principalId: STRING,
arn: STRING,
accountId: STRING,
userName: STRING
>,
ec2RoleDelivery: string,
webIdFederationData: map <
string,
string >
>
>,
`eventtime` string,
`eventsource` string,
`eventname` string,
`awsregion` string,
`sourceipaddress` string,
`useragent` string,
`errorcode` string,
`errormessage` string,
`requestparameters` string,
`responseelements` string,
`additionaleventdata` string,
`requestid` string,
`eventid` string,
`resources` array <
STRUCT <
arn: STRING,
accountid: STRING,
type: STRING
>
>,
`eventtype` string,
`apiversion` string,
`readonly` string,
`recipientaccountid` string,
`serviceeventdetails` string,
`sharedeventid` string,
`vpcendpointid` string,
`tlsDetails` struct <
tlsVersion: string,
cipherSuite: string,
clientProvidedHostHeader: string
>
)
PARTITIONED BY (region string, date string)
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://{対象のバケット名}/AWSLogs/{AWSアカウントID}/'
TBLPROPERTIES (
'projection.enabled' = 'true',
'projection.date.type' = 'date',
'projection.date.range' = '{任意の開始日(yyyy/MM/dd形式で)},NOW',
'projection.date.format' = 'yyyy/MM/dd',
'projection.date.interval' = '1',
'projection.date.interval.unit' = 'DAYS',
'projection.region.type' = 'enum',
'projection.region.values'='us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2,eu-south-1,eu-west-3,eu-north-1,me-south-1,sa-east-1',
'storage.location.template' = 's3://{対象のバケット名}/AWSLogs/{AWSアカウントID}/CloudTrail/${region}/${date}',
'classification'='cloudtrail',
'compressionType'='gzip',
'typeOfData'='file',
'classification'='cloudtrail'
);
虫食い箇所
-
{対象のバケット名}
: CloudTrailログが保存されているS3バケット名 -
{AWSアカウント番号}
: 作業しているAWSのアカウントID -
{任意の開始日(yyyy/MM/dd形式で)}
: CloudTrailログの保存開始日
実際に埋めるとこんな感じになります。
s3://my-cloudtrail-logs-bucket/AWSLogs/123456789012/
'projection.date.range' = '2019/01/01,NOW'
パーティショニングについて詳細解説
PARTITIONED BY (region string, date string)
この部分でパーティションとするキーを指定しています。今回はリージョンと日付。
S3に存在するTrailのデータ内にはこの情報はないのですが、S3のPrefixとしてリージョンと日付が切られた形で出力されているため、このようなパーティショニングが可能です。
'projection.date.type' = 'date',
'projection.date.range' = '{任意の開始日(yyyy/MM/dd形式で)},NOW',
'projection.date.format' = 'yyyy/MM/dd',
'projection.date.interval' = '1',
'projection.date.interval.unit' = 'DAYS',
この部分で、カラムとしてのdate
についての設定をしています。見てわかる通りですが、型や日付書式、いつからいつまでの範囲でパーティションを作成するか、などを指定しています。
'projection.region.type' = 'enum',
'projection.region.values'='us-east-1,us-east-2,us-west-1,us-west-2,af-south-1,ap-east-1,ap-south-1,ap-northeast-2,ap-southeast-1,ap-southeast-2,ap-northeast-1,ca-central-1,eu-central-1,eu-west-1,eu-west-2,eu-south-1,eu-west-3,eu-north-1,me-south-1,sa-east-1',
次はregion
についての設定です。リージョンはenum型で、この値でパーティションが切られているよという指定です。
ちなみにここで指定する各リージョンを示す文字列は間違っていても問題はなく、単純にその値で検索してもヒットするものが0件になります。(パーティションが正しく認識されないため)
上記 CREATE EXTERNAL TABLE 文を実行した際も、 SELECT 文を実行した際もエラーとはならないため、ご注意を。
'storage.location.template' = 's3://{対象のバケット名}/AWSLogs/{AWSアカウントID}/CloudTrail/${region}/${date}',
最後に、パーティションの位置についての設定です。
${region}
や${date}
の部分で、これまで設定してきたパーティションの値はS3のパス上のココに入るよ、ということを示しています。
上記のようなパーティショニングが無いとTrailのログを全て読み込むことになり、非常にAthenaの利用料が高くなります。
検索例文
WHERE句に対象期間は必ず入れましょう!
そうしないとAthenaの利用料が大爆発します。
そもそも検索も遅くなってしまうので、逆に検索したらやけに遅いな…という場合は正しくパーティションを使えていない可能性があるので急ぎ、実行をキャンセルしましょう。
操作の内容で検索
SELECT * FROM cloudtrail_logs
WHERE eventname = 'CreateBucket'
AND date BETWEEN '2021-01-01' AND '2021-01-31'
あの操作の詳細情報が見たい、このリソースを作ったのは誰だ?なんて時に。
サービスの種類で検索
SELECT * FROM cloudtrail_logs
WHERE eventsource = 's3.amazonaws.com'
AND date BETWEEN '2021-01-01' AND '2021-01-31'
サービス単位で操作を追いたい時に。
特定ユーザーの操作すべてを検索
SELECT * FROM cloudtrail_logs
WHERE useridentity.userName = 'myuser'
AND date BETWEEN '2021-01-01' AND '2021-01-31'
ストーキング…
特定のリソース名で検索
正直これはオススメしません。
なぜなら、サービスごとにどのようなキーに対してどういう値で検索すれば良いかが異なるため、調査や試行錯誤に無駄に時間を取られがちです。
このリソースは誰が作ったんだ?という場合は、そのリソースを作成するAPIの機能名で検索するのが良いでしょう。(操作の内容で検索)
一応、特定のLambda関数を例にした検索クエリを以下に記載します。
SELECT * FROM cloudtrail_logs
WHERE requestParameters like '%arn:aws:lambda:ap-northeast-1:123456789012:function:my-function%'
AND date BETWEEN '2021-01-01' AND '2021-01-31'
まとめ
今回のパーティションの追加の仕方は、Partition Projectionという方式を使っています。
手動でパーティションを追加していく方法もありますが、これは非常に手間がかかるので、せめてGlueのCrawlerを使わないとやってられないです…
それでは、よきTrail検索ライフを。