AQ Tech Blog

オブジェクトロックによるサーバーアクセスログの制約に対処した

作成者: tsuyoshi.watanabe|2023年12月06日

本記事はAsiaQuest Advent Calendarの4日目です。

はじめに

クラウドインテグレーション部の渡邊です。
今回はAWS S3のオブジェクトロックによるサーバーアクセスログ(以下、「アクセスログ」と呼ぶ)の制約とその対処について説明します。
公式に記載の通り、オブジェクトロックが有効なバケットをアクセスログの送信先バケットに設定することができません。

S3 オブジェクトロックが設定された S3 バケットは、サーバーアクセスログの送信先バケットとして使用できません。

引用元:Amazon S3 サーバーアクセスログを有効にします。

一方で、アクセスログ保管用のバケットにオブジェクトロックを有効にする方針であった場合、制約に対処する必要があります。
対処法としては、以下の2ステップで対処しました。

①オブジェクトロックが無効のバケットをアクセスログの送信先に設定(以下、「中継用バケット」と呼ぶ)
②オブジェクトロックが有効のバケット(以下、「アクセスログ保管用バケット」と呼ぶ)に対してレプリケーションを実施

上記に加え、考慮する点は以下の2つです。

  • レプリケーションが失敗した場合の対処
    • レプリケーションの失敗によって、アクセスログ保管用バケットでオブジェクトが欠落するのを防ぐための対処が必要です。
    • レプリケーションに失敗した場合、バッチレプリケーション(後述)によって再試行する仕組みを用意します。
  • 中継用バケットのライフサイクル設定
    • 中継用バケットは保管用ではないため、短期間で削除するライフサイクルルールを設定することで、コスト削減を図ったほうがよいと思います。

やってみた

前提となる設定

今回は以下の3つのバケットが登場します。

  • アクセスログを取る対象のバケット名:XX-s3-samplebucket
  • 中継用バケット名:XX-repl-src
  • サーバーアクセスログ保管用バケット名:XX-s3accesslog

中継用バケット名:XX-repl-src

中継用バケットをターゲットバケットに設定するため、引用の制約に注意してバケットを作成します。

  • サーバーアクセスログ記録:無効
  • オブジェクトロック:無効

ターゲットバケットでサーバーアクセスのログ記録が有効になっていない必要があります。
S3 オブジェクトロックが設定された S3 バケットは、サーバーアクセスログの送信先バケットとして使用できません。

引用元:Amazon S3 サーバーアクセスログを有効にします。

また、レプリケーションをするために、バケットのバージョニングを有効化する必要があります。
有効化しないとレプリケーションルール作成時に以下の警告が出ます。

レプリケーションでは、送信元バケットでバージョニングを有効にする必要があります。レプリケーションルールの作成を続行するには、送信元バケットでオブジェクトのバージョニングを有効にしてください。

アクセスログ保管用バケット名:XX-s3accesslog

本記事の前提として、アクセスログ保管用バケットのオブジェクトロックを有効化します。
オブジェクトロックは作成時に有効化する必要があるため、注意してください。

[補足]
オブジェクトロックを作成後に有効化することも可能ですが、有効にするためのトークン発行をカスタマーサポートへ依頼する必要があります。

既存の S3 バケットのオブジェクトロックを有効にするには、オブジェクトロックトークン値が必要です。

引用元:既存の Amazon S3 バケットのオブジェクトロックを有効にするにはどうすればよいですか?

オブジェクトロックを有効化したバケットを作成すると、以下のメッセージが表示されます。

バケット「XX-s3accesslog」内のオブジェクトが削除または上書きされないようにするには、オブジェクトロックを使用して追加の設定が必要です。オブジェクトロックのバケットの設定を表示するには、バケットの詳細を参照してください。

オブジェクトロックを有効化した時点だと、デフォルトの保持期間が設定されておりません。
よって、次のステップで実施する、以下の追加設定を促していると思われます。

  • デフォルトの保持期間
  • デフォルトの保持モード

よって、オブジェクトロックの保持期間を設定します。
オブジェクトロックにはガバナンスモードコンプライアンスモードがあります。
コンプライアンスモードの場合、rootユーザさえも動作を制限されるため、今回はガバナンスモードとします。

コンプライアンスモードでは、AWS アカウント の root ユーザーを含め、ユーザーが、保護されたオブジェクトのバージョンを上書きまたは削除することはできません。

引用元:S3 オブジェクトロックの仕組み

また、意図的に設定せずとも、バージョニングは強制的に有効化されておりました。

レプリケーション用IAMロール

この後、レプリケーションを実施するために、以下のポリシーがアタッチされた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"
}

 

リソース間の連携設定

ここからは各バケット間で連携するための設定を行います。

レプリケーション設定

中継用バケットでレプリケーションルールの設定を行います。
以下の設定があれば問題ないです。

  • ソースバケット > ルールスコープを選択:バケット内のすべてのオブジェクトに適用
  • 送信先:アクセスログ保管用バケット名(XX-s3accesslog)
  • IAMロール:ライブレプリケーション用のIAMロール
  • 追加のレプリケーションオプション > レプリケーションメトリクス:有効化

※レプリケーションメトリクスはこの後、「高品質のための追加設定」で実施するイベント通知のために、事前に有効化しております。
作成時に既存のオブジェクトをレプリケートするか聞かれますが、「いいえ」を選択し、「送信」をクリックします。

アクセスログ送信先設定

アクセスログを取るバケットで、中継用バケットをアクセスログの送信先として設定します。

以下の警告通り、サーバーアクセスのログ記録のターゲットバケット(中継用バケット)のバケットポリシーが更新されます。

サーバーアクセスのログ記録を有効にすると、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の「バッチオペレーション」からジョブを作成します。
バッチオペレーションマニフェスト、完了レポートは作成しないため各設定画面でそれらに関する設定は無効にします。

以下の設定に気をつけて設定しましょう。

  • ソースバケット:s3://XX-repl-src
  • レプリケーションステータス:失敗、なし
  • IAMロール:バッチレプリケーション用のIAMロール
  • 完了レポート:無効

しばらくするとジョブが作成され、ステータスが「実行のための確認待ち」となるので、「ジョブを実行」をクリックします。

ジョブの実行画面に遷移するので、しばらく待つと、ステータスが「完了済み」になります。

FAILEDとなっていたオブジェクトを再確認するとCOMPLETEDに変化しておりました。

各バケットのオブジェクト件数も一致していたのでレプリケーションが問題なく実行されています。

まとめ

  • オブジェクトロックが有効のバケットへサーバアクセスログを格納するために以下のステップで対処する
    • ①オブジェクトロックが無効のバケットをアクセスログの送信先に設定
    • ②オブジェクトロックが有効のバケットに対してレプリケーションを実施
  • 信頼性やコストを考慮して以下を実現するように設定した
    • 信頼性:レプリケーションが失敗時の通知、バッチレプリケーションによるレプリケーションの再実行
    • コスト:ライフサイクル設定
  • レプリケーションにはライブレプリケーションとバッチレプリケーションがある
    • 既存のオブジェクトに対してはバッチレプリケーションで対応する必要がある

【参考】

Amazon SNS トピックが Amazon S3 イベント通知を受け取らないのはなぜですか?

新機能 – Amazon S3 バッチレプリケーションで既存のオブジェクトをレプリケートする

バッチレプリケーションジョブのフィルター

レプリケーションステータス情報の取得

レプリケーションメトリクスと S3 イベント通知による、進捗状況のモニタリング

Amazon S3 ライフサイクルで世代管理を実現する「バージョン数の保持」を試してみた