tl;dr

AWS アカウントをまたいだ S3 バケットへの書き込みが色々とアレなので Lambda で無理矢理アレした。

背景

S3 は AWS 最古参のサービスであり、 S3 が登場した頃は IAM も存在しなかった。このため、今では AWS サービスの標準アクセス権限管理になった IAM とは異なる、独自のアクセス権限管理の仕組みが S3 にはある。

この歴史的経緯により、ある状況で非常に面倒な事態が発生する。

バケットオーナー != オブジェクトオーナー の場合に発生するつらみ

  • 凡例
    • つらくない現実
    • つらい現実
  • バケットポリシーで他アカウントへの認可を設定すると、別の AWS アカウント(と、その配下の IAM User など)がバケットへの書き込みを行えるようになる
  • バケットにPutObject されたオブジェクトのオーナーはそのオブジェクトを PUT したユーザーになる
    • ここでの ユーザーAWS アカウントと読み替えても構わない
    • これが誰なのかを調べる API はGetObjectACLである
  • 権限付与(Object ACL)を明示的に指定せず PUT されたオブジェクトへの権限は、オブジェクトオーナーのフルアクセスのみになる
    • オブジェクトオーナーのフルアクセス権限は剥がすことができるが、そのようにしても依然としてオブジェクトオーナーはそのオブジェクトへのフルアクセスが可能である
  • 自分がオーナーではなく、自分に対して明示的許可が与えられていないオブジェクトに対するGetObjectGetObjectACL はバケットオーナーであっても不可能
  • バケットオーナーは、自分に対してアクセス許可がないオブジェクトを DeleteObject できる
  • 自分がオーナーでないオブジェクトと同じキーに対して PutObject すること(上書き)は可能
  • バケットポリシーによるアクセス許可は、オーナーがバケットオーナーであるオブジェクトのみに適用される

どうしたか

アカウント A と B があるとして、アカウント A にあるバケットに対し、アカウント B からPutObjectした場合に上のようなつらみが発生する。

そこで、まず s3:PutObject, s3:GetObject, s3:GetObjectAclが可能な Role をアカウント B に作り、さらに以下のような Lambda Function をアカウント A に配置して S3 の ObjectCreatedイベントで発火するようにした。

これは何をしているのか

1.前述の、アカウント B にある Role を sts:AssumeRoleする
2.作成されたオブジェクトにバケットオーナー(アカウント A)に対する ACL が設定されていなければ
3. バケットオーナー(アカウント A)に対する ACL を明示的に指定して、PutObjectしなおす

1が必要なのは、この場合2と3がそもそもアカウント A からは操作できないから。

まとめ

ここまで全部 BK 。 S3 (Simple Storage Service) は極力シンプルに使うべき。

その他

Serverless Framework で書き直したい。

元記事はこちら

S3 のクロスアカウント書き込みにまつわる面倒を Lambda でねじ伏せる