@ledsun blog

Hのキーがhellで、Sのキーがslaveだ、と彼は思った。そしてYのキーがyouだ。

名探偵、Rails 6.0のDNSリバイディング攻撃対策を探すの巻

前提条件

マイクロサービスをdocker composeで動かしていました。 Ruby on RailsでできたAPIサーバーのRailsのバージョンを6.1.4.1に上げました。 するとAPIサーバーは 403 Forbidden を返すようになりました。

奇妙なことに、ホストOSからリクエストを送ると 200 OK が返ってきます。 Dockerコンテナで起動している別のWebアプリケーションから、Dockerコンテナで起動しているAPIサーバーを呼び出すと 403 Forbidden が返ってきます。 また、403 Forbiddenを返すときはAPIサーバーのコンテナでログが出力されません。

ユージュアル・サスペクツ

最初の容疑者 Docker

Dockerで動かすと上手く動かないので、DockerとDocker Composeを疑いました。 次のような項目を確認しました。

  • docker-compose.ymlのポートの設定
  • Railsの起動コマンドの -b 0.0.0.0 オプション

不審な点はありません。

第二の容疑者

つぎに、APIサーバーと通信するクライアントにRest Clientを使っていたので、これを疑いました。 なにか問題があるのかもしれません。

RestClient::Request.execute method: :post, url: url, payload: payload

を、Net::HTTPで次のように置き換えてみました。

uri = URI(url)
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req.body = payload.to_json
res = Net::HTTP.start(uri.hostname, uri.port) do |http|
  http.request(req)
end

特に変わらず 403 Forbiddenが返ってきます。

もう、クライアント側のWebアプリケーション自体が邪魔です。 クライアント側のDockerコンテナに入って、irbを起動して次のようにHTTPリクエストを送ってみました。

Net::HTTP.get(URI('http://lodqa_bs:3000/searches'))

POST リクエストを作るのが面倒だったので、とりあえず GET リクエストを送信してみました。 対象はAPIサーバーですが、管理用のページもあります。 すると、こんな感じのHTML(整形してあります)が返ってきました。

<body>
  <header>
    <h1>Blocked host: lodqa_bs</h1>
  </header>
  <div id=\"container\">
    <h2>To allow requests to lodqa_bs, add the following to your environment configuration:</h2>
    <pre>config.hosts &lt;&lt; \"lodqa_bs\"</pre>
  </div>
</body>

名探偵はひらめきました。 ひらめいたっていうか、Railsからの「設定を変えろ」というメッセージを読みとりました。

犯人

もうね、「Blocked host rails」でググれば犯人特定ですよ。 犯人はRails 6.0から導入された「DNSリバイディング攻撃対策」でした。 とはいえ、証拠は必要です。 「DNSリバイディング攻撃対策」では、承認済みのホスト名を登録すれば回避できるようです。

config/environments/development.rb

config.hosts << 'lodqa_bs'

を追加して、200 OK が返ってくれば万事解決です。

ところが返ってくるのは 403 Forbiddenです。 ポート番号の指定が必要かとかproductionモードで動かしていたかとか悩みました。 設定を変えて試してみました。 結果、現象を整理すると、

config/environments/development.rb

config.hosts.clear

と書いたり

config/application.rb

config.middleware.delete ActionDispatch::HostAuthorization

を書いたら 200 OK が返ってきます。 つまり、機能全体を無効化すれば200 OK が返ってきます。 やはり、犯人はこいつです。

しかし、なぜ lodqa_bs は追加できないのでしょうか?

残された謎

名探偵は灰色の脳細胞の力を使って次のissueを見つけました。

github.com

RFCではホスト名に_を使うことが許可されていますが、多くのDNSサーバーやネットワークでは_をホスト名に使えません。 ドメイン名としても有効ではありません。 このためRailsではconfig.hosts_を含むホスト名が指定されても無視します。

というわけで、ホスト名、というか 「docker-compose.yml で指定するサービス名」を lodqa_bs から lodqa-bs に変更して、万事解決しました。 めでたしめでたし。

slack上の記録によると調査開始が11:29、完了が14:17。約3時間の探偵ごっこでした。

参考