Cloudflareで始める:Cloudflare Tunnelを使ったwebサーバ公開

この記事は1年以上前に投稿されました。情報が古い可能性がありますので、ご注意ください。
はじめに

Cloudflareという会社の名前を聞いたことがある人でも、CloudflareはCDN屋さんやDDoS対策屋さん、WAF屋さん、あるいはDNS屋さんだと思っている思っている人は多いのではないでしょうか。
この(勝手に始めた)「Cloudflareで始める」シリーズでは、クラウド事業者としてのCloudflareの知られざる実力を暴いていきたいと思います!
Cloudflareがどのような会社か、他社と比較してどうなのか等は特に述べません。
注意事項
サービス内容やコストなどは執筆時点の情報になります。最新の情報と相違無いか確認しつつ活用してください。
今回の題材
「はじめに」で大見得を切りましたが、今回は自宅サーバの定番でありCloudflareの基本中の基本であるwebサーバの公開を取り扱います。
自宅で簡単にお試しできる、ぐらいの構成から始めていこうという魂胆です。
とはいえ、普通にルータのポート開放してIPアドレスをDNSサーバに登録して、IPアドレス非公開化のためにproxy設定して...みたいな使い方は多く紹介されていると思うので、Cloudflare Tunnelを利用する構成をご紹介します。

Cloudflare Tunnelを採用する利点としては
- ルータのポート開放不要
- 手間の削減だけでなく、ルータの操作ができない場合にも有効
 
 - Webサーバ、ルータのIPアドレス変更に強い
- ルータにwebサーバのIPアドレスを覚えさせる必要がない
 - CloudflareのDNSサーバにルータのグローバルIPアドレスを覚えさせる必要がない
 
 - 設定をTerraformで行える・管理できる
- (最初のCloudflareのアカウント登録やwebサイト登録はご容赦ください...)
 
 
があります。
欠点としては
- 接続元IPアドレスによる制限がwebサーバ側でかけられない
- IPのレイヤではlocalhostからの通信に見える為
 - HTTP以後のレイヤであれば
Cf-Connecting-Ipヘッダを読み込み、処理分岐させる事で対処可能 
 - webサーバへの一回のアップロード容量が絞られる可能性
- Freeプランでは100MBまでになるとの情報あり(未検証)
 - 有料プラン契約、ファイルの分割などで対処してください
 
 
があります。
具体的にどのような経路を辿ってユーザがwebサービスを利用するかはこちらの公式ドキュメントを参照してください。
https://developers.cloudflare.com/cloudflare-one/connections/connect-network
検証
折角なのでCloudflare側の設定はTerraformを使って設定を試みます。
(webサーバ側の設定は手動で行います)
前提
- Cloudflareにアカウントを作成済み
- 公式サイト https://www.cloudflare.com/ja-jp/ からサインアップできます
 - FreeプランでOKです
 
 - example.comなど、自分のDNSドメイン名を取得している
- Cloudflareでも取得可能です!
 - 今回コストがかかるのはこの項目だけ!
 
 - Cloudflareアカウントにwebサイトを登録済み
- 「Webサイト」と言うと警戒されますが、DNSドメイン名とCloudflareアカウントの紐付けです
 
 - クレジットカード、PayPalなどCloudflareで有効な支払い方法が用意できる
- 用途
- Cloudflare Zero Trustの利用登録
- 課金が発生しない構成でも支払い情報の登録が必要な場合があります
 
 - (Optional) Cloudflareで自分のDNSドメイン名を取得する際に利用
 
 - Cloudflare Zero Trustの利用登録
 
 - 用途
 - Cloudflare Tunnelで利用するドメイン、IPアドレス、ポートへのアクセスが可能
- ファイアウォールなどで通信を制限している場合は許可するように設定
 - https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/deploy-tunnels/tunnel-with-firewall/
 
 
検証環境
- 設定PC
- Terraform v1.9.5
 
 - webサーバ
- Nginx公式dockerイメージ(1.27.1-alpine3.20-slim)を設定を変えずにデプロイ済
- デプロイコマンド
docker run --rm -it -p 80:80 nginx:1.27.1-alpine3.20-slim
 
 - デプロイコマンド
 
 - Nginx公式dockerイメージ(1.27.1-alpine3.20-slim)を設定を変えずにデプロイ済
 
設定手順
Cloudflare Zero Trustの登録
- Cloudflareアカウントにログインし、Zero TrustをクリックしてZero Trustの管理メニューに移動します。
 

- このタイミングで支払い情報の登録やZero Trustのプラン選択を迫られるので、画面の指示に従って登録を進める
- 今回の検証の範囲ではプランはFreeプランで十分です
- プランごとの差異はこちらで確認可能です
 - https://www.cloudflare.com/ja-jp/plans/zero-trust-services/
 
 - Team domainについては後から変更が可能です
 
 - 今回の検証の範囲ではプランはFreeプランで十分です
 
CloudflareのAPIキー設定
- https://dash.cloudflare.com/profile/api-tokens に移動してAPIトークンを作成してください。
 - 必要な権限:日本語版
 

- 必要な権限:英語版
 

TerraformでCloudflare Tunnelを設定
Terraformで設定を行っていきます。
- webサイト編集画面でアカウントIDを控えておく
 

- terraformファイルを作成し、適切なパラメータを設定しておく
 
locals {
  cloudflare_account_id = "****"        # 自前のアカウントのアカウントIDに置き換えてください
  cloudflare_zone_name  = "example.com" # 自前のアカウントのドメイン名に置き換えてください
  subdomain             = "nginx"       # webサーバに設定したいサブドメインに置き換えてください
  fqdn                  = "${local.subdomain}.${local.cloudflare_zone_name}"
}
variable "cloudflare_token" {
  description = "Cloudflare API token created at https://dash.cloudflare.com/profile/api-tokens"
  type        = string
  sensitive   = true
}
terraform {
  required_providers {
    cloudflare = {
      source  = "cloudflare/cloudflare"
      version = "4.40.0"
    }
    random = {
      source = "hashicorp/random"
    }
  }
  required_version = ">= 0.13"
}
# Providers
provider "cloudflare" {
  api_token = var.cloudflare_token
}
provider "random" {
}
resource "random_password" "tunnel_secret" {
  length = 64
}
# Creates a example tunnel.
resource "cloudflare_zero_trust_tunnel_cloudflared" "example_tunnel" {
  account_id = local.cloudflare_account_id
  name       = "Example tunnel"
  secret     = base64sha256(random_password.tunnel_secret.result)
}
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "example_config" {
  account_id = local.cloudflare_account_id
  tunnel_id  = cloudflare_zero_trust_tunnel_cloudflared.example_tunnel.id
  config {
    warp_routing {
      enabled = true
    }
    ingress_rule {
      hostname = local.fqdn
      service  = "http://localhost:80"
    }
    ingress_rule {
      service = "http_status:404"
    }
  }
}
data "cloudflare_zone" "example" {
  name = local.cloudflare_zone_name
}
resource "cloudflare_record" "example" {
  zone_id = data.cloudflare_zone.example.zone_id
  name    = local.subdomain
  content = "${cloudflare_zero_trust_tunnel_cloudflared.example_tunnel.id}.cfargotunnel.com"
  type    = "CNAME"
  proxied = true
}
- Terraformのvariableを指定するファイルを作成しておく
- 例としてexample.tfvarsという名前で作成
 
 
cloudflare_token = "****" # 自前のトークンに置き換える
- Terraformを実行し、Cloudflare TunnelとDNSレコードの設定を行う
terraform initterraform plan -var-file example.tfvarsterraform apply -var-file example.tfvars
 
webサーバにcloudflaredを導入しCloudflare Tunnelに接続する
webサーバにcloudflaredというCloudflare Tunnelに接続するためのクライアントを導入し、設定する。
- Cloudflare Zero Trustのwebページに移動する
 
- Tunnelsメニューから作成したトンネルのConfigure画面に移動する
 

- Configure画面の下方にChoose your environmentメニューがあるので、webサーバの環境を選択する
 

- 同じ画面にInstall and run a connectorメニューがあるので、curlから始まるコマンドをコピーしてwebサーバで実行してcloudflaredをインストール、セットアップする
- ドラッグで選択してコピーではなく、枠をクリックするだけでコピーできる
 
 
動作確認
シンプルにwebブラウザから「TerraformでCloudflare Tunnelを設定」で指定したFQDNに接続するだけです!
(スマホのモバイル回線などで接続するとローカルネットワーク経由の可能性を除外できます)
HTTPsでの終端もやってくれていますね!

おわりに
今回は自宅サーバの定番でありCloudflareの基本中の基本であるwebサーバの公開をCloudflare Tunnelを通すという変化球の構成で行ってみました!
まだまだCloudflareの真価は発揮できていませんが、第一歩としては程よい難易度だったのではないでしょうか。
脚注
米国およびその他の管轄区域において、Cloudflare、Cloudflareのロゴ、Cloudflare Workersは、Cloudflare, Inc.の商標および/または登録商標です。
