resqueを使っているのですが、本番環境でダッシュボードを見ようとしたらHost not permittedと言われて表示できなくなっていました。
なんの味気もない無慈悲な画面。

もちろんresque以外の通常ページはアクセスできますし、ローカル環境ではresqueダッシュボードも表示できます。

設定はよくあるこういう感じです。
# config/routes.rb
Rails.application.routes.draw do
  mount Resque::Server.new, at: '/resque'
end
もちろん production.rb で config.hosts は正しく設定しています。
アップデートが原因
特にResque周りは変えていないのになんでだろう?と思ったら、最近依存関係にあるSinatraをアップデートしていました(このプロジェクトではrenovateで定期的に依存関係をアップデートしています)。
resqueの依存関係的には >= 0.9.2ですが
s.add_dependency "sinatra", ">= 0.9.2"
このプロジェクトでは最近sinatraが 4.1.1 にアップデートされていました。
CHANGELOGを見ると4.1.0に以下の記述があり、これが原因の可能性が高そうです。
- New: Add host_authorization setting (#2053)
- Defaults to .localhost, .test and any IP address in development mode.
- Security: addresses CVE-2024-21510.
詳細調査
ローカルで再現しない
ローカルで RAILS_ENV=production を指定して動かしたのですが、本番と同じ状況が再現しません。
- config.hostsに書いたのと同じホスト名(- 192.168.10.10)にすると、resqueダッシュボード画面が表示される。
- config.hostsに書いたのと違うホスト名として、適当なドメイン(- hogehoge.comなどを適当に- /etc/hostsに書く)を使用すると、Rails側のBlocked hostエラーになる。
後者は良いとして、前者はなんでだろう。
developmentで動いていた
コードを読んでいて気づいたのですが、RAILS_ENV=production を指定しても、マウントした Resque::Server はそれを認識しないので、developmentモードで動いていました。
そのため localhost .localhost .test 0.0.0.0/0 ::/0 が許可リストに入っていたようです。IPアドレスアクセスなら全て通過できるので、ローカル環境でアクセスできた理由はこれですね。また、本番環境ではドメイン名でアクセスしており、そのドメインが .localhost ということはないので、弾かれていました。
本番サーバでdevelopmentモードで動いていたとは...なんてこったい(サーバ管理者しかアクセスできないページだから実害はない)
productionにしたら、設定しないでも許可された
ということで RACK_ENV=production を指定すれば、許可ホスト未設定だからエラーになるのだろう、と思ったらアクセスできてしまいました。
許可ホストが空の場合、すべて禁止ではなくすべて許可という動作になるのですね。つまり、デフォルトではproductionはdevelopmentよりザルです。
完全に理解した
- 先にRails側の config.hostsによるチェックが走り、ここで弾かれたらRails側のエラーになる。- つまり、Sinatra側のチェックは無効化しても実害はないし、逆にSinatra側でチェックする前提でRailsの config.host_authorizationでexcludeを指定しても良いはず。
 
- つまり、Sinatra側のチェックは無効化しても実害はないし、逆にSinatra側でチェックする前提でRailsの 
- Rails側のチェックを通過すると、Sinatra側の Rack::Protection::HostAuthorizationが走る。- developmentの場合、デフォルトではIPアドレスと .localhost.testが許可。
- productionの場合、デフォルトでは素通し。
 
- developmentの場合、デフォルトではIPアドレスと 
二重チェックが無駄とはいえ、せっかくある機能を無効化するのももったいないし、辻褄合わせて動かしたほうが気分が良さそうです。
最終的なコード
こんな雰囲気にすることで解決しました。
# config/routes.rb
require 'resque/server'
class ResqueServerWithPermittedHost < Resque::Server
  set :environment, Rails.env.to_s
  configure :production do
    set :host_authorization, { permitted_hosts: Rails.application.config.hosts }
  end
end
Rails.application.routes.draw do
  mount ResqueServerWithPermittedHost.new, at: '/resque'
end
 
      