MeetingBaaSにあなたが所有するオブジェクトストレージを指定すれば、録画も文字起こしも音声チャンクもlogも、すべてそちらに保存されます。2つのcredential、実効性のあるアクセス確認、そして引き換えに何を手放すのかの率直な説明。

AAmr El Shimy
··10 min read
Bring Your Own Storage:録画を私たちのインフラに残さない

「録画はどこに保存されるのですか?」——エンタープライズの商談を止めるのは、この質問です。銀行や病院、あるいはデータレジデンシー要件のもとで運用している企業にとって、「当社のバケットに、暗号化して保存しています」はコンプライアンス部門に持っていける答えではありません。

そこで、バケットを私たちに預けられるようにしました。それ以降にbotが生成するすべてのアーティファクト、つまり録画、音声チャンク、文字起こし、スクリーンショット、logは、あなたが所有するストレージに、あなたが発行し、いつでも失効させられるcredentialで書き込まれます。

GET    /v2/storage-config          your current configuration, 404 if unset
PUT    /v2/storage-config          set or replace it
POST   /v2/storage-config/test     re-run the access check
DELETE /v2/storage-config          back to MeetingBaaS storage

または、dashboardの設定(Settings)→ ストレージ(Storage)からも操作できます。セッション認証の背後にある、同じ4つのhandlerです。

2つのcredentialを、物理的な到達先で分ける

これは私たちが最も強く擁護したい設計判断なので、そこから始めさせてください。

用意していただくaccess keyのペアは、1つではなく2つです。

権限存在する場所
ingestPutObject、PutObjectTagging、AbortMultipartUpload録画botに渡されます。私たちのネットワークの外に出ます。
serviceGetObject、ListBucket、PutObject、DeleteObject私たちのAPIサーバー内のみ。外には出ません。

ingest keyは、あなたのミーティングの近くに行く唯一のcredentialです。通話の中でheadlessブラウザを動かしているpodに載るため、このシステムのcredentialが置かれうる場所の中で、最も露出の大きい位置にあります。このkeyはオブジェクトを書き込めます。読み戻すことはできず、中身を一覧することもできず、何も削除できません。podが侵害されたとしてもできるのはprefixにゴミを追加することだけで、被害の範囲はそれがすべてです。

service keyはそれ以外のすべてを担います。署名付きURLによる録画の配信、文字起こしが届いたときの書き込み、保持期間が切れたときのアーティファクトの削除です。

ingestキーはbot podに乗り込み書き込みのみ可能。serviceキーは弊社APIサーバーに留まり、読み取り・書き込み・削除が可能

ここは正確に書いておく価値があります。service keyは読み取り専用ではありません。そう説明すれば、いずれ嘘だと見抜かれます。APIが実際に書き込みと削除の両方を行う以上、このkeyも書き込み、削除します。この分割によって得られるのは、危険な場所に置かれるcredentialのほうが弱い、という性質です。これは影響範囲の実質的な縮小であって、あらゆる箇所で最小権限を実現することとは別物です。どちらを提供しているのかは、正確にお伝えしたいと思います。

設定する

{
  "endpoint": "https://s3.eu-west-3.amazonaws.com",
  "region": "eu-west-3",
  "force_path_style": false,
  "artifacts_bucket": "acme-meeting-artifacts",
  "audio_chunks_bucket": "acme-meeting-audio-chunks",
  "logs_bucket": "acme-meeting-logs",
  "allow_transient_spill": false,
  "ingest_access_key_id": "AKIAIOSFODNN7EXAMPLE",
  "ingest_secret_access_key": "...",
  "service_access_key_id": "AKIAI44QH8DHBEXAMPLE",
  "service_secret_access_key": "..."
}

3つのバケット名は、すべて同じバケットでも構いません。いずれの場合もオブジェクトキーの先頭にbot IDが付くため、衝突は起きません。force_path_styleはMinIOやCeph、そして大半のセルフホストのゲートウェイで必要になる設定です。AWSとScalewayでは不要です。

endpointは公開されたHTTPSである必要があり、credentialやquery stringを含めることはできません。アーティファクトは短命な署名付きURLで配信されるため、バケットは非公開にすべきですが、インターネットから到達可能である必要はあります。文字起こしのプロバイダーは、私たちを経由せず、署名付きURLから直接音声を取得するためです。

ついでに、アーティファクト用バケットで dashboard の origin を許可しておいてください。dashboard はアーティファクトに直接リンクするのではなく自分で取得し、文字起こしの JSON も同じように読み込みます。つまりあなたの endpoint に対する cross-origin の読み取りであり、CORS ルールがなければ、ブラウザは何かが表示される前にレスポンスを破棄します。ルールの中身は、あなたの dashboard の origin からの GET と HEAD、それだけです。なお PutBucketCors は、service key が持つオブジェクト権限より一段上の操作なので、owner 相当の credential が必要です。ここで service key が Forbidden を返すのは、権限の絞り込みが効いている証拠であって、バグではありません。

設定を読み出すと、どのcredentialが使われているか判別できるように2つのaccess key IDが返ります。シークレットはどちらも返りません。今後もです。設定を置き換える際は両方のシークレットを再入力していただく必要があります。保存されている値をお見せできないからです。

アクセス確認と、それが形式的でない理由

PUTは、設定を保存する前に検証します。ListBucketsを呼んで終わり、という方式ではありません。

まずingest keyが、異なるバケットそれぞれにマーカーオブジェクトを書き込みます。続いてservice keyが、そのオブジェクトを読み取り、書き込み、削除します。

各操作は、本番で実際にそれを行うcredentialによって実行されるため、この検査は本番とまったく同じ形で失敗します。4つのいずれもが欠かせません。ingestのアップロードは録画が届く経路、serviceのアップロードは文字起こしが書き込まれる経路、読み取りはアーティファクトが配信される経路、削除は保持期間が実際に何かを失効させる経路です。

本当に有用なのは、書き込みと読み取りを分けている点です。マーカーは一方のcredentialが書き込み、もう一方が見つけなければなりません。これによって、誰も検査しない失敗を捕まえられます。つまり、2つのkeyペアがそれぞれ完全に有効でありながら、別々のバケットや別々のアカウントを指しているケースです。単一credentialのヘルスチェックであれば、世界中のどれもがこの設定を通してしまい、そしてあなたは録画を1本失います。

これがなければ、バケット名の打ち間違いに最初に気づくのは、実際のミーティングが終わり、podのローカルディスクが消えた後のアップロード失敗という形になります。確認に失敗した場合、汎用的なエラーではなく、プロバイダー自身のメッセージをそのまま提示します。「ingest keyはartifactsバケットに書き込めません」と伝えられること自体が、この確認を実行する価値のすべてだからです。

POST /v2/storage-config/testでいつでも再実行できます。keyをローテーションした後や、バケットポリシーを変更した後にお使いください。

この検査が意図的に捕捉できないものが一つあります。CORS です。プローブは私たちのサーバーから実行され、そこに CORS は適用されません。したがってバケットは上記のすべての動詞に合格しながら、ブラウザへの録画の引き渡しを拒否することがあり得ます。この隙間を埋めるのは私たちの仕事であり、それまでは、dashboard の origin を許可するという上記の注記が、緑のチェックと何も表示されない dashboard とを分けるものになります。

すべてがそこから導かれる不変条件

botは常に、書き込まれた時点のストレージに解決されます。そのチームが現在設定しているストレージに解決されることは決してありません。

botがディスパッチされると、その時点で有効なストレージ設定がbotのレコードに刻印されます。そのbotに対するすべての読み取り、すべての署名付きURL、すべての削除は、この刻印を通じて解決されます。チームの設定を通じてではありません。

実装上の細部のように聞こえるかもしれません。しかし、この機能を安心して有効化できるのはこれがあるからで、ここから4つの帰結が直接導かれます。

設定の変更は、新しい行を挿入し、古い行を無効化します。 その場で更新することは決してありません。行を書き換えてしまえば、既存のすべてのbotのアーティファクトが、それらの存在しないバケットへ黙って向けられてしまいます。

DELETE /v2/storage-configはフラグを切り替えるだけです。 どちら側でも何も削除されません。物理削除を行えば、古いbotが今も必要としているcredentialが失われ、アカウント内のすべての録画が孤立します。

解決処理は、設定が有効かどうかを意図的に無視します。 無効化によって止まるのは、新しいアーティファクトがあなたのバケットへ送られることだけです。すでにそこにあるものを孤立させてはなりません。

刻印された設定が見つからない場合は、私たちのバケットにフォールバックせずエラーを発生させます。 フォールバックすれば、あなたのアーティファクトは存在しないものとして報告され、保持期間による削除では、成功を報告しながらデータを黙って残すことになります。大きな音を立てて失敗するのが正しい挙動です。

実務的に言えば、設定も、変更も、削除も、いつでも好きなときに行えます。その間ずっと、古い録画は問題なく機能し続けます。古いアーティファクトが読めなくなる唯一の要因は、あなた側で私たちのアクセスを失効させることであり、それはあなたが下す判断で、期待どおりに機能します。

あなたが実際に引き受けるトレードオフ

自分のストレージを使うチームでは、allow_transient_spillのデフォルトはfalseです。これは私たちのプラットフォームのデフォルトとは逆です。この機能で唯一、何かを差し出すことになる箇所なので、その意味を余さず説明します。

通常、アップロードが失敗すると、私たちは自社のネットワークボリュームにアーティファクトをミラーし、後から突き合わせジョブがそれを送信します。この経路では、ミーティング全体、つまり映像、音声、話者分離の出力、すべてのチャンクがコピーされます。私たちのインフラ上に、私たちのアカウントで、です。

これこそ、データレジデンシーを求めるお客様が避けたかったことです。さらに悪いことに、レジデンシー要件のあるお客様は、プラットフォームのお客様よりもこの経路に多く当たります。バケットが私たちのネットワークの外にあり、多くの場合まったく別のプロバイダーにあるからです。

そのためallow_transient_spill: falseでは、すべてのbot種別とconsumerでこのフォールバックが無効になります。再試行を使い切ったアップロードは失敗として報告され、そのアーティファクトは失われます。

これがトレードオフです。レジデンシーと引き換えに耐久性を差し出します。私たちはbotのアップロードクライアントに、SDKのデフォルトの3回ではなく6回の試行を与えることで部分的に埋め合わせています。一瞬の乱れで録画を失うことはありませんが、バケット側の障害が長引けば、やはり失われます。

保証よりも録画を残すほうを取りたい場合は、trueに設定してください。切り替える際にはdashboardが、何を手放すことになるのかを正確に説明する警告を表示します。読み飛ばしてチェックを入れるようなチェックボックスであってはならないからです。

録画が失われていて、かつバケットに障害があったのであれば、真っ先に確認すべきはここです。

この機能がしないこと

後から気づくことのないよう、4点を先に挙げておきます。

すでに録画されたものが移動することはありません。 移行処理はありません。どのbotも、書き込まれた場所に解決され続けます。これがこの機能を安全に有効化できる理由であり、同時に、過去の履歴は私たちのバケットに残るという意味でもあります。

バケットごとのprefixは設定できません。 キーは<bot_uuid>/…で固定です。

これだけでレジデンシーが完全になるわけではありません。 ミーティングの実行中、botのpodは依然としてローカルディスク上に録画を保持します。この機能が塞ぐのは、あなたの録画が共有のMeetingBaaSインフラ上に留まる唯一の経路です。

dashboardのアセットは私たちのバケットに残ります。 ロゴ、ユーザーのアバター、サポートへの添付ファイルはミーティングのデータではないため、対象外です。

元に戻す

DELETE /v2/storage-configは、いつ実行しても安全です。新しいbotはただちに私たちのストレージに戻ります。既存のbotはあなたのバケットに解決され続け、そのまま動作します。

関連記事

データレジデンシー要件に取り組んでいて、どのコンポーネントが何に触れるのかを正確に知りたい場合は、お問い合わせください。ここに書いたのと同じ答えをお伝えします。

関連ブログstorage