てくなべ (tekunabe)

ansible / network automation / 学習メモ

[Cloudflare] Cloudflare Tunnel の Ingress rules で複数サービスまとめて公開する

はじめに

Cloudflare Tunnel は、手軽に内部のサーバーを公開できるサービスです。固定グローバルIPや、インバウンドのポート開放も不要で便利です。ngrok あたりが類似サービスに当たるかなと思います。

developers.cloudflare.com

これまで、複数のサーバーを公開する際は、サーバーごとに cloudflared (トンネルを張るデーモン) をインストール、個別のトンネルを張っていました。しかし、だんだん面倒になってきました。

Ingress rules という機能を利用すると、一つのトンネルで、ホスト名と転送先サービスのセットを複数定義できることを知りました。便利そうなので試してみました。

公式ドキュメントがしっかりしてるのでありがたいです。一次情報は公式ドキュメントを参照してください。

developers.cloudflare.com

  • 検証環境

やりたいこと

こんなイメージです。1つの cloudflared でトラフィックを受けてリクエストホスト名に応じて各サービスに転送します。

イメージ(すべて https

トラフィックを受けて、といっても直接このサーバーがポートを開放して待ち受けるわけではありません。実際は cloudflared が Cloudflare にトンネルを張り、DNS のレコードとそのトンネルを対応付けることにより、間接的にトラフィックがくるようなイメージです。

設定

基本的に公式ドキュメント通りです。今回は CLI で設定を進めます。

developers.cloudflare.com

設定1. cloudflared のインストール

インストール方法はいろいろありますが、ここではさくっと dnf でインストールします。

curl -fsSl https://pkg.cloudflare.com/cloudflared-ascii.repo | sudo tee /etc/yum.repos.d/cloudflared.repo
sudo dnf install cloudflared

単一のバイナリでも提供されてて便利ですね。

設定2. 認証

clouldflared logint tunnel コマンドでログインします。

$ cloudflared login tunnel
A browser window should have opened at the following URL:

https://dash.cloudflare.com/argotunnel?aud=略

If the browser failed to open, please visit the URL above directly in your browser.
You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
/home/admin/.cloudflared/cert.pem

表示された認証用の URL を開き、Cloudflare にログインし、トンネルを利用する Zone を選択します。

この段階で、~/.cloudflared/cert.pem というファイルが生成されます。

設定3. トンネルの作成

トンネルを作成します。

トンネルには名前を付ける必要があります。ここでは、複数のサーバーへの転送を束ねる意味合いとして gateway とします。

$ cloudflared tunnel create gateway
Tunnel credentials written to /home/admin/.cloudflared/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa.json. cloudflared chose this file based on where your origin certificate was found. Keep this file secret. To revoke these credentials, delete the tunnel.

Created tunnel gateway with id aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa

トンネルの認証情報を含むファイルが生成されます。同時に、トンネルに対する ID も生成されます。上記のログでは aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa です。

cloudflared tunnel list コマンドで、トンネルの一覧が確認できます。

$ cloudflared tunnel list
You can obtain more detailed information for each tunnel with `cloudflared tunnel info <name/uuid>`
ID                                   NAME    CREATED              CONNECTIONS 
aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa gateway 2023-08-05T10:57:44Z             

なお、この段階ではトンネル自体は作成したものの、Cloudflare 上に定義ができただけで、まだトンネルは張れていません。

$ cloudflared tunnel info gateway
Your tunnel aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa does not have any active connection.

設定4. 設定ファイルの作成

トンネルを張るための設定ファイルを作成します。この設定ファイルに、利用する認証情報ファイルのパスや、Ingress rules などを指定します。

デフォルトで読み込んでくれるパスである ~/.cloudflared/config.yml として以下の内容で作成します。説明用にコメントをつけています。

# トンネルのID
tunnel: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 
# トンネル作成時に生成された認証情報ファイル
credentials-file: /home/admin/.cloudflared/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa.json 

# Ingress rules
# どのホスト名のリクエストを内部のどのサービスに転送するかの設定
ingress:
  - hostname: sv01.example.com
    service: https://10.0.1.1   # ローカルの転送先 sv01
    originRequest:
      noTLSVerify: true         # 証明書検証の無視(ラボ環境のため)
  - hostname: sv02.example.com
    service: https://10.0.1.2   # ローカルの転送先 sv02
    originRequest:
      noTLSVerify: true         # 証明書検証の無視(ラボ環境のため)
  - service: http_status:404    # どのホスト名にもマッチしない場合

設定項目の意味は公式ドキュメントに掲載されています。

最後の service: http_status:404 は、どのホスト名にもマッチしないときの挙動を指定しています。仕様上、この定義がないとエラーになるめ、無難に 404 を返すようにしています。実際は、今回の DNS の設定の仕方(後述)によって、指定ホスト名以外のトラフィックはトンネルにこないはずです。

設定5. トラフィックのルーティング

ドメイン上に CNAME レコードを作成し、トンネルを紐づけてトラフィックをルーティングします

今回は、2つのホスト(sv01sv02)を先ほど作成したトンネルに紐づけます。

$ cloudflared tunnel route dns gateway sv01
2023-08-05T11:20:23Z INF Added CNAME sv01.example.comwhich will route to this tunnel tunnelID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
$ cloudflared tunnel route dns gateway sv02
2023-08-05T11:20:26Z INF Added CNAME sv02.example.comwhich will route to this tunnel tunnelID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa

ワイルドカード * を指定して、あらゆるホスト名の指定をトンネルに向けることもできますが、少し抵抗があったので今回はやめておきます。

設定6. トンネルの起動

準備ができたので、いよいよトンネルを起動します。

$ cloudflared tunnel run gateway
2023-08-05T11:22:57Z INF Starting tunnel tunnelID=aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
...(略)...
2023-08-05T11:22:58Z INF Registered tunnel connection connIndex=0 connection=略
2023-08-05T11:22:59Z INF Registered tunnel connection connIndex=1 connection=略
2023-08-05T11:23:00Z INF Registered tunnel connection connIndex=2 connection=略
2023-08-05T11:23:01Z INF Registered tunnel connection connIndex=3 connection=略

なお、--loglevel debug をつけて、cloudflared tunnel --loglevel debug run gateway のようにして起動すると、アクセスごとにログが表示されるので、動作確認に便利です。

トンネルを起動している状態で、cloudflared tunnel info を実行すると、起動していることが確認できます。

$ cloudflared tunnel info gateway
NAME:     gateway
ID:       aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
CREATED:  2023-08-05 10:57:44.021508 +0000 UTC

CONNECTOR ID                         CREATED              ARCHITECTURE VERSION  ORIGIN IP     EDGE         
bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb 2023-08-05T11:37:50Z linux_amd64  2023.7.3 203.0.113.1   2xKIX, 2xNRT 

サービスとしてトンネルの起動できるようなので、別途試してみたいと思います。

動作確認のアクセス

トンネルを起動した状態で、今回ルールを定義した以下 2ホストのアクセスをして目的の画面が開けばOKです。

  • https://sv01.example.com
  • https://sv02.example.com

cloudflared tunnel run をフォアグラウンドで起動しているため、Ctrl+C で終了するとトンネルが停止してアクセスもできなくなります。

ホスト追加時のオペレーション

今後、公開したいサーバー/サービスが増えてきたら、以下の手順で対応する予定です。

  1. 設定ファイル ~/.cloudflared/config.ymlingress の追記
  2. CNAME の追加 cloudflared tunnel route dns gateway 追加ホスト名前
  3. トンネルの再開

おわりに

Cloudflare Tunnel の Ingress rules で、複数サービスへの転送をまとめて定義する方法を試してみました。

一通りコマンドで済むのが良かったです。

この手の役割は、常時起動している機器、たとえばルーターに仕込んでみたくなりました。