本記事はAsiaQuest Advent Calendarの4日目です。
クラウドインテグレーション部の渡邊です。
今回はAWS S3のオブジェクトロックによるサーバーアクセスログ(以下、「アクセスログ」と呼ぶ)の制約とその対処について説明します。
公式に記載の通り、オブジェクトロックが有効なバケットをアクセスログの送信先バケットに設定することができません。
S3 オブジェクトロックが設定された S3 バケットは、サーバーアクセスログの送信先バケットとして使用できません。
引用元:Amazon S3 サーバーアクセスログを有効にします。
一方で、アクセスログ保管用のバケットにオブジェクトロックを有効にする方針であった場合、制約に対処する必要があります。
対処法としては、以下の2ステップで対処しました。
①オブジェクトロックが無効のバケットをアクセスログの送信先に設定(以下、「中継用バケット」と呼ぶ)
②オブジェクトロックが有効のバケット(以下、「アクセスログ保管用バケット」と呼ぶ)に対してレプリケーションを実施
上記に加え、考慮する点は以下の2つです。
今回は以下の3つのバケットが登場します。
中継用バケットをターゲットバケットに設定するため、引用の制約に注意してバケットを作成します。
ターゲットバケットでサーバーアクセスのログ記録が有効になっていない必要があります。
S3 オブジェクトロックが設定された S3 バケットは、サーバーアクセスログの送信先バケットとして使用できません。
引用元:Amazon S3 サーバーアクセスログを有効にします。
また、レプリケーションをするために、バケットのバージョニングを有効化する必要があります。
有効化しないとレプリケーションルール作成時に以下の警告が出ます。
レプリケーションでは、送信元バケットでバージョニングを有効にする必要があります。レプリケーションルールの作成を続行するには、送信元バケットでオブジェクトのバージョニングを有効にしてください。
本記事の前提として、アクセスログ保管用バケットのオブジェクトロックを有効化します。
オブジェクトロックは作成時に有効化する必要があるため、注意してください。
[補足]
オブジェクトロックを作成後に有効化することも可能ですが、有効にするためのトークン発行をカスタマーサポートへ依頼する必要があります。
既存の S3 バケットのオブジェクトロックを有効にするには、オブジェクトロックトークン値が必要です。
引用元:既存の Amazon S3 バケットのオブジェクトロックを有効にするにはどうすればよいですか?
オブジェクトロックを有効化したバケットを作成すると、以下のメッセージが表示されます。
バケット「XX-s3accesslog」内のオブジェクトが削除または上書きされないようにするには、オブジェクトロックを使用して追加の設定が必要です。オブジェクトロックのバケットの設定を表示するには、バケットの詳細を参照してください。
オブジェクトロックを有効化した時点だと、デフォルトの保持期間が設定されておりません。
よって、次のステップで実施する、以下の追加設定を促していると思われます。
よって、オブジェクトロックの保持期間を設定します。
オブジェクトロックにはガバナンスモードとコンプライアンスモードがあります。
コンプライアンスモードの場合、rootユーザさえも動作を制限されるため、今回はガバナンスモードとします。
コンプライアンスモードでは、AWS アカウント の root ユーザーを含め、ユーザーが、保護されたオブジェクトのバージョンを上書きまたは削除することはできません。
引用元:S3 オブジェクトロックの仕組み
また、意図的に設定せずとも、バージョニングは強制的に有効化されておりました。
この後、レプリケーションを実施するために、以下のポリシーがアタッチされたIAMロールを用意します。
Simple Storage Service (Amazon S3) は、ユーザーに代わってオブジェクトをレプリケートするための許可を必要とします。IAM ロールを作成してこれらの許可を付与し、その後、レプリケーション設定でそのロールを指定します。
引用元:許可のセットアップ
{
"Statement": [
{
"Action": [
"s3:GetReplicationConfiguration",
"s3:ListBucket"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::XX-repl-src"
},
{
"Action": [
"s3:GetObjectVersionTagging",
"s3:GetObjectVersionAcl",
"s3:GetObjectVersionForReplication"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::XX-repl-src/*",
"Sid": ""
},
{
"Action": [
"s3:ReplicateObject",
"s3:ReplicateTags",
"s3:ReplicateDelete"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::XX-s3accesslog/*",
"Sid": ""
}
],
"Version": "2012-10-17"
}
ここからは各バケット間で連携するための設定を行います。
中継用バケットでレプリケーションルールの設定を行います。
以下の設定があれば問題ないです。
※レプリケーションメトリクスはこの後、「高品質のための追加設定」で実施するイベント通知のために、事前に有効化しております。
作成時に既存のオブジェクトをレプリケートするか聞かれますが、「いいえ」を選択し、「送信」をクリックします。
アクセスログを取るバケットで、中継用バケットをアクセスログの送信先として設定します。
以下の警告通り、サーバーアクセスのログ記録のターゲットバケット(中継用バケット)のバケットポリシーが更新されます。
サーバーアクセスのログ記録を有効にすると、S3 コンソールは自動的にバケットポリシーを更新し、S3 ログ配信グループへのアクセスを含めます。
確認してみると、たしかに中継用バケットへポリシーが追加されていました。
{
"Version": "2012-10-17",
"Id": "S3-Console-Auto-Gen-Policy-1696993534754",
"Statement": [
{
"Sid": "S3PolicyStmt-DO-NOT-MODIFY-1696993534661",
"Effect": "Allow",
"Principal": {
"Service": "logging.s3.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::XX-repl-src/*"
}
]
}
試しに、サーバアクセスログを有効にしたバケットを操作して(ファイルアップロード等)、アクセスログを確認してみましょう。
中継用バケット(XX-repl-src)および、アクセスログ保管用バケット(XX-s3accesslog)のオブジェクトを確認すると、アクセスログが格納されておりました。
よって、対象のバケットのアクセスログを、オブジェクトロックを有効化したバケットへ、間接的に格納できたことを確認できました。
次に、レプリケーションのエラーハンドリングやコストを考慮した設定を行います。
失敗時のハンドリングのためにイベント通知を利用してSNS通知します。
今回は、失敗時に手動でエラーハンドリングする方針にしました。
ハンドリングを実現するにあたって、Lambdaを実装しない理由としては、レプリケーションはあまり失敗しないからです(私は正しい設定状態で遭遇したことがありません)。
バッチレプリケーション用のIAMロールの設定
レプリケーションにはライブレプリケーションとバッチレプリケーションがあります。
既存のオブジェクトに対してはライブレプリケーションで対応できないため、レプリケーションをする場合は、バッチレプリケーションをする必要があります。
バッチレプリケーションを実施する際に、以下のポリシーをアタッチしたIAMロールが必要なため、用意します。
IAMロール作成時に、信頼ポリシーで許可するサービスは「S3 Batch Operations」である点に注意してください。
バッチオペレーションマニフェスト、完了レポートは作成しないため、公式のポリシーとは少し異なっております。
{
"Statement": [
{
"Action": [
"s3:InitiateReplication"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::XX-repl-src/*"
},
{
"Action": [
"s3:GetReplicationConfiguration",
"s3:PutInventoryConfiguration"
],
"Effect": "Allow",
"Resource": "arn:aws:s3:::XX-repl-src"
}
],
"Version": "2012-10-17"
}
信頼ポリシーの"Service"フィールドもバッチオペレーション用のサービスとなっております。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "batchoperations.s3.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
KMSの設定
必須ではありませんが、今回はSNSを暗号化する想定で実施します。
そのために、S3用のカスタムキーポリシーを付与するためにカスタマー管理型のキーを作成する必要があります。
デフォルトで設定されているポリシーに、S3用のキーポリシーを追加します("Sid"が"Allow_Service_for_CMK"のブロック)。
{
"Id": "key-consolepolicy-3",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::XXXXXXXXXXXX:root"
},
"Action": "kms:*",
"Resource": "*"
},
{
"Sid": "Allow_Service_for_CMK",
"Effect": "Allow",
"Principal": {
"Service": [
"s3.amazonaws.com"
]
},
"Action": [
"kms:Decrypt",
"kms:GenerateDataKey*"
],
"Resource": "*"
}
]
}
SNSの設定
S3のレプリケーション失敗時に通知を送信するためのSNSトピックを作成する必要があります。
その際に、イベント通知用のポリシーを設定する必要があります。
よって、デフォルトで設定されているポリシーに、S3用のキーポリシーを追加します("Sid"が"S3-policy"のブロック)。
暗号化をするために作成したカスタムキーを指定しましょう。
サブスクリプションの登録も忘れずに行いましょう。
{
"Version": "2008-10-17",
"Id": "__default_policy_ID",
"Statement": [
{
"Sid": "__default_statement_ID",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": [
"SNS:GetTopicAttributes",
"SNS:SetTopicAttributes",
"SNS:AddPermission",
"SNS:RemovePermission",
"SNS:DeleteTopic",
"SNS:Subscribe",
"SNS:ListSubscriptionsByTopic",
"SNS:Publish",
"SNS:Receive"
],
"Resource": "<SNSトピックのARN>",
"Condition": {
"StringEquals": {
"AWS:SourceOwner": "XXXXXXXXXXXX"
}
}
},
{
"Sid": "S3-policy",
"Effect": "Allow",
"Principal": {
"AWS": "*"
},
"Action": "SNS:Publish",
"Resource": "<SNSトピックのARN>",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:s3:::XX-repl-src"
}
}
}
]
}
イベント通知設定
送信先のSNSトピックを指定し、イベント「オブジェクトのレプリケートに失敗しました(s3:Replication:OperationFailedReplication)」を通知対象にします。
レプリケーションに失敗した場合、この設定によってイベントを通知できます。
SNSのアクセスポリシーの権限が不足していた場合、作成時に以下のエラーが発生しました。
以上で、レプリケーション失敗時の通知設定が完了しました。
ライフサイクルルールを作成します。
GWなどの長期休暇の際にレプリケーションの失敗に気づかず、削除されることを防ぐために、2週間(14日)で現行バージョンを削除する設定にしました。
また、非現行バージョンが1日で削除される設定を行っております。
アクセスログ保管用バケットのバケットポリシーを変更します。
レプリケーションを拒否するポリシーを一時的に設定します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Deny",
"Principal": "*",
"Action": [
"s3:ReplicateObject",
"s3:ReplicateTags",
"s3:ReplicateDelete"
],
"Resource": [
"arn:aws:s3:::XX-s3accesslog",
"arn:aws:s3:::XX-s3accesslog/*"
]
}
]
}
しばらくすると、サブスクリプションに登録したメールアドレス宛へ通知が届くと思います。
届かない場合は、ログを取得する対象バケットにファイルをアップロードなどの操作を実施しましょう。
{"Service":"Amazon S3","Event":"s3:TestEvent","Time":"2023-10-11T11:05:09.238Z","Bucket":"XX-repl-src","RequestId":"Z07XGH15WKWF0DFK","HostId":"gInvrP9z2g7oR1XAw9iRvXTDZBA7Y9eC2MczRlo9BllAMpZDcHQwtGYuRA4tORdYIIiZSjrgaR4="}
レプリケーションを拒否する設定後に格納されたアクセスログを確認すると、以下の通り、レプリケーションステータスがFAILED
となっていることがわかります。
以上を確認できたら、レプリケーションを拒否するポリシーを削除しましょう。
これから、レプリケーションステータスがFAILED
のオブジェクトをアクセスログ保管用バケットへ格納します。
レプリケーションに失敗した場合、レプリケーションを再試行できませんので、既存のオブジェクトのアクセスログ保管用バケットに格納する方法は、以下の2パターンです。
①失敗したオブジェクトを直接移動させる
②バッチレプリケーションでレプリケーションに失敗したオブジェクトをレプリケーションする
移動させるオブジェクトが多い場合、失敗したオブジェクトに絞って移動させるのは手間であるため、バッチレプリケーションがよいかと思います。
手動処理としてS3の「バッチオペレーション」からジョブを作成します。
バッチオペレーションマニフェスト、完了レポートは作成しないため各設定画面でそれらに関する設定は無効にします。
以下の設定に気をつけて設定しましょう。
しばらくするとジョブが作成され、ステータスが「実行のための確認待ち」となるので、「ジョブを実行」をクリックします。
ジョブの実行画面に遷移するので、しばらく待つと、ステータスが「完了済み」になります。
FAILEDとなっていたオブジェクトを再確認するとCOMPLETED
に変化しておりました。
各バケットのオブジェクト件数も一致していたのでレプリケーションが問題なく実行されています。
【参考】
Amazon SNS トピックが Amazon S3 イベント通知を受け取らないのはなぜですか?
新機能 – Amazon S3 バッチレプリケーションで既存のオブジェクトをレプリケートする