Web アプリケーションが送信元 IP アドレス指定でアクセスコントロールしている場合、リモートワークの環境ではそこがネックになることがあります。
この記事では Cloudflare のプロダクトを利用して、そんな状況に対応するためのいくつかの方法をメモしておきます。(他にもありそうですが、思いつくものを)
- 固定 IP を持つのは「データセンター」、遠隔から繋ぐ人は「リモートユーザー」と呼ぶことにします。
- Free プランでできるものから、Enterprise プランまで載せてます。
1. データセンターの HTTP Proxy 経由で外部に接続
リモートユーザーはデータセンターに配備されている明示 HTTP Proxy を踏んで、そこの固定 IP で Internet にアクセスします。
データセンター、リモートユーザーの双方に cloudflared を準備します。
ただ、これだとオープンに接続できてしまうので、Cloudflare Access を付けて認証・認可を噛ませます。
cloudflared と Access を介してアプリケーションを公開する方法は本家以外にも情報がありますのでここでは割愛しますが、基本的な準備としては
- Cloudflare にゾーン(ドメイン)をつくる
- 上の図では example.com か proxy.example.com
- そのゾーンに Access アプリケーション(Self-Hosted)をつくる

- cloudflared トンネル(Public Hostnames)を作り、Access アプリケーションと紐つける
- 今回はデータセンターの HTTP Proxy サーバーを TCP の Service として指定
- localhost なので cloudflared と HTTP Proxy を同一ホストで動かすケース

を完了しておきます。
操作
リモートユーザー側は cloudfalred access
コマンドでブラウザーからの接続を待ち受けておきます。
~ $ cloudflared access tcp --url localhost:12345 --hostname proxy.example.com
2023-08-29T06:44:54Z INF Start Websocket listener host=localhost:12345
ブラウザーからデータセンターの HTTP Proxy 経由で外部にアクセスを試みます。
~ $ curl -x localhost:12345 https://httpbin.org/ip
Access を噛ませているので認証が走ります。
認証画面がブラウザに自動表示されます。
されなければターミナルに表示された URL をブラウザで叩きます。
:
A browser window should have opened at the following URL:
https://proxy.example.com/cdn-cgi/access/cli?edge_token_transfer=true&redirect_url=https%3A%2F%2F
...
If the browser failed to open, please visit the URL above directly in your browser.
外部 IDP での認証と Access での認可が成功すると、HTTP Proxy サーバーの固定 IP で接続することができます。
~ $ curl -x localhost:12345 https://httpbin.org/ip
{
"origin": "データーセンターの IP アドレス"
}
HTTP Proxy サーバー側で受信するリクエストは下記のとおりでした。
Internet Protocol Version 4, Src: 127.0.0.1, Dst: 127.0.0.1
Transmission Control Protocol, Src Port: 59368, Dst Port: 3128, Seq: 1, Ack: 1, Len: 113
Hypertext Transfer Protocol
CONNECT httpbin.org:443 HTTP/1.1\r\n
Host: httpbin.org:443\r\n
User-Agent: curl/8.2.1\r\n
Proxy-Connection: Keep-Alive\r\n
\r\n
~$ tail /var/log/squid/access.log
127.0.0.1 TCP_TUNNEL/200 5841 CONNECT httpbin.org:443 - HIER_DIRECT/3.94.140.209 -
接続元は 127.0.0.1 となっていますが、これは cloudflared と HTTP Proxy が同じサーバーで動いている環境のためです。
メモ
受領した Token の状況は cloudflared access
でも確認できます。
設定通り24時間で Token が発行されているようです。
~ $ cloudflared access token -app proxy.example.com | sh jwtdecode
{
"alg": "RS256",
"kid": "a0ed..."
}
{
"aud": [
"b00..."
],
"email": "foo@bar",
"exp": 1693377955,
"iat": 1693291555,
"nbf": 1693291555,
"iss": "https://<YOUR_ORG>.cloudflareaccess.com",
"type": "app",
"identity_nonce": "JFk...",
"sub": "a23...",
"country": "JP"
}
>>> (1693377955-1693291555)/60/60
24.0
2. cloudflared の socks-service で外部に接続
1 と違い、データセンターに HTTP Proxy は不要で、シンプルな構成になります。
cloudflared が socks5 サーバで動きます。

操作
この場合リモートの cloudflared は CLI で設定します。
(ダッシュボードは socks-service 未対応)
ingress:
- hostname: socks.example.com
service: socks-proxy
originRequest:
ipRules:
- prefix: 0.0.0.0/0
allow: true
リモートユーザー側は cloudflared access
で接続を待ち受けておきます。
~ $ cloudflared access tcp --hostname socks.example.com --url 127.0.0.1:23456
2023-08-31T04:52:19Z INF Start Websocket listener host=127.0.0.1:23456
ブラウザーから socks で接続すると、1 のように Access での認証が走ったあと、接続されます。(有効な認証情報がキャッシュされいれば、認証プロセスはバイパスされます)
~ $ curl httpbin.org/ip --socks5-hostname localhost:23456
{
"origin": "データーセンターの IP アドレス"
}
3. WARP クライアントからデータセンターの HTTP Proxy 経由で外部に接続

ローカル側は cloudflared でなく WARP Client にします。
1、2 と違い 、リモートユーザー側は HTTP Proxy のプライベート IP に直接つなぐ、VPN的な接続になります。
操作
cloudflared の設定で Private Network(WARP Routing)をデータセンターのサブネットで有効にしておきます。
WARP On の状態のブラウザーから HTTP Proxy 172.16.0.252:3128 経由で接続することができました。
~$ warp-cli status
Status update: Connected
Success
~$ curl -x 172.16.0.252:3128 httpbin.org/ip
{
"origin": "データーセンターの IP アドレス"
}
HTTP Proxy の接続ログは下記の通りです。
~$ sudo tail -f /var/log/squid/access.log
172.16.0.147 TCP_MISS/200 352 GET http://httpbin.org/ip - HIER_DIRECT/54.175.144.50 application/json
接続元の 172.16.0.147 というのはデータセンター側の cloudflared の IPアドレスになります。つまり、cloudflared はソース NAT をして HTTP Proxy にリクエストを転送します。
cloudflared と HTTP Proxy は別サーバーで動いていることがわかります。
メモ
内部のホストに IP アドレスではなく、名前でアクセスしたい場合は DNS Policy の Override でホスト名を設定することもできます。
その場合、まず Fallback domains を確認します。
ここにあるドメインは Gateway の DNS でなくローカルのリゾルバに問い合わせます。(リスト中のドメインの変更はユーザでできますし、内部ホストの名前解決なら Gateway で Override するのでなく、そっちにやらせてもいいですね)
~$ warp-cli settings
:
Fallback domains:
home.arpa
intranet
internal
private
localdomain
domain
lan
home
host
corp
local
localhost
invalid
test
:
Gateway に DNS 解決を依頼したいので、ここに入ってない名前をつけます。
my.proxy という名前にし 172.16.0.252 で A を返すようにします。(テスト的に使ってます。いずれ Cloudflare の権威DNS で内部のゾーンもサービスできることを期待)

名前で接続してみます。
~$ dig my.proxy +short
172.16.0.252
~$ curl -x my.proxy:3128 httpbin.org/ip
{
"origin": "データーセンターの IP アドレス”
}
4. Magic を使う
Magic WAN というプロダクトもあります。Enterprise プランのみです。
ざっくり言うと、プライベートネットワークをまるごと Cloudflare に載せ、相互通信や Internet との接続を行うというものです。
従来の IP-VPN 網などを置き換えます、地球規模で一枚に。
これにリモートユーザーが WARP で乗り入れることができます。

操作
まず、プライベートネットワークのつなぎですが、さまざまなルーターとの接続例があります。
今回は strongswan でつながってます。
~$ sudo ipsec status
Security Associations (1 up, 0 connecting):
oci_to_cf[43]: ESTABLISHED 8 minutes ago, 172.16.1.223[...]...<Cloudflare の Anycast IP>
oci_to_cf{268}: INSTALLED, TUNNEL, reqid 1, ESP SPIs: c5_i 06_o
oci_to_cf{268}: 0.0.0.0/0 === 0.0.0.0/0
WARP Client から接続すると HTTP Proxy で Block されました…
ログを見ると WARP Client の IP が 100.96.0.0/12 で ISP Shared Address となってます。
$ sudo tail -f /var/log/squid/access.log
100.96.0.62 TCP_DENIED/403 3858 GET http://httpbin.org/ip - HIER_NONE/- text/html
そうでした。WARP クライアントが Cloudflare の中をさまようときはそのレンジから IP を割り振られるのでした。
HTTP Proxy の ACL に追加していなかったので、こいつを通すようにします。
acl my_net src 100.96.0.0/12
接続できました。
~$ curl -x 172.16.1.243:3128 httpbin.org/ip
{
"origin": "データーセンターの IP アドレス"
}
プロキシーを噛ます場合は、送信元の IP アドレス確認も大事ですね。
5. データセンターの固定 IP の代わりに、Gateway の Dedicated Egress IP オプションに切り替える
これまでのような、データセンターの配備が不要です(cloudflared や HTTP proxy など)。
Cloudflare Gateway だけで、お客様専有の固定された IP アドレスでインターネットに出れます。
目的をシンプルに実現できます。
リモートユーザーからの接続の方法としては下記あたりです。
- WARP クライアント
- ブラウザで明示 HTTP Proxy 指定
- 上の例と違うのは Cloudflare Gateway 自身が明示 Proxy としても動くところ → こちらはEnterprise のみ

操作
まず、WARP Client から直接接続してみます。
~$ warp-cli status
Status update: Connected
Success
~$ curl httpbin.org/ip
{
"origin": "ユーザー専用の Gateway 出口 IP アドレス"
}
次に Gateway の 明示 HTTP Proxy 経由でアクセスします。
これは送信元の IP アドレスで利用をみてアクセスコントロールをしますので、一旦 WARP を切りリモートユーザー自身の IP アドレスを確認します。
~$ warp-cli disconnect
Success
~$ curl httpbin.org/ip
{
"origin": "リモートユーザーのIPアドレス"
}
この IP を Gateway の明示 HTTP Proxy で受け入れるように設定します。
すると、下記のような接続が可能となります。(”サブドメイン”の部分は自動で割り振られます)
~$ curl -x https://サブドメイン.proxy.cloudflare-gateway.com:443 httpbin.org/ip
{
"origin": "ユーザー専用の Gateway 出口 IP アドレス"
}
Gateway 側の関連設定を確認する方法は下記のとおりです。(APIでの例)
~ $ curl https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/gateway/rules \
--header 'Content-Type: application/json' \
--header 'X-Auth-Email: '$EMAIL'' \
--header 'X-Auth-Key: '$API_KEY'' -s | jq '.result[]|select (.action=="egress")' | jq '.|select (.name =="設定名")|.rule_settings.egress'
{
"ipv6": "ユーザー専用の出口 IPv6/64",
"ipv4": "ユーザー専用の出口 IPv4/32"
}
~ $ curl "https://api.cloudflare.com/client/v4/accounts/$ACCOUNT_ID/gateway/proxy_endpoints" \
--header 'Content-Type: application/json' \
--header 'X-Auth-Email: '$EMAIL'' \
--header 'X-Auth-Key: '$API_KEY'' -s| jq '.result[]|select (.subdomain=="サブドメイン")|.ips'
[
"Proxy の利用を許可するリモートユーザー側のサブネット",
:
]
5. 【番外】1〜4 は併用が可能
いくつかの方法を示しましたが、これらは併用することができます。
6. 【番外】WARPクライアント利用時にローカルに流す
WARP を利用しているときに、そこの環境の出口 IP で接続するのでよければ、Split Tunnel が使えます。
トラフィックを WARP のトンネルに流さずに、物理インタフェースに流すため、ローカルのIPで出ることができます。
ルーターでローカルブレイクアウトするのではなく、端末の時点でブレイクアウトするという形です。
7. 【番外】WARP から WARP へ
WARP-to−WARP ってのも使えそうです。執筆時点では closed beta とありますが、本家のドキュメントはどんどん更新されていきますので、期待です。
逆にこの様な記事がすぐ古くなります…
8.【番外】勝手トンネル対策
便利さの裏返しで、組織内から勝手にトンネル張られないようにするには、cloudflared の利用ポート および WARP の利用ポート を参考に、対象のサーバーからの接続のみ許可するなど、対策をしておきましょう。
他の VPN や SSH トンネルなどと考え方は同じ ですね。
まとめ
準備する事項、使われる IP アドレス、対応プランをまとめます。
リモートユーザー側 | データセンター側 | 公開ドメイン | Webアプリが受ける送信元 IP アドレス | 対応プラン | |
---|---|---|---|---|---|
1 | cloudflared access + ブラウザー |
cloudflared tunnel + HTTP proxy |
要 | データセンターのIP | すべて |
2 | cloudflared access + ブラウザー |
cloudflared tunnel | 要 | データセンターのIP | すべて |
3 | WARP + ブラウザー |
cloudflared tunnel + HTTP proxy |
不要 | データセンターのIP | すべて |
4 | WARP + ブラウザー |
Magic WAN + HTTP Proxy |
不要 | データセンターのIP | Enterprise |
5 | WARP + ブラウザー or ブラウザーのみ |
なし | 不要 | Cloudflare Gateway の Dedicated Egress IP | Enterprise |
これも追加
https://zenn.dev/oymk/articles/98177fc9636c26
執筆BGM
ニッポン放送ショーアップナイター
巨人 対 広島 24回戦