🍡

Cloudflare Tunnel 経由で自前のDNSリゾルバーを利用する

2024/08/09に公開

現状

cloudflare Zero Trust の Tunnel を使うとリモートのホストにローカルIPアドレスで接続することができるようになります。
実際私もAWS上のサーバーに接続する際に重宝していました。

Cloudflare Zero Trust 経由でAWS上のEC2(グローバルIPアドレス無し)にSSHでログインする

AWSのプライベートDNSが使えない

ただ、私はVPCにRoute53でプライベートDNSを構築し、各ホストの名前を登録して、その名前でホストやRDSに接続しているのですが、Cloudflare Zero Trust 経由だとそれができないという問題がありました。

具体的には、AWSのVPC上に Route53 Resolver インバウンドエンドポイントを用意しており、そこに向かって手元のPCから Cloudflare Tunnel経由で nslookup しても応答がありませんでした。

ping も通らない

あと、これはそこまで問題では無いのですが、手元のPCからEC2インスタンスに対して、Cloudflare Tunnel経由でpingを打っても応答がありませんでした。まあこれは、そういうものだと思っていたのですが...

原因

原因を知るには、Cloudflare tunnel がどのような原理で通信しているのか、ある程度知る必要がありました。

調べてみると、Cloudflare tunnel は、QUIC または TCP によって Cloudflare側のネットワークと通信しており、EC2インスタンスでcloudflared 起動後、最初は QUIC で、それがダメなら TCP で Cloudflareネットワークに接続するようです。
(QUIC は内部で UDP を使ったプロトコルです)

HTTP/3とは?HTTP/2との違い(quic・udp)をわかりやすく紹介

そして、もし QUIC での接続に成功するとTunnel経由で UDP,ICMP を転送できるのですが、TCP で接続するとTunnelは UDP,ICMP を通さないということがわかりました。

Getting Cloudflare Tunnels to connect to the Cloudflare Network with QUIC

DNS は通常は UDP を使用しますから、手元のPCからTunnel経由で Route53 Resolver が使えない理由は明確になりました。pingに応答がないのも同じですね。

Cloudflare Tunnel が使っているポートを調べる

ネットワークの設定を修正し、Cloudflare Tunnel経由で Route53 Resolver を使用するには、Cloudflare tunnel がUDPの何番ポートを使っているか知る必要があります。
UDPの cloudflare 側の待機ポートは7844のようですが、ローカル側は何番を使っているのでしょうか。

早速、cloudflare tunnel が稼働しているEC2インスタンスに、別ルートで入り、cloudflared を起動直後のパケットをキャプチャしてみます。

コンソール1
sudo tcpdump -n -i ens5 port 7844 and udp

ens5 はホストによって異なるので、もし自分で試す場合は /sbin/ifconfig して確認してみてください。

この状態で、別のコンソールを開いて cloudflared を再起動してみます。

コンソール2
sudo systemctl restart cloudflared

すると、次々とパケットが流れます。ある程度流れたら Ctrl-C で止めます。

コンソール1
IP xx.xx.xx.xx.53813 > 198.41.200.113.7844: UDP, length 72

これを見ると、cloudflare側の待機ポートはは7844を使っていますが、ローカル側はそうではないようです。
今回、ローカル側は 55577 を使っていましたが、再起動してやり直してみると、cloudflare側は7844で変わりありませんが、ローカルは異なるポートを使っていました。

つまり、ローカル側がランダムに選択したポートと、cloudflare側7844ポートの間で UDP の送受信が行われることになります。

ここまでの整理

というわけで、ここまでの調査で、

  1. アウトバウンドは、プロトコル:UDP、宛先ポート:7844 を開放すれば良い。
  2. インバウンドは、プロトコル:UDP、宛先ポートは一般ユーザーが使えるポート範囲を開放してやる必要がある。

ことがわかりました(cloudflared が使用する送信元ポート番号を固定する方法は、ざっと調べた限り見つかりませんでした)。

問題になるのは、cloudflareからローカルに向かって送信される UDPパケットが、ローカルでランダムに選択されたポートに対して飛んでくるので、これを通るようにネットワークを設定してやらないといけないという点です。

もし、そんな開放できませんよーという方は、cloudfalred が次に使おうとする TCP での通信が安全と思いますので、これ以降の設定はせずに、おとなしくローカルDNSを使うのは諦めましょう。

VPCのネットワークの設定

前項までの内容を踏まえて、以下の通り UDP のポートを開放してやります。

ネットワークACL

VPCのネットワークACLのインバウンドに、以下の設定を追加します。

セキュリティグループ

cloudflared が稼働するEC2インスタンスに関連付けられているセキュリティグループのインバウンドに以下のルールを追加します。

いずれも 1024〜65535 を指定しているのは、ローカルポートで何番ポートが使われるかわからないからです。

以上でネットワークの設定は終わりです。

cloudflared を再起動

cloudflared は起動時に QUIC、TCP どちらのプロトコルで接続するか決定しますので、上記のネットワーク設定が終わった時点でcloudflared を再起動します。

sudo systemctl restart cloudflared

Cloudflare Zero Trust で Local Doman Fallback を設定する

Cloudflare Zero Trust には、クライアントが特定のドメインの名前解決する場合に Cloudfalre のDNS以外の、指定したDNSを使う設定があります。

  1. SettingsWarp ClientDevice settings でプロファイルを選択し Edit ボタンをクリックし編集モードに入る。
  2. Configure settingsLocal Domain Fallback の Manageボタンをクリック。
    • ここでは、特定のドメインに対するDNSクエリを、cloudflare管理以外のDNSが解決するように指定することができます。

例として、 hoge-vpc.com ドメインの解決を Route53 Resolver に解決させたい場合、Route53 Resolver のインバンドエンドポイントが10.128.0.61 10.128.0.130 の2つだとすると、以下のように入力して Save Domain をクリックします。

設定の反映に数分かかる場合がありますが、以上で特定のドメインの名前解決を Cloudflare Tunnel の向こうにあるDNSリゾルバーが解決するようにできました。

おまけ: ping も通るようになった

自分にとってはそこまで重要ではありませんが、今回の変更によってローカルPCから Tunnelの向こうにあるホストに対して ping が通るようになりました。

Discussion