こんにちは。技術4課の保田(ほだ)です。
在宅勤務に伴いずっと家にいるので7年ぶりに自炊を再開しました。でもちゃんと作るのが面倒なので海外の調味料などを飛び道具として、日々よく分からない強い味の料理を食べています。
要約
S3 のオブジェクトをライフサイクルポリシー以外で削除させないようにして経過を見守った
導入
S3 にはライフサイクルポリシーという、一定期間が経過すると別のストレージクラスに移行したり、有効期限を設定して自動的にオブジェクトを削除したりして主に保管コストを削減できる機能があります。
また、S3 にはバージョニング機能というものもあり、複数のバージョンを保持することができます。これによって、意図しないオブジェクトの変更や削除から保護することができます。
バージョニングを有効にすると、オブジェクトを削除しても表向きは消えたように見えますが、実際には削除マーカーで上書きされているだけでオブジェクト自体にはアクセスできます。
このバージョニングとライフサイクルを併用すると、一定期間が経過すると過去のバージョンを永久的に削除したり、最新のバージョンを有効期限切れにする(削除マーカーの作成)、といったことが可能になります。
そしてバケットポリシーを使用するとバケットやバケット内のオブジェクトに対する API アクションに制限を設けられます。
今回はこれらを駆使して、ライフサイクルポリシー以外の方法ではオブジェクトを削除できないようにしてみます。
バージョニングの有効化
バージョニングを有効にします。マネコンポチポチお手軽です。
ライフサイクルの設定
prefix が archive
であるようなオブジェクトを1日で最新のバージョンと以前のバージョンを期限切れにするよう設定します。
ここの期限切れ時間の算出方法については若干の注意が必要です。今回は1日と設定しましたが、これは明日になったら削除される、というわけではありません。
ライフサイクルルールに指定された日数(今回は1日)をオブジェクトの作成時刻に加算し、それを翌日の 00:00 UTC (日本時間では 9:00)に丸めることで算出します。
したがって、2020 年 5 月 18 日の 19:28:14 JST にオブジェクトを作成した場合、ルールに設定した1日が経過したうえでその翌日の 00:00 UTC になるので、有効期限は 2020 年 5 月 20 日の 9:00:00 JST です。ここで「削除されるのは」と書かなかったのは実際にオブジェクトが削除されるのはこの有効期限に対して遅延が発生する可能性があるためです。(参考:https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/how-to-set-lifecycle-configuration-intro.html)
また、バージョニングと併用した場合は、オブジェクトが最後に更新された時点(最終更新日時)から先ほどのようなやり方で有効期限を算出します。つまり1日で以前のバージョンを削除する設定にしたとして、2020 年 5 月 18 日の 19:28:14 JST にオブジェクトを最終更新した場合、有効期限は 2020 年 5 月 20 日の 9:00:00 JST になります。
ドキュメントにも記載はありますがちょいとややこしいですね。
バケットポリシーの設定
root ユーザー以外には削除させないようにします。以下のようなバケットポリシーを適用します。xxxxx
にはバケット名が、 012345678901
には AWS アカウントの ID が入ります。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "statement1", "Effect": "Deny", "Principal": "*", "Action": [ "s3:DeleteObject", "s3:DeleteObjectVersion", "s3:PutLifeCycleConfiguration" ], "Resource": [ "arn:aws:s3:::xxxxxx", "arn:aws:s3:::xxxxxx/archive/*" ], "Condition": { "StringNotLike": { "aws:userId": [ "012345678901" ] } } } ] }
ここで明示的に拒否している API アクションについては以下のドキュメントを参考にしました。
ユーザーやアカウントがオブジェクトを削除することを明示的に禁止する場合は、
s3:DeleteObject
、s3:DeleteObjectVersion
、およびs3:PutLifecycleConfiguration
のアクセス許可を明示的に拒否する必要があります。
ここで、 s3:DeleteObject
アクションを明示的に禁止していますが、ライフサイクルポリシーによるオブジェクトの削除は内部の S3 エンドポイントを使用して実行されるため、このバケットポリシーの影響を受けません。なのでちゃんと削除されます。
また、このライフサイクルによって実行される API アクションは CloudTrail ではキャプチャされません。(参考:https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/dev/lifecycle-and-other-bucket-config.html#lifecycle-general-considerations-logging)
オブジェクトをアップロードする
prefix が archive
であるようなオブジェクト test.txt
を作成します。つまりオブジェクトのキーは archive/test.txt
です。
中身は適当でいいでしょう。
$ cat test.txt init
アップロードします。
そしてバージョニングによる効果も確認したいので、複数バージョン作成します。
$ cat test.txt version2
これもアップロードします。
こんな感じです。バージョニングされてる感が伝わってきますね。
二晩寝かせる
ここまでの手順で、archive
という prefix 下にアップロードされたオブジェクトは、作成されてから1日の有効期限をもって以前のバージョンが完全に削除され、最新版が削除マーカーで上書きされ、しかもその期限までは root ユーザー以外は消したりライフサイクルポリシーを弄ったりバージョニングを無効にしたり、ができないようになりました。
削除されるのは2日後なのでそれまで待ちます。
2日経ちました。
古いバージョンが削除されて、最新のバージョンとして削除マーカーが上書きされてますね。日本時間の 9:00 に削除されているように見えますが、前述の通り実際にはラグがあるので、3~4時間程度かかっていました。
ちなみにこの段階でマネジメントコンソールのバージョンを非表示にするオプションを選ぶと prefix 含め何もないように見えます。
マネジメントコンソールからフォルダオブジェクトを作成した場合、 prefix 以下に配置したオブジェクトを削除してもフォルダオブジェクトは残るのですが、ライフサイクルだと prefix も消えてしまうみたいです。
さらに二晩寝かせる
まだ続きがあります。さらに二晩寝かせるとどうなるでしょうか。
2日経ちました。
どうやら「最新版を失効する」というルールは削除マーカーには適用されないみたいですね。
翌日
最終段階がありました。
削除マーカーだけになった翌日。
削除マーカーも消えており、画像のようにバージョニングを有効にした表示でも何も確認できなくなりました。
実はライフサイクル設定の画面上にて、現行バージョンを失効させる設定にする(画像左上の 現行バージョン にチェックを入れる)と 期限切れのオブジェクト削除マーカーをクリーンアップする にはチェックが入れられなくなります。
「有効期限を有効にする場合、期限切れのオブジェクト削除マーカーのクリーンアップを有効にすることはできません」という表示もされます。
当初は削除マーカーだけ残るのかと思っていましたが、おそらく有効期限を設定することがすなわち削除マーカーだけ残っても最終的には削除されることと同義になるため、「クリーンアップする」チェックボックスが意味をなさないから有効にできないのだと思われます。
とはいえ削除マーカーが最終更新(作成)された時点から、最新版が1日で失効するライフサイクルに照らして言えば前日の段階で消えていてもおかしくないのでこの点についてはまだ検証の余地がありそうです。
結果
長くなってしまいましたが、冒頭のような設定をすることで
- ルートユーザー以外によるオブジェクト削除を拒否
- 一定期間は過去のバージョンを取得可能
- ライフサイクルにより一定期間で自動的に完全に削除
という設定が可能であることがわかりました。
まとめ
S3 は Simple Strage Service という名前の割りにできることがめちゃくちゃ多くて、組み合わさると実際どういう挙動になるのかわからなくなったりしますよね。今回はほんの一部ですが、複数の機能を組み合わせてどんな挙動になるのかを見てみました。これが何かしら参考になる方がいらっしゃったら幸いです。