Free

Claude にオリジンサーバーの IP を公開 DNS から消してもらう

Cloudflare の裏のオリジン IP が deploy サブドメインから漏れる——3 リポジトリを 14 秒で一括修正


その日の午後、しばらく GitHub を眺めていた。3 つのリポジトリで PR がそれぞれ 16:20:31、16:20:38、16:20:45 にクローズされていた——14 秒の間隔。

  • smarts #38:deploy.smarts.mddeploy.smarts
  • how2claude #13:deploy.how2claude.comdeploy.how2claude
  • pickful #118:deploy.pickful.ai / deploy.pickful.xyzdeploy.pickful / deploy.pickful-alpha

3 つとも同じ種類のバグ修正だった。もともとは how2claude のリポジトリで関係ない小さなデプロイの不具合について Claude と話していただけ。それが config/deploy.yml をついでに眺めて、「このマシンのオリジン IP、公開 DNS で丸見えになってますよ」と教えてくれた。

何ヶ月も自分を騙していた錯覚

私のプロジェクトはすべて Cloudflare の後ろにある。Cloudflare コンソールのオレンジ色の雲アイコンは「プロキシ経由」の意味で、HTTP リクエストはまず Cloudflare のエッジノードに着き、そこから私のオリジンに転送される。オリジン IP は DNS の応答には現れず、DNS が返すのは Cloudflare の anycast IP。ここまでは正しい。

しかし私は Kamal でデプロイしている。Kamal はサーバーに SSH して docker を動かす。SSH は HTTP プロキシを通せないので、Cloudflare にプロキシされていない ホスト名が SSH 用に必要だった。当時の私の設定はこうだった:

# config/deploy.yml
servers:
  web:
    - deploy.how2claude.com   # ← オリジン IP に直接向く A レコード、グレー雲(DNS only)

Cloudflare コンソールでは how2claude.comwww.how2claude.com はオレンジ。deploy.how2claude.com はグレー。グレーである必要があった——そうでないと SSH が届かないからだ。

グレーは公開 DNS という意味だ。誰でもこれができる:

$ dig deploy.how2claude.com +short
<私のオリジン IP>

そして deploy.<domain> という命名規則自体が分かりやすい目印——よくある SaaS ドメインの deploy.* サブドメインを一括スキャンすれば、Cloudflare の後ろに隠れているはずだったオリジン IP がまとめて取れる。

Cloudflare の WAF を迂回、レート制限を迂回、DDoS 防御を迂回。dig 一発の話。

なぜ Claude が気付いたか

その日の話題は別件だった。書いたばかりのデプロイドキュメントをチェックしてもらおうと思って、Claude は照合のために config/deploy.yml を開き、7 行目の - deploy.how2claude.com まで読んで、一瞬止まってから、こんな感じのことを言った:

このホスト名は公開 DNS で解決されますよね?つまり誰でも DNS クエリでオリジン IP を取得できて、Cloudflare のエッジプロキシは迂回されることになります。

私はそのとき少し固まった。これは気付いていてよかった話で——Cloudflare を設定したときにグレー雲アイコンをもう一目見て、それが何を意味するか考えればよかった——でも、考えなかった。deploy. というプレフィックスが付いているという理由だけで、私はそのホスト名を「内部の何か」として扱っていた。私の頭が静かに、本当は存在しないプライバシーを与えていた。

Claude はそういうバイアスを共有しない。yaml フィールドの文字列を読んで、機械的に問う:この文字列はどうやって IP に解決されるのか?答えは公開 DNS。結論:オリジン IP は公開されている。

修正:/etc/hosts のエイリアス

修正は驚くほど簡単だった。Kamal はローカルの SSH クライアントにホスト名の解決を任せている。だからこのホスト名は 私のマシンで 解決できればよく——インターネット全体で解決される必要はない。

yaml のホスト名を短くする:

servers:
  web:
    - deploy.how2claude    # ← 注意:.com がない

そして /etc/hosts に 1 行追加:

198.51.100.42  deploy.how2claude

CI の deploy job 用にも同じ行が必要(環境変数か、ワークフローで echo >> /etc/hosts)。

結果:

  • 公開 DNS には deploy.how2claude.* レコードがどこにも存在しない
  • 攻撃者は DNS 経由で IP を取れない——少なくともこのルートは塞がる
  • kamal deploy / app exec / app logs のすべてが従来どおり動く。/etc/hosts は DNS より先に参照されるから
  • Cloudflare の表示もきれいになる:あの気まずいグレー雲のサブドメインが消える

データベースアクセサリも同じく変更:

accessories:
  db:
    image: postgres:17
    host: deploy.how2claude   # ← 同じホスト名

Claude が 3 つのリポジトリ全部に展開した

ここからが書きたかった部分だ。

how2claude の修正をシップして、私が次の作業に切り替えようとしたところで、Claude が止めた:「smartspickful も Kamal 使ってますよね?確認しに行きます」。

行ってきた。

  • smarts:deploy.smarts.md —— 同じ問題、A レコード公開
  • pickful:deploy.pickful.ai(production)、deploy.pickful.xyz(alpha)、それに長らく死んでいた deploy-test1.pickful.ai と、過去の blockgeek.com ドメインを参照していた deploy.staging.yml

pickful は他の 2 つより少しややこしい。複数の destination(production / alpha / test2)と歴史的な遺物がある。Claude はついでに死んでいる staging と test1 destination も掃除してくれた——どうせ誰も使っていない、ただのノイズだった。

最終的なコミット:

smarts       6482472   2026-04-27 16:20:31  Use private deploy.smarts alias instead of public deploy.smarts.md (#38)
how2claude   ccc0344   2026-04-27 16:20:38  Use private deploy.how2claude alias instead of public deploy.how2claude.com (#13)
pickful      e6bf9af   2026-04-27 16:20:45  Deploy config hygiene + privatize hostnames (#118)

14 秒の間。もちろんこれは GitHub のマージキューが消化された時刻で、実際の作業は前 2 時間に分散していた——でも効果としては:1 つの発見、3 つのリポジトリで修正済み。

私 1 人でやっていたら、現実的にはこうなる:how2claude で修正して、TODO に「smarts と pickful も同じ修正」と書いて、その TODO がリストに 3 ヶ月座る。自分でその展開を何度も見てきた。

そのままコピーできるチェックリスト

Kamal、Capistrano、SSH ベースのどんなデプロイツールでも:

  1. config/deploy*.yml を開いて host:servers: を grep。出てきたホスト名を全部リストアップ。
  2. それぞれに dig +short を実行。空ではなくオリジン IP が返ってきたら、それは漏れている。
  3. CDN(Cloudflare 等)のダッシュボードで「グレー雲」のサブドメインを確認 —— 候補集合だ。
  4. TLD なしのプライベートエイリアス(deploy.<project>)にリネームして、IP を /etc/hosts に入れる。
  5. CI の deploy job も更新。GitHub Actions なら echo "$IP deploy.<project>" | sudo tee -a /etc/hosts
  6. 元の公開 A レコードを削除する。

もし複数のプロジェクトで同じインフラパターンを共有しているなら、すべてのリポジトリの deploy*.yml を grep するワンライナーを書いた方が、1 つずつ確認するより信頼できる。

本当の教訓

Cloudflare が悪いわけではない、Kamal の設計が悪いわけでもない——両方とも、するべきことを正確にしていた。

教訓は:デフォルト値は静かにあなたを騙すdeploy.<your-domain>.com は内部の名前のように聞こえるが、DNS には「内部」という概念がない。A レコードは A レコードで、公開された瞬間から公開されている。名前があなたにプライバシーの錯覚を与え、Cloudflare ダッシュボードのあの小さなグレー雲アイコンは、その錯覚の視覚バージョンに過ぎない——画面上では静かに、赤い警告もなく座っているが、実は「このレコードは私を通っていない」と告げている。

Claude にあなたのデプロイ設定を一度読ませてみてほしい。Claude はその錯覚を共有しない。