#author("2020-10-16T13:07:15+09:00","default:dex","dex")
#author("2020-12-23T18:50:34+09:00","default:dex","dex")
#contents

----
* CloudTrailのログをAthena で検索する [#f36f371f]

- Athenaはスキャンしたデータサイズによって課金される。1回のスキャンで1TBあたり$5。
- AWSアカウントID、リージョン、年、月、日等で、パーティションを作成し1回のスキャン量を減らす。

- [[AWS CloudTrail ログのクエリ - Amazon Athena:https://docs.aws.amazon.com/ja_jp/athena/latest/ug/cloudtrail-logs.html]]
- [[AWS CloudTrail ログを検索するために Amazon Athena でテーブルを作成する:https://aws.amazon.com/jp/premiumsupport/knowledge-center/athena-tables-search-cloudtrail-logs/]]
-- CloudTrailの管理画面からAthenaのTableを作成すると、パーティションが設定されない。よって、日時の絞り込みが出来ず、スキャン量が大きくなり料金も高くなる。
-- 「CREATE TABLE」「DROP TABLE」「SELECT」等すべて「start-query-execution」で非同期に実行する
-- ワークグループ選択 > 詳細を表示 > データ使用状況の制御タブ > データの制限で1回のスキャンの上限を設定できる。1000MBにした場合は、0.005 USD(0.53円)に抑えられる。
-- 「no viable alternative at input」が出る場合は、CREATE TABLE時のパラメータ順序が間違っている。「COMMENT」の後に「PARTITIONED BY」を指定する


- 記事
-- [[AWS CloudTrail ログ検索をするために Amazon Athena のテーブルを自動的に作成する方法を教えてください。:https://aws.amazon.com/jp/premiumsupport/knowledge-center/athena-tables-search-cloudtrail-logs/]] 
-- [[Amazon Athena で no viable alternative at input への対処法 - Qiita:https://qiita.com/szk3/items/0f048add7e0b61bc650d]]
-- [[AWS CloudTrailの証跡情報をAmazon Athenaで集計してみた | Developers.IO:https://dev.classmethod.jp/articles/cloudtrail-athena-aggre/]]

----
** クエリの例 [#rc0aa15e]

- IAM userのイベントだけにしたい。AWSがAssumeRoleで自動的に実行するイベントは除外したい
#geshi(sql){{
where
  userIdentity.arn != ''
  AND userIdentity.username != 'null'
}}

- Create系のイベント(EC2の起動も)を抽出したいが、CreateLogStream, CreateLogGroupが多いので除外したい。
#geshi(sql){{
where
  regexp_like(eventName, '^(Create|RunInstances)')
  and not(regexp_like(eventName, '^(CreateLog)'))
}}


----
** CloudTrailのログを別アカウントから参照しようとするとエラーになる [#s36b33c0]

※ALB/ELBのログもs3 objectのownerが外部アカウントなので同じエラーが出ると思われる。
- Object Ownership が追加されたので設定したほうが良い。ただし、既存のobjectには適用されない。
-- [[Amazon S3 アップデート – セキュリティおよびアクセス制御のための 3 つの新しい機能 | Amazon Web Services ブログ:https://aws.amazon.com/jp/blogs/news/amazon-s3-update-three-new-security-access-control-features/]]
- [[Object Ownership>Memo/AmazonWebServices/S3#tde3f9f4]] が追加されたので設定すると、「権限が不足しているs3 object」の作成はエラーにできる。


- 記事
-- [[Amazon S3 バケットへの Athena のクロスアカウントアクセス - Amazon Athena:https://docs.aws.amazon.com/ja_jp/athena/latest/ug/cross-account-permissions.html]]
-- [[別AWSアカウントに保存しているCloudTrailログをAthenaから検索する - 本日も乙:https://blog.jicoman.info/2019/01/using-athena-cloudtail-logs-anorher-aws-account/]]

- 目的
-- アカウントA(111111111111)にあるCloudTrailのログを、アカウントB(222222222222)のAthenaから参照したい。

- 現象:
-- アカウントBのAthenaから、アカウントAへのs3 bucketへのqueryが失敗する。
-- s3 lsは成功する。s3 cpが失敗する。
#geshi(text){{
aws s3 ls --profile account-b s3://cloudtrailbucket-example/
# OK

aws s3 cp --profile account-b s3://cloudtrailbucket-example/AWSLogs/111111111111/CloudTrail/ap-northeast-1/2018/01/01/111111111111_CloudTrail_ap-northeast-1_20180101T0000Z_****.json.gz ./

fatal error: An error occurred (403) when calling the HeadObject operation: Forbidden
}}}
-- s3 bucket policyでgetやlistの権限は与えている
#geshi(javascript){{{
        {
            "Sid": "AthenaRead",
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::222222222222:root"
            },
            "Action": [
                "s3:GetBucketLocation",
                "s3:GetObject",
                "s3:ListBucket",
            ],
            "Resource": [
                "arn:aws:s3:::cloudtrailbucket-example",
                "arn:aws:s3:::cloudtrailbucket-example/*"
            ]
        }
}}}

- 理由:
-- 基本はs3 objectのownerしか読み書きできない。
--- [[オブジェクトへのアクセス許可の設定方法 - Amazon Simple Storage Service:https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/user-guide/set-object-permissions.html]]
--- バケットに対するアクセス許可とオブジェクトに対するアクセス許可は相互に独立しています。オブジェクトはバケットからアクセス許可を継承しません。
-- s3 objectのownerがcloudtrail(aws_cloudtrail_ap-northeast-1)になっている。そのままでは、アカウントAのIAM userがs3 objectが見えなくなるので、[[CloudTrailのs3 bucket policy:https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/create-s3-bucket-policy-for-cloudtrail.html]] でアカウントAのオーナー(account-a)にもフルコントロールが与えられている。
#geshi(bash){{
aws s3api get-object-acl --profile account-a --bucket cloudtrailbucket-example --key AWSLogs/111111111111/CloudTrail/ap-northeast-1/2018/01/01/111111111111_CloudTrail_ap-northeast-1_20180101T0000Z_****.json.gz
{
    "Owner": {
        "DisplayName": "aws_cloudtrail_ap-northeast-1",
        "ID": "***"
    },
    "Grants": [
        {
            "Grantee": {
                "DisplayName": "aws_cloudtrail_ap-northeast-1",
                "ID": "****",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        },
        {
            "Grantee": {
                "DisplayName": "account-a",
                "ID": "****",
                "Type": "CanonicalUser"
            },
            "Permission": "FULL_CONTROL"
        }
    ]
}
}}
-- この状態では、アカウントBのIAM userからはs3:GetObjectが失敗する。

- 代案
-- AssumeRoleで、アカウントAのAthenaから、アカウントAのs3を検索する。
--- [[S3保管したCloudTrailログに別アカウントのLambdaからアクセスする | Developers.IO:https://dev.classmethod.jp/articles/cross-access-s3-object-from-lambda/]]
-- 全object ACLにアカウントBのreadを許可する。-> Lambdaやcronで定期実行するなど、別の仕組みが必要で面倒。

- 失敗した方法
-- s3 bucket ACLにアカウントの正規ユーザIDで読み取りを許可する。
#geshi(bash){{
aws s3api list-buckets --query Owner.ID --output text --profile account-b
}}
-- 明示的にIAM権限で許可してみる
--- 結果: Principal にはワイルドカードがpolicy errorになるので、以下の様な指定は出来なかった。userやIAM roleを個別に書けばpolicy errorにはならない。s3 lsは成功するが、s3 cpはエラーで変わらず。
--- [[S3のバケットポリシーでハマったので、S3へのアクセスを許可するPrincipalの設定を整理する | Developers.IO:https://dev.classmethod.jp/articles/summarize-principal-settings-in-s3-bucket-policy/]]
--- s3 bucket policy
#geshi(javascript){{
            "Principal": {
                "AWS": [
                  "arn:aws:iam::222222222222:user/*",
                  "arn:aws:iam::222222222222:role/*"
                ]
            },
}}
-- Principalではなく、ConditionでIAM roleを指定する。
--- 結果: s3 lsは成功するが、s3 cpはエラーで変わらず。
--- [[S3 バケットポリシーの Principal 要素と共に明示的な拒否があるワイルドカードを使用する:https://aws.amazon.com/jp/premiumsupport/knowledge-center/explicit-deny-principal-elements-s3/]]
--- role-id
#geshi(bash){{
aws sts get-caller-identity --profile account-b
{
    "UserId": "AROAID2GEXAMPLEROLEID:botocore-session-1234567890"
...

# または
aws iam get-role --role-name assumerole-readonly --profile account-b --query 'Role.RoleId'
AROAID2GEXAMPLEROLEID
}}
--- s3 bucket policy:
#geshi(javascript){{
            "Principal": {
                "AWS": "*"
            },
...
            "Condition": {
                "StringLike": {
                    "aws:userid": [
                        "222222222222",
                        "AROAID2GEXAMPLEROLEID:*"
                    ]
                }
            }
}}


----
** ログイン監査の作成 [#m6867576]

CloudTrailはAPIのログが残るので、API名(eventName)を知る必要がある。
- [[CloudTrail レコードの内容:https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-event-reference-record-contents.html]]

- ConsoleLogin:
-- [[AWS コンソールのサインインイベント:https://docs.aws.amazon.com/ja_jp/awscloudtrail/latest/userguide/cloudtrail-event-reference-aws-console-sign-in-events.html]]
-- AWS マネジメントコンソール、AWS ディスカッションフォーラム、AWS サポートセンターへのサインイン

- SwitchRole:
-- roleを切り替えた時。複数アカウントある環境で、別アカウントへの切替(AssumeRole)もこのログになる。
-- switch元は「additionalEventData.SwitchFrom」にarnがある

- GetSessionToken, GetFederationToken, AssumeRole, AssumeRoleWithSAML, AssumeRoleWithWebIdentity:
-- [[一時的な認証情報のサインインイベントのログ記録:https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/cloudtrail-integration.html#cloudtrail-integration_signin-tempcreds]]
-- CloudTrailの設定で、「書き込みイベントのみ」の場合はログに残らない。

- 記事
-- [[CloudTrailログからAmazon Athenaを使ってログイン監査レポートを作成する | Developers.IO:https://dev.classmethod.jp/articles/cloudtrail-audit-report-by-amazon-athena/]]
-- [[SwitchRoleした時のCloudTrailで元のIAMユーザをトレースする流れ - Qiita:https://qiita.com/atsumjp/items/096915622a68f81d0cd3]]

サンプル:
#geshi(sql){{
SELECT eventTime,
         eventSource,
         eventName,
         recipientAccountId,
         awsRegion,
         userIdentity.type as userIdentity_type,
         userIdentity.arn as userIdentity_arn,
         sourceIPAddress,
         userAgent
FROM cloudtrail_logs_allregion
WHERE eventName IN ('ConsoleLogin')
        AND date = '2019/01/02'
ORDER BY  eventtime desc limit 10;
}}

----
** organizations [#n886e6be]

- organizationsの場合、s3 bucket pathに「o-abc123」のようなorg idが入る
#geshi(text){{
s3://<bucket>/AWSLogs/<org id>/<accountid>/CloudTrail/<region>/<date>/<object.gz>
}}

----
** 都度ALTER TABLEが必要な基本的な方法 [#zb4d6d5b]

''Partition Projection機能でパーティション管理を自動化する方法は次の項目参照''

- Athena上で、CloudTrail用のTableを作成。CloudTrailの管理画面から作る時のDDL(CloudTrailのイベント履歴 > 「Amazon Athena で高度なクエリを実行~」) + PARTITIONED句

- workgroup, databaseは省略すると、「primary」「default」が使われる。
#geshi(bash){{
ATHENA_TABLE_NAME=cloudtrail_logs_allregion
SOURCE_BUCKET=cloudtrail-example
OUTPUT_BUCKET=aws-athena-query-result-123456789012-ap-northeast-1/cloudtrail-example/output/
AWS_PROFILE=example
AWS_REGION=ap-northeasst-1
AWS_ACCOUNT_ID=123456789012

aws athena start-query-execution --profile $AWS_PROFILE --region $AWS_REGION --result-configuration OutputLocation="s3://$OUTPUT_BUCKET" --query-string \
"CREATE EXTERNAL TABLE $ATHENA_TABLE_NAME (
    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 $SOURCE_BUCKET bucket'
PARTITIONED BY (
  region string,
  year string,
  month string,
  day string
  )
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://$SOURCE_BUCKET/AWSLogs/$AWS_ACCOUNT_ID/CloudTrail/'
TBLPROPERTIES ('classification'='cloudtrail');"
}}

- パーティションを設定。これが無いと全データスキャンになり、コストが高く実行も遅い。全リージョン、指定日(2019-01-02)のみ追加
#geshi(sql){{
PART_YEAR=2019
PART_MONTH=01
PART_DAY=02

for region in $(aws ec2 describe-regions --profile $AWS_PROFILE --query 'Regions[].RegionName' | jq -r '.[]'); do \
aws athena start-query-execution --profile $AWS_PROFILE --region $AWS_REGION --result-configuration OutputLocation="s3://$OUTPUT_BUCKET" --query-string \
"ALTER TABLE $ATHENA_TABLE_NAME ADD PARTITION (region='$region',year='$PART_YEAR',month='$PART_MONTH',day='$PART_DAY') LOCATION 's3://$SOURCE_BUCKET/AWSLogs/$AWS_ACCOUNT_ID/CloudTrail/$region/$PART_YEAR/$PART_MONTH/$PART_DAY/'"; \
done
}}

- 実際のクエリ: 誰が作ったを調べる。
-- 「requestparameters=<resource id>」APIのパラメータの中で、ユニークになりそうなIDを指定する。例:RDSのcreate-db-instance APIなら、db-instance-identifier。
-- region, year, month, dayで絞り込む程、高速、低価格になる。
#geshi(sql){{
SELECT *
FROM cloudtrail_logs_allregion
WHERE (requestparameters LIKE '%dev-web01%')
        AND region = 'ap-northeast-1'
        AND year = '2019'
        AND month = '01'
        AND day = '02'
ORDER BY  eventtime ASC limit 10;
}}

----
** Partition Projection(射影)機能でパーティション管理を自動化 [#md20632d]

- 記事
-- [[[新機能]Amazon Athena ルールベースでパーティションプルーニングを自動化する Partition Projection の徹底解説 | Developers.IO:https://dev.classmethod.jp/articles/20200627-amazon-athena-partition-projection/]]
-- [[Partition projection: possible alternative to Cloudtrail Partitioner &#183; Issue #14 &#183; duo-labs/cloudtrail-partitioner:https://github.com/duo-labs/cloudtrail-partitioner/issues/14]] Tableサンプル

- CREATE TABLE: PARTITIONED, TBLPROPERTIESが変わっている
#geshi(SQL){{
CREATE EXTERNAL TABLE $ATHENA_TABLE_NAME (
    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 $SOURCE_BUCKET bucket'
PARTITIONED BY (
  accountId string,
  region string,
  date string
  )
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://$SOURCE_BUCKET/AWSLogs/$AWS_ACCOUNT_ID/CloudTrail/'
TBLPROPERTIES(
  "projection.enabled" = "true",
  "projection.date.type" = "date",
  "projection.date.range" = "2019/01/01,NOW",
  "projection.date.format" = "yyyy/MM/dd",
  "projection.date.interval" = "1" ,
  "projection.date.interval.unit" = "DAYS",
  "projection.accountid.type" = "enum",
  "projection.accountid.values" = "123456789012,etc",
  "projection.region.type" = "enum",
  "projection.region.values" = "us-east-1,ap-northeast-1,etc",
  "storage.location.template" = "s3://$SOURCE_BUCKET/AWSLogs/${accountid}/CloudTrail/${region}/${date}"
);
}}

- 実際のクエリ: 誰が作ったを調べる。
identifier。
-- accountid, region, date(YYYY/MM/DD)で絞り込む。
-- dateはSTRING型なので、月間の場合「date LIKE '2019/01/%'」のように指定する。
-- dateはパーティション絞り込み用で、厳密なログの日時ではない。ログの日時を指定したい場合eventTimeも指定する。YYYY/MM/01/に02日のログが一部入っていたりする。
#geshi(sql){{
SELECT *
FROM cloudtrail_logs_allregion
WHERE (requestparameters LIKE '%dev-web01%')
        AND accountid = '123456789012'
        AND region = 'ap-northeast-1'
        AND date = '2019/01/02'
ORDER BY  eventtime ASC limit 10;
}}

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS