Cloudflare Tunnel 経由で自前のDNSリゾルバーを利用する
現状
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 を起動直後のパケットをキャプチャしてみます。
sudo tcpdump -n -i ens5 port 7844 and udp
ens5
はホストによって異なるので、もし自分で試す場合は /sbin/ifconfig
して確認してみてください。
この状態で、別のコンソールを開いて cloudflared を再起動してみます。
sudo systemctl restart cloudflared
すると、次々とパケットが流れます。ある程度流れたら Ctrl-C
で止めます。
IP xx.xx.xx.xx.53813 > 198.41.200.113.7844: UDP, length 72
これを見ると、cloudflare側の待機ポートはは7844
を使っていますが、ローカル側はそうではないようです。
今回、ローカル側は 55577
を使っていましたが、再起動してやり直してみると、cloudflare側は7844
で変わりありませんが、ローカルは異なるポートを使っていました。
つまり、ローカル側がランダムに選択したポートと、cloudflare側7844ポートの間で UDP の送受信が行われることになります。
ここまでの整理
というわけで、ここまでの調査で、
- アウトバウンドは、プロトコル:UDP、宛先ポート:7844 を開放すれば良い。
- インバウンドは、プロトコル: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を使う設定があります。
-
Settings
→Warp Client
→Device settings
でプロファイルを選択しEdit
ボタンをクリックし編集モードに入る。 -
Configure settings
→Local 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