S3/R2から読み込まれたカスタム絵文字はCDNのルーティングをバイパスする

概要

S3 または Cloudflare R2 をアップロード用ストレージとして、かつカスタム CDN URL と併用している場合、カスタム絵文字のアップロードは CDN の設定を無視し、生(raw)のバケット URL から直接読み込もうとします。

問題点

管理者がカスタム絵文字をアップロードすると、アップローダーは upload レコードを作成し、生(raw)のバケット URL(例://my-bucket.s3.amazonaws.com/...//my-bucket.r2.cloudflarestorage.com/...)をデータベースに保存します。これは Discourse の標準的な動作です。

しかし、app/models/emoji.rb/site.json 用の絵文字キャッシュを生成する際、upload.url をそのまま emoji オブジェクトに渡しています:

e.url = emoji.upload&.url

これにより CDN ヘルパーがスキップされるため、フロントエンドには生(raw)のバケット URL が渡されます。その結果、バケットのアクセスポリシーの厳格さによっては、画像が破損したり、Discourse が内部の avatar_proxy を介して絵文字をルーティングせざるを得なくなったりします。

解決策

URL の代入処理を Discourse.store.cdn_url() でラップする PR を作成しました。これにより、カスタム絵文字のローダーが、標準的な投稿画像やアバターと同じようにルーティングされるようになります。

暫定的な回避策

PR のレビューおよびマージを待っている間、カスタム絵文字が DOM にレンダリングされる直前に、生(raw)のバケット URL を適切な CDN URL に置き換える軽量なテーマコンポーネントを作成しました(投稿とチャットの両方で動作します)。

あなたのサイトでこのバグに遭遇している場合は、このコンポーネントをインストールし、テーマ管理設定で S3 の文字列を設定することで、破損しているカスタム絵文字を修復できます:

注意: すでにアップロード済みで現在破損しているカスタム絵文字がある場合、コンテナ内で discourse remap "//my-raw-bucket-url.com" "https://my-cdn.com" を実行すると、データベース内の既存の絵文字が修復されます。一方、テーマコンポーネントは、PR の修正がコアにマージされるまでの間、新しくアップロードされた絵文字の修復を行います。

「いいね!」 5

デフォルトの絵文字のテスト

:smiley:

カスタム絵文字のテスト

:falco:

「いいね!」 2

ああ、もしかしたらCloudflare R2特有の問題かもしれないね。自分のインスタンスで壊れてるよ。新しいものをアップロードしたときだけ壊れるように見える。

テーマコンポーネントの修正がないと、新しいものをアップロードするたびにリマップを実行しなきゃいけないんだ。PRはもう少し作業が必要かも。絵文字のコードにはあまり詳しくないから。

「いいね!」 2

Discourse は 2 つの CDN 構成を使用しており、1 つはアセット用、もう 1 つはアプリのプロキシ用です。

標準絵文字は 1 つの CDN を使用し、カスタム絵文字は別の CDN を使用しますが、適切に設定されたウェブサイトでは、稼働中の 2 CDN 構成によって両方が保護されています。

これは、最初の関連トピックで説明しました。

あなたのサイトには両方の CDN が設定され、稼働していますか?

「いいね!」 4

なるほど、確認してみないといけないですね。知りませんでした。ありがとうございます!

編集:

Cloudflare R2のセクションを書いた私なので、設定は正しいと仮定してよいでしょうか? 何が不足しているのでしょうか?

「いいね!」 2

「いいね!」 1

ここでの確認ですが、最近の変更後にPRブランチをインストールしてテストし、この問題が修正されていることを確認しました。

「いいね!」 1

私が書いたwikiガイドのこのセクションです:

両方設定していますか?

これはMetaをAWSからMetalに戻す際のアーティファクトで、新しいcookが不足していました。HTMLを再構築して修正しました。

テストサイトには両方のCDN環境変数が設定されていますか?

「いいね!」 3

設定はしているのですが、神様よ、DISCOURSE_CDN_URLが指すCDN固有のDNSレコードのCloudflare設定でタイプミスをしていました。しかも、設定した時にテストしなかったのは、サイトが動いていたからなんです :laughing: なんてことだ。

まあ、無駄なPRを作って絵文字のコード生成についてもっと学べたので良しとしましょう。
:woman_facepalming:t2:

Falcoさん、ありがとう!

「いいね!」 3

ふふ、大丈夫よ!

私が一番学べるのは、決して具体的な成果に結びつかないウサギの穴を追いかけるときよ。でも、そこから得られる知識は黄金の価値があるの!

「いいね!」 3

わお、これは相当な冒険でした。Cloudflare R2 オブジェクトストレージと Discourse インスタンスの構成をほぼ全面的に再設定しましたが、このバグは R2 に関連する正当な問題だと思われます。Cloudflare DNS レコードを修正し、DISCOURSE_CDN_URL が実際にその先を指すようにインスタンスを再構築したところ、翻訳文字列などの他の多数の機能が壊れ、コンソールには複数のエラー(CORS エラーを含む)が出力されました。今日一日、この問題で様々な調査を繰り返しました。つまり、DISCOURSE_CDN_URL の使用は Cloudflare R2 と互換性がないようです。(これは非常に奇妙でした。当初 DNS エントリを設定した際、cdn.mysite.com の DNS レコードを誤って設定し、cdn.mysite.com.mysite.com として解決されるようになっていました。)DISCOURSE_CDN_URL を正しく設定しても、Cloudflare R2 オブジェクトストレージとは互換性がないようです。ここにはまだ完全に理解していない部分があるかもしれません。

ともかく、私の PR ブランチで再構築するとすべてが修正されます。これは、カスタム絵文字のアップロードが、標準の投稿画像アップロードと同じ S3 CDN ルーティングとフォールバックロジックに従うように、代入処理を Discourse.store.cdn_url() でラップしているためです。

PR を再開し、説明を編集しました。ただし、Discourse チームがこれをマージしないことを選んだとしても、それはそれで構いません。なぜなら、テーマコンポーネントはクライアント側でこの問題を修正するからです。なお、この PR の修正は、カスタム絵文字の R2 オブジェクトストレージ構成のみを対象とし、AWS などの他の通常の S3 互換ストレージには影響しないはずです。

「いいね!」 2

このPRはマージされ、アップロードにCloudflare R2オブジェクトストレージを使用している場合のカスタム絵文字の表示が修正されました。関連するR2ドキュメントへの更新提案をこちらに投稿しました: Configure an S3 compatible object storage provider for uploads

「いいね!」 3

問題が解決しました。再度発生した場合は、新しいレポートを作成してください。