双方向ではなく、Cloudflare One → Tailnet (Tailscale)の方向のみ
構成図

リレーサーバを用意して接続を中継する
なぜこんなことをするのか?
クライアント端末はインターネットへの通信を含めたほとんどの通信を管理下に置きたい
サーバはサーバ同士やクライアントとの接続を簡単にしたい
それぞれの目的が異なるため、クライアント端末にはCloudflare One、サーバにはTailscaleが向いている
そのため、Cloudflare OneとTailscaleを接続した
手順
このとき気をつけなければならないのが、Cloudflare OneとTailscaleの予約済みIPレンジが被らないようにしてあげる必要がある
Cloudflare OneもTailscaleもCGNATで使われるIPレンジ (100.64.0.0/10)の一部を使用するため、うまいこと重複を避ける必要がある
Tailscaleのデフォルトでは、100.64.0.0/10の範囲からデバイスにIPアドレスが割り当てられるため、Cloudflare Oneと競合するが、別の範囲に変更することができる
予約済みIPレンジ
それぞれの予約済みIPレンジから隙間を見つける
100.64.0.0/12
100.80.0.0/16
100.96.0.0/12
100.112.0.0/16Tailscale (デフォルトのIP Poolを除く)
100.100.0.0/24
100.100.100.0/24
100.100.100.100/32
100.115.92.0/23
100.101.102.103/32これらの範囲を100.64.0.0/10から除外してやると、以下の範囲が残る
100.81.0.0/16
100.82.0.0/15
100.84.0.0/14
100.88.0.0/13
100.113.0.0/16
100.114.0.0/16
100.115.0.0/18
100.115.64.0/20
100.115.80.0/21
100.115.88.0/22
100.115.94.0/23
100.115.96.0/19
100.115.128.0/17
100.116.0.0/14
100.120.0.0/13この中でわかりやすくかつ十分な範囲があるものを選ぶと、100.81.0.0/16, 100.113.0.0/16, 100.114.0.0/16 になる
今回はTailscaleのデバイスIPアドレスは100.81.0.0/16の範囲から割り振るように変更する
Cloudflare One側でも、Split Tunnelsで100.81.0.0/16 の範囲がGatewayを通るように構成する
また、MagicDNSも使えるようにするため、100.113.0.0/16 の範囲から100.113.0.53 を振り出して、100.100.100.100 に転送する
sudo iptables -t nat -A OUTPUT -d 100.113.0.53 -j DNAT --to-destination 100.100.100.100Cloudflare One側では、Local Domain Fallbackでts.net の名前解決に使用するDNSサーバを100.113.0.53 に設定する
最後に、Cloudflare Tunnelの設定でCIDR routeとして100.81.0.0/16と100.113.0.53/32 を設定する
以上でCloudflare One → Tailscaleの接続設定が完了する
本当はCloudflare Mesh nodesを使いたかった
Tailscaleがtailscale0インターフェース以外から来た100.64.0.0/10宛のパケットをドロップするようなiptablesルールを挿入する
DROP all -- !tailscale0 * 100.64.0.0/10 0.0.0.0/0100.64.0.0/10をハードコードでドロップするため、Cloudflare Oneの予約済みアドレスと重複が発生する
Cloudflare Mesh nodesはL3で動作するため、該当パケットがドロップされてしまう
そのため、今回はCloudflaredを使用した
