Redshift監査ログのS3バケットでバケットポリシーを基本Denyにする
Amazon Redshiftの監査ログ機能で、「ログ出力先のS3バケットのアクセス制限をできるだけ絞りたい」という要件を実現するバケットポリシーについて整理していきます。セキュリティ要件的にありがちな設定だとは思うのですが、Condition
の中身が結構複雑になるんですよね。
結論
先にバケットポリシーを掲載します。要点は以下の通りで、★が付いている項目が今回の肝です。このS3バケットを作成したCloudFormationのテンプレートは、本文の最後に掲載しておきます。
- 明示的な許可
- Redshift サービスプリンシパル名からのS3へのアクセス
- Denyの例外
- ★Redshift サービスプリンシパル名からのアクセス
- 監査ログバケットを参照できるIAMユーザー or スイッチロール
- ★Redshiftクラスタにアタッチしている、S3へのアクセス権限があるIAM RoleのID
- CloudFormationからの制御
{ "Version": "2008-10-17", "Statement": [ { "Sid": "Put bucket policy needed for audit logging", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::my-redshift-audit-bucket/*" }, { "Sid": "Get bucket policy needed for audit logging", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::my-redshift-audit-bucket" }, { "Sid": "MultiRestrictPolicy", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::my-redshift-audit-bucket", "arn:aws:s3:::my-redshift-audit-bucket/*" ], "Condition": { "StringNotLike": { "aws:PrincipalServiceName": "redshift.amazonaws.com", "aws:PrincipalArn": "arn:aws:iam::<my-account-id>:role/<my-iam-role>", "aws:userId": "AROAXXXXXXXXXXXXXXXXX:*" }, "StringNotEquals": { "aws:CalledVia": "cloudformation.amazonaws.com" } } } ] }
順を追って解説していきます。
Redshift監査ログの基本設定
まず、AWS公式ドキュメントに書いてあることを整理していきます。
Redshift監査ログはデフォルトでは無効です。有効化にはマネジメントコンソールか、AWS CLIでenable_user_activity_logging
を叩く方法があります。前者の方法では、Redshiftクラスタのプロパティ
タブの編集
からEdit audit logging
をクリックします。
有効化
のボタンを選択し、監査ログの出力先に既存のS3バケットを使用するか、新しくバケットを作成するか選択します。
既存バケットを使用する際に、S3バケット側で何も設定していないと以下のエラーが表示されるはずです。
ここがなぜエラーになるのかというと、Redshiftサービスが監査ログをS3に出力できないからです。詳細は公式ドキュメントをご覧ください。
Database audit logging: Managing log files - Amazon Redshift
{ "Version": "2012-10-17", "Statement": [ { "Sid": "Put bucket policy needed for audit logging", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::BucketName/*" }, { "Sid": "Get bucket policy needed for audit logging", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::BucketName" } ] }
この監査ログバケットに対して、さらにアクセス制限を加えたいというのが今回の試みです。
基本Denyのバケットポリシーに例外を加えていく
基本Denyにすると以下のようなバケットポリシーとなりますが、これではルートアカウント以外からは誰もアクセスできないバケットができてしまいます。
{ "Version": "2008-10-17", "Statement": [ { "Sid": "Stmt1376526643067", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:PutObject", "Resource": "arn:aws:s3:::my-redshift-audit-bucket/*" }, { "Sid": "Stmt137652664067", "Effect": "Allow", "Principal": { "Service": "redshift.amazonaws.com" }, "Action": "s3:GetBucketAcl", "Resource": "arn:aws:s3:::my-redshift-audit-bucket" }, { "Sid": "MultiRestrictPolicy", "Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": [ "arn:aws:s3:::my-redshift-audit-bucket", "arn:aws:s3:::my-redshift-audit-bucket/*" ] } ] }
ここにCondition
のNot系の条件を加えていくことで、Denyの例外を定義していきます。バケットポリシーの集合関係については下記事が参考になるかと思います。
まずは、Redshiftサービスを除外対象にします。
{ "StringNotLike": { "aws:PrincipalServiceName": "redshift.amazonaws.com", ... }
便宜上、監査ログバケットにアクセスできるユーザー(スイッチロール)をaws:PrincipalArnで除外対象にします。必要なければ加えなくて大丈夫です。
{ "StringNotLike": { "aws:PrincipalServiceName": "redshift.amazonaws.com", "aws:PrincipalArn": "arn:aws:iam::<my-account-id>:role/<my-iam-role>", ... }
続いて、Redshiftクラスタから監査ログバケットへのアクセスを除外対象にします。監査ログ検索用にRedshift Spectrumはよく使用されるケースですので、合わせて設定しておきます。監査ログバケットへのアクセス権限を付与したIAMロールをRedshiftクラスタにアタッチし、AROA
で始まるそのRole IDをaws:userIdで指定します。この考え方の解説は下記をご覧ください。
{ "StringNotLike": { ..., "aws:userId": "AROAXXXXXXXXXXXXXXXXX:*" }, ... }
最後にCloudFormationからの制御を機能させるために、CloudFormationからの呼び出しをaws:CalledVia で除外対象にします。
{ "StringNotEquals": { "aws:CalledVia": "cloudformation.amazonaws.com" } }
以上でCondition
の中身は整いました。バケットポリシーをアタッチして監査ログを有効にした後、Redshift Spectrumで外部テーブルを作成していきます。
Redshift Spectrumから監査ログを確認する
Redshift Spectrumから監査ログを参照するための手順は、AWSの公式ブログで取り上げられているので、こちらを参考にしていきます。
Amazon Redshift Spectrum を使用した監査ログの分析
Redshift Spectrumで使用するIAM Policyをアタッチした後、以下のDDLを順次実行していきます。
-- 外部データベースとスキーマを作成 create external schema s_audit_logs from data catalog database 'audit_logs' iam_role 'arn:aws:iam::<my-account-id>:role/<my-iam-role>' create external database if not exists ; -- ユーザーアクティビティログテーブル create external table s_audit_logs.user_activity_log( logrecord varchar(max) ) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://my-redshift-audit-bucket/logs/AWSLogs/<my-account-id>/redshift/ap-northeast-1/' ; -- 接続ログテーブル CREATE EXTERNAL TABLE s_audit_logs.connections_log( event varchar(60), recordtime varchar(60), remotehost varchar(60), remoteport varchar(60), pid int, dbname varchar(60), username varchar(60), authmethod varchar(60), duration int, sslversion varchar(60), sslcipher varchar(150), mtu int, sslcompression varchar(70), sslexpansion varchar(70), iamauthguid varchar(50), application_name varchar(300)) ROW FORMAT DELIMITED FIELDS TERMINATED BY '|' STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat' LOCATION 's3://my-redshift-audit-bucket/logs/AWSLogs/<my-account-id>/redshift/ap-northeast-1/' ;
バケットに対して監査ログを有効にした後、最初のログ出力まで1~2時間はかかるので、しばし待ってから確認することにします。
SELECT * FROM s_audit_logs.user_activity_log LIMIT 10; SELECT * FROM s_audit_logs.connections_log LIMIT 10;
無事、Redshiftから監査ログを取得することができました。
CloudFormationのテンプレート
最後に、今回のバケット作成に使用したCloudFormationのテンプレートを載せておきます。他にIPやVPCエンドポイントで除外条件を加えたい場合は、このテンプレートで色々試してみてください。
AWSTemplateFormatVersion: 2010-09-09 Description: Access Restricted S3 Bucket Parameters: S3BucketName: Type: String Default: my-redshift-audit-bucket Resources: SampleBucket: Type: 'AWS::S3::Bucket' Properties: BucketName: !Sub ${S3BucketName} PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true SampleBucketPolicy: Type: 'AWS::S3::BucketPolicy' Properties: Bucket: !Ref SampleBucket PolicyDocument: Statement: - Action: - s3:PutObject Effect: Allow Sid: 'Put bucket policy needed for audit logging' Resource: !Sub 'arn:aws:s3:::${SampleBucket}/*' Principal: Service: redshift.amazonaws.com - Action: - s3:GetBucketAcl Effect: Allow Sid: 'Get bucket policy needed for audit logging' Resource: !Sub 'arn:aws:s3:::${SampleBucket}' Principal: Service: redshift.amazonaws.com - Action: - 's3:*' Effect: Deny Sid: MultiRestrictPolicy Resource: - !Sub 'arn:aws:s3:::${SampleBucket}' - !Sub 'arn:aws:s3:::${SampleBucket}/*' Principal: '*' Condition: StringNotLike: 'aws:userId': - "AROAXXXXXXXXXXXXXXXXX:*" 'aws:PrincipalServiceName': - redshift.amazonaws.com 'aws:PrincipalArn': - arn:aws:iam::<my-account-id>:role/<my-iam-role> StringNotEquals: "aws:CalledVia": - "cloudformation.amazonaws.com" Outputs: SampleBucket: Value: !Ref SampleBucket