🤾‍♂️

Cloudflare WARP から自前データセンターの固定 IPv4 で抜ける

2024/01/29に公開

はじめに

こんなケースの実験です。

  • 組織で利用している Web アプリが送信元 IP でアクセス制限をしている。
  • 組織のデータセンターの出口 IP を登録しているので、WARP のリモートユーザーが Cloudflare Gateway から抜けると、使えない。

手っ取り早く Dedicated Egress IP での解決策がありますが、有償なので、それを使わない方法を考えます。(Dedicated Egress IP の具体例はこちら)。

今回は Gateway 経由でデータセンターにリクエストを転送し、宛先に抜かします。

Split Tunnel で特定宛先を WARP トンネルから除外し、ローカルの IP 転送でデータセンターに持っていく、ってのがありますが、リモート環境だと適用しにくいので、WARP 一本にして、その先の Gateway で分けてみます。

前提

特定のホスト宛の HTTP, HTTPS だけデータセンターの IP から抜ければいいとします。

方法

データセンターにプロキシーを置きます。PAC ファイルの運用は避けたいとの声が多いので、透過型(Transparent Proxy)でにします。データセンターのプロキシーでは HTTP, HTTPS のインスペクションはしません。(必要なら Gateway でやれるので)

実装は下記です。

  • データセンター側のサーバー
    • cloudflared
    • 透過プロキシー
      • 透過プロキシーと言っても、今回は宛先 IP がプロキシー宛で到着する(RFC 1919 Classical IP Proxy の方)ので、下記の対応が必須
      • HTTPS に対しては SNI を参照し、DNS 解決、宛先 IP を差し替え、転送
      • HTTP に対しては Host ヘッダーを参照し、DNS 解決、宛先 IP を差し替え、転送
  • Gateway DNS
    • 対象ホストへの AAAA クエリーは落とし(block)、A に対して、透過プロキシーの IP アドレスを答える(override
  • Gateway HTTP

今回は httpbin.org をデータセンター経由で通します。
通信フローは下記のとおりです。

実装

透過プロキシーは nginx、手軽さのために cloudflared と nginx は同じホスト(ubuntu 22)で稼働します。(異なるホストでもOK)

  • cloudflared
    • サーバーのインターフェース IP 172.16.0.147/32 を cloudflared トンネルに転送(Private Network
  • nginx
    • stream 有効
      apt install libnginx-mod-stream
    • SSL(L4)転送
    stream {
        resolver 127.0.0.53:53 ipv6=off;
        server {
    	listen 172.16.0.147:443;
    	ssl_preread on;
    	proxy_connect_timeout 3s;
    	proxy_pass $ssl_preread_server_name:$server_port;
        }
    }
    
    • HTTP(L7)転送
    server {
    	resolver 127.0.0.53:53 ipv6=off;
    	listen 172.16.0.147:80 default_server;
    	server_name _;
    	location / {
    		proxy_pass http://$http_host$request_uri;
    	}
    }
    
  1. Gateway DNS

    A に向けるため、念のため AAAA クエリーを Block。

    プロキシーサーバーの NIC に override(172.16.0.147)。
  1. Gateway HTTP(オプション)
    プロキシー経由の通信をインスペクトしない場合は Do not inspect 指定

結果

DNS 検索は A のみプロキシーサーバーの IP が得られる。

~$ dig httpbin.org AAAA +short
::
~$ dig httpbin.org A +short
172.16.0.147

対象の httpbin.org で送信元 IP アドレスを確認すると、データセンターの IP で出ています。

~$ curl 'https://httpbin.org/ip'
{
  "origin": "158.*.*.*"
}

対象外の ipify.org は Cloudflare Gateway の IP で出ています。

~$ curl 'https://api.ipify.org?format=json'
{"ip":"104.*.*.*"}

その他

Explicit Proxy の場合は、クライアントがプロキシーを明示するので、任意の宛先ホストに cloudflared から抜けることが可能です。でも、PAC がね。。

~$ curl 'https://httpbin.org/ip' -x 172.16.0.147:8080
{
  "origin": "158.*.*.*"
}

~$ curl 'https://api.ipify.org?format=json' -x 172.16.0.147:8080
{"ip":"158.*.*.*"}

また、手元の Kali Linux に入っていた Burp Suite でも動きました。

  • Burp Suite
    • 下記の L4 プロキシを実施
      • 透過プロキシ = invisible モード
      • intercept はしないので off
      • port 80 での Host ヘッダーベースの宛先 IP を付け替え、 port 443 での SNI ベースの IP を付け替えはデフォルトで対応
      • TLS のインスペクションはしない(空の指定で、全部パススルー
      • invisible を外せば明示プロキシー(デフォルト)になるようです。

余談

squidmitmproxy の透過モードでは、インスペクトせずに SNI ベースで宛先 DNS を差し替えるのには対応してなさげ??

Discussion