LoginSignup
13
14

Cloudflare を経由し、固定の IP から Internet にアクセスをする

Last updated at Posted at 2023-09-01

Web アプリケーションが送信元 IP アドレス指定でアクセスコントロールしている場合、リモートワークの環境ではそこがネックになることがあります。

この記事では Cloudflare のプロダクトを利用して、そんな状況に対応するためのいくつかの方法をメモしておきます。(他にもありそうですが、思いつくものを)

  • 固定 IP を持つのは「データセンター」、遠隔から繋ぐ人は「リモートユーザー」と呼ぶことにします。
  • Free プランでできるものから、Enterprise プランまで載せてます。

1. データセンターの HTTP Proxy 経由で外部に接続

リモートユーザーはデータセンターに配備されている明示 HTTP Proxy を踏んで、そこの固定 IP で Internet にアクセスします。
Screenshot

データセンター、リモートユーザーの双方に cloudflared を準備します。
ただ、これだとオープンに接続できてしまうので、Cloudflare Access を付けて認証・認可を噛ませます。
Screenshot

cloudflared と Access を介してアプリケーションを公開する方法は本家以外にも情報がありますのでここでは割愛しますが、基本的な準備としては

  • Cloudflare にゾーン(ドメイン)をつくる
    • 上の図では example.com か proxy.example.com
  • そのゾーンに Access アプリケーション(Self-Hosted)をつくる
Screenshot
  • cloudflared トンネル(Public Hostnames)を作り、Access アプリケーションと紐つける
    • 今回はデータセンターの HTTP Proxy サーバーを TCP の Service として指定
    • localhost なので cloudflared と HTTP Proxy を同一ホストで動かすケース
Screenshot

を完了しておきます。

操作

リモートユーザー側は cloudfalred access コマンドでブラウザーからの接続を待ち受けておきます。

リモートユーザー側:cloudflared
~ $ 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 をブラウザで叩きます。

リモートユーザー側:cloudflared
:
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.

認証を進めます。
Screenshot

外部 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
データセンター側:明示 HTTP Proxy ログ
~$ 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 が同じサーバーで動いている環境のためです。

メモ

Access の認証については認証期間なども設定できます。
Screenshot

受領した Token の状況は cloudflared access でも確認できます。
設定通り24時間で Token が発行されているようです。

リモートユーザー側:JWT
~ $ 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 サーバで動きます。

Screenshot

操作

この場合リモートの cloudflared は CLI で設定します。
ダッシュボードは socks-service 未対応)

データセンター側:cloudflared 設定抜粋
ingress:
 - hostname: socks.example.com
   service: socks-proxy
   originRequest:
     ipRules:
       - prefix: 0.0.0.0/0
         allow: true

リモートユーザー側は cloudflared access で接続を待ち受けておきます。

リモートユーザー側:cloudflared
~ $ 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 経由で外部に接続

Screenshot

ローカル側は cloudflared でなく WARP Client にします。
1、2 と違い 、リモートユーザー側は HTTP Proxy のプライベート IP に直接つなぐ、VPN的な接続になります。

操作

cloudflared の設定で Private NetworkWARP Routing)をデータセンターのサブネットで有効にしておきます。
Screenshot

WARP On の状態のブラウザーから HTTP Proxy 172.16.0.252:3128 経由で接続することができました。

リモートユーザー側:WARP Client & ブラウザー
~$ warp-cli status
Status update: Connected
Success

~$ curl -x 172.16.0.252:3128 httpbin.org/ip
{
  "origin":  "データーセンターの IP アドレス"
}

HTTP Proxy の接続ログは下記の通りです。

データセンター側:明示 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 設定
~$ 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 で内部のゾーンもサービスできることを期待)

Screenshot

名前で接続してみます。

リモートユーザー側:内部名で接続
~$ 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 で乗り入れることができます。

Screenshot

操作

まず、プライベートネットワークのつなぎですが、さまざまなルーターとの接続例があります。
今回は strongswan でつながってます。

データセンター側:IPsec ルータの状況
~$ 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/12ISP Shared Address となってます。

データセンター側:明示 HTTP Proxy ログ
$ 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 に追加していなかったので、こいつを通すようにします。

データセンター側:squid.conf
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 オプションに切り替える

Enterprise plan のみ

これまでのような、データセンターの配備が不要です(cloudflared や HTTP proxy など)。
Cloudflare Gateway だけで、お客様専有の固定された IP アドレスでインターネットに出れます

目的をシンプルに実現できます。

リモートユーザーからの接続の方法としては下記あたりです。

  • WARP クライアント
  • ブラウザで明示 HTTP Proxy 指定
    • 上の例と違うのは Cloudflare Gateway 自身が明示 Proxy としても動くところ → こちらはEnterprise のみ
Screenshot

操作

まず、WARP Client から直接接続してみます。

リモートユーザー側:WARP 経由インターネットへの直接接続
~$ warp-cli status
Status update: Connected
Success

~$ curl httpbin.org/ip
{
  "origin": "ユーザー専用の Gateway 出口 IP アドレス"
}

次に Gateway の 明示 HTTP Proxy 経由でアクセスします。
これは送信元の IP アドレスで利用をみてアクセスコントロールをしますので、一旦 WARP を切りリモートユーザー自身の IP アドレスを確認します。

リモートユーザー側:WARP Off & IP 確認
~$ warp-cli disconnect
Success

~$ curl httpbin.org/ip
{
  "origin": "リモートユーザーのIPアドレス"
}

この IP を Gateway の明示 HTTP Proxy で受け入れるように設定します。
すると、下記のような接続が可能となります。(”サブドメイン”の部分は自動で割り振られます)

リモートユーザー側:Gateway Explicit Proxy 経由での接続
~$ curl -x https://サブドメイン.proxy.cloudflare-gateway.com:443 httpbin.org/ip

{
  "origin": "ユーザー専用の Gateway 出口 IP アドレス"
}

Gateway 側の関連設定を確認する方法は下記のとおりです。(APIでの例)

Cloudflare Gateway:Proxy ユーザーに割り当てる出口 IP
~ $ 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"
}
Cloudlare Gateway:Proxy に接続許可する IP
~ $ 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で出ることができます。
Screenshot

ルーターでローカルブレイクアウトするのではなく、端末の時点でブレイクアウトするという形です。

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回戦

13
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
14