以前に、CloudTrailのログを別のAWSアカウントのS3バケットに転送する方法を紹介しました。
今回はCloudTrailを設定しているAWSアカウントのAthenaからそのS3バケットに検索する方法を紹介します。
…が、正直言ってあまりオススメできる方法ではないので、可能であるならばS3バケットと同じAWSアカウントのAthenaから検索することをおすすめします。
目次
Athenaテーブル定義
例として東京リージョンかつ2018年12月のログを検索するテーブルを定義します。
CloudTrailログの格納先のS3バケット名は cloudtrail-test
とします。'has_encrypted_data'='true'
で暗号化していますが、これは必須ではありません。
CREATE EXTERNAL TABLE ap_northeast_1_201812 ( 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>>>, 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 ) COMMENT 'CloudTrail table for cloudtrail-test bucket' ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde' STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://cloudtrail-test/AWSLogs/123456789123/CloudTrail/ap-northeast-1/2018/12' TBLPROPERTIES ('classification'='cloudtrail','has_encrypted_data'='true');
S3バケットポリシーの変更
こちらに書かれているバケットポリシーを参考に設定します。すでにCloudTrail用の設定がありますので、それと上手くマージしてください。
こんな感じになります。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AWSCloudTrailAclCheck20150319", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::cloudtrail-test" }, { "Sid": "AWSCloudTrailWrite20150319", "Effect": "Allow", "Principal": { "Service": "cloudtrail.amazonaws.com" }, "Action": "s3:PutObject", "Resource": [ "arn:aws:s3:::cloudtrail-test/AWSLogs/123456789123/*" ], "Condition": { "StringEquals": { "s3:x-amz-acl": "bucket-owner-full-control" } } }, { "Sid": "EnableAccessFromAthena", // Athenaからアクセスする権限付与 "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::123456789123:root" }, "Action": [ "s3:GetBucketLocation", "s3:GetObject", "s3:ListBucket", "s3:ListBucketMultipartUploads", "s3:ListMultipartUploadParts", "s3:AbortMultipartUpload", "s3:PutObject" ], "Resource": [ "arn:aws:s3:::cloudtrail-test", "arn:aws:s3:::cloudtrail-test/*" ] } ] }
普通のクロスアカウントであればこれで上手くいくのですが、CloudTrailログの場合だと上手くいきません。
以下のようにクエリを実行するとS3へのアクセスが拒否されてしまいます。
SELECT * FROM "cloudtrail_logs"."ap_northeast_1_201812" limit 10;
エラー
Your query has the following error(s): com.amazonaws.services.s3.model.AmazonS3Exception: Access Denied (Service: Amazon S3; Status Code: 403; Error Code: AccessDenied; Request ID: B40611520649A9F7; S3 Extended Request ID: lYIpHpayYEeVqMQoG1mcRCCbuGgk6j2CxXJpKj+uVrzaseY8/N5RKGLWG7UYja8y5r5oPHcaXBU=), S3 Extended Request ID: lYIpHpayYEeVqMQoG1mcRCCbuGgk6j2CxXJpKj+uVrzaseY8/N5RKGLWG7UYja8y5r5oPHcaXBU= (Path: s3://cloudtrail-test/AWSLogs/123456789123/CloudTrail/ap-northeast-1/2018/12/04/123456789123_CloudTrail_ap-northeast-1_20181204T0215Z_MDyrjIQRhqyZcGRk.json.gz) This query ran against the "cloudtrail_logs" database, unless qualified by the query. Please post the error message on our forum or contact customer support with Query Id: 199bcb82-b9ae-4ab8-9e83-cd8845325637.
CloudTrailログのS3オブジェクトACL
実はCloudTrailログの場合、上記のS3バケットポリシーだけでは不十分なのです。
なぜならS3の仕様として、S3バケットにファイル(S3オブジェクト)をアップロードしたAWSアカウントがそのS3オブジェクトの所有者となり、デフォルトではそのAWSアカウントしかアクセスができません(デフォルトで他のAWSアカウントからアクセスできちゃうと漏洩事故になってしまいますよね?)
今回の場合、cloudtrail-test
バケットにログをアップロードしているAWSアカウントはCloudTrail自身 *1 であるため、そのログの所有者も CloudTrail自身になります。
CloudTrailログのS3オブジェクトACLを見てみると分かります。
$ aws s3api get-object-acl --bucket cloudtrail-test --key AWSLogs/123456789123/CloudTrail/ap-northeast-1/2018/12/04/123456789123_CloudTrail_ap-northeast-1_20181204T0150Z_ERB1ETgQgc0XuBnZ.json.gz { "Owner": { "DisplayName": "aws_cloudtrail_ap-northeast-1", "ID": "9e6b02405e2933107e2d7409d7a22ca772ae1eecf4677a27517b9ec21d248d9b" }, "Grants": [ { "Grantee": { "Type": "CanonicalUser", "DisplayName": "aws_cloudtrail_ap-northeast-1", "ID": "9e6b02405e2933107e2d7409d7a22ca772ae1eecf4677a27517b9ec21d248d9b" }, "Permission": "FULL_CONTROL" }, { "Grantee": { "Type": "CanonicalUser", "DisplayName": "foobar", "ID": "xxxxxxu" }, "Permission": "FULL_CONTROL" } ] }
このS3オブジェクトの所有者は aws_cloudtrail_ap-northeast-1 になっています。このままだとS3バケットの所有者(foobar)がアクセスできないため、フルコントロールが付与されています。
したがって、S3オブジェクト所有者でもない、S3バケット所有者でもない、別のAWSアカウント(Athenaを使いたい)からこのS3オブジェクトにアクセスすることができないのです。
S3オブジェクトACLの付与
そこで、一つひとつのS3オブジェクトに対して、Athenaを実行するAWSアカウントから読み込みできるようにオブジェクトACLを付与する必要があります。
以下のコマンドで可能です。
aws s3 cp \ s3://cloudtrail-test/AWSLogs/123456789123/CloudTrail/ap-northeast-1/2018/12 \ s3://cloudtrail-test/AWSLogs/123456789123/CloudTrail/ap-northeast-1/2018/12 \ --recursive \ --grants full=id=<CloudTrailの開アカウントID>,id=<S3バケット所有者のアカウントID> \ read=id=<Athena検索するアカウントID>
問題点としては、一つひとつのS3オブジェクトに対してACLを設定していくため、時間がかかりますし、COPY操作に対する料金がかかってしまいます。こんな面倒なことをするなら、CloudTrailとAthenaを同じAWSアカウントで実行したほうがよい気がしますね。
まとめ
別AWSアカウントのCloudTrailログをAthenaから検索するためのS3の設定方法を紹介しました。バケットポリシーとS3オブジェクトACLを設定することで検索可能になりますが、設定するのに時間とお金がかかってしまいますので、素直に同じAWSアカウントで検索するようにしたほうがよいでしょう。
参考文献
今日の一言
世の中には「弓道警察」というのがいまして、弓を引いている漫画やイラスト、実動画・写真をみて、「あの射型 *2 がおかしい」「○○ができていない」と一々ツッコミをしていく人たちのことです。
弓道警察とは (キュウドウケイサツとは) [単語記事] - ニコニコ大百科
弓道経験がある私ですが、たまに漫画とかでツッコミしがいがある描写もしばしば見かけますが、一々つっかかるようなことでもないですし、他人の射型を指摘するほど、あなたは上手いの?偉いの?と思ってしまいたくなりますね。弓道も流派が数多くいますし、指導者によって教えていることがまったく違うこともありますので、絶対に正しい射というのはないと思います。他人のことを突っ込む前にまずは自分のことを顧みてはいかがでしょうか、と言いたい今日このごろです。
*1:S3バケットを所有しているAWSアカウントでもなければ、CloudTrailを設定しているAWSアカウントでもない、第三者のAWSアカウントです
*2:弓道の型のこと。弓道には射法八節というのがあり、これに従って弓をひきます。 http://www.kyudo.jp/howto/syaho.html