Cloudflare Tunnelでk8sのServiceを公開する
2024-09-29
#はじめに
今回は、Cloudflare Tunnelを使ってk8sのServiceを公開する方法について紹介します。
#目次
#なぜCloudflare Tunnelを使うのか
まず、Webアプリを作って、サクッとデモとして公開したいと思ったとき、どうしますか?
当社では主にGoogle Cloudを使っていますが、Google Cloudの場合、アプリを公開する方法としていくつかあります。
- Cloud Run
- Google App Engine
- GKE + Cloud Load Balancer
Cloud Run, Google App EngineはIAPを設定すれば、特定の人だけに公開できますが、サーバレスであるため、動作させるアプリによっては不向きな場合があります。
GKE + Cloud Load Balancerは、コンテナであれば動かすのに困りませんが、Cloud Load Balancerがこのような用途ではオーバースペックであると考えています。
当社では主にコンテナを動かすのにKubernetesを使っており、ドメインをCloudflareで管理しています。
Cloud Load Balancerを使わずに、KubernetesにデプロイしたアプリをCloudflareで公開する方法を探していたところ、Cloudflare Tunnelを見つけました。
#Cloudflare Tunnelとは
Cloudflare Tunnel provides you with a secure way to connect your resources to Cloudflare without a publicly routable IP address. With Tunnel, you do not send traffic to an external IP — instead, a lightweight daemon in your infrastructure (cloudflared) creates outbound-only connections to Cloudflare’s global network. Cloudflare Tunnel can connect HTTP web servers, SSH servers, remote desktops, and other protocols safely to Cloudflare. This way, your origins can serve traffic through Cloudflare without being vulnerable to attacks that bypass Cloudflare.
Cloudflare Tunnelは、パブリックにルーティング可能なIPアドレスなしでCloudflareにリソースを接続する安全な方法を提供します。Tunnelでは、外部IPにトラフィックを送信するのではなく、お客様のインフラ(cloudflared)内の軽量デーモンがCloudflareのグローバルネットワークへのアウトバウンド専用接続を作成します。Cloudflare TunnelはHTTPウェブサーバー、SSHサーバー、リモートデスクトップ、その他のプロトコルを安全にCloudflareに接続できます。こうすることで、オリジンはCloudflareをバイパスする攻撃に対して脆弱になることなく、Cloudflareを通してトラフィックを提供することができます。
説明は上記の通りです。
Cloudflare Tunnelを知る前は、ngrokが似たようなサービスという認識がありました。
どちらも閉域やパブリックIPを持たないマシンでもインターネットに公開できるサービスです。
#Cloudflare Tunnelを使ってみた感想
実際に利用してみた感想として、以下の特徴があります。
- サブドメインを設定して、トンネルを作成するとすぐにつながる
- Zero Trust Access Applicationと連携して、アクセス制御ができる
- 閉域やパブリックIPを持たないサーバーにもアクセスできるようになる
1の特徴は、通常、サブドメインにDNSの設定をしてオリジンのIPアドレスなどをDNSに設定しますが、そうするとDNSの設定が反映されるまで時間がかかります。Cloudflare Tunnelを使うと、サブドメインを設定して、トンネルを作成するとすぐにつながるので、デモやテスト環境を作るときに便利です。
2の特徴は、Zero Trust Access Applicationと連携して、アクセス制御ができるということです。デモで特定の人にだけ公開したい場合、アクセス制御ができるので、セキュリティ面でも安心です。
3の特徴は、閉域やパブリックIPを持たないサーバーにもアクセスできるので、例えば自宅のPCにアクセスするときにも使えますし、クラウド上のプライベートIPしか持っていないサーバーにもアクセスできるようになります。
あと、Cloudflare Tunnelは無料でも使えるので、コストを気にせずに使えるのも魅力です。ただし、無料プランだと、SLAの保証がなかったりするので、それぞれの用途に合わせてプランを選ぶと良いでしょう。
#実際に構築してみる
terraformでCloudflareのリソースを作成し、k3dでk8sクラスタを作成します。そして、k8sのServiceを作成して、Cloudflare Tunnelで公開します。
#Cloudflareのリソースを作成する
事前にCloudflareのAPI Tokenを取得しておきます。
権限は以下の通りです。
All accounts - Cloudflare Tunnel:Edit, Zero Trust:Edit, Access: Apps and Policies:Edit
All zones - Access: Apps and Policies:Edit, DNS:Edit
リソース作成のポイントは以下の通りです。
-
zero trust access applicationを作成する
policyの設定をして、アクセス制御を行います。
今回は、かんたんに設定できるように、emailでアクセス制御を行います。 -
cloudflare tunnelを作成する
k8sのServiceにルーティングするので、事前にサービス名を設定しておきます。今回設定するのは、
whoami
を使います。http://whoami.default.svc.cluster.local
-
対象ドメインのCNAMEを設定する
パブリックドメインをargotunnelのドメインを指すようCNAMEを設定します。
この設定は、UIからだと自動的に設定してくれるのですが、terraformで設定する場合は、このように手動で設定する必要があります。
APIトークンやアカウントIDは、terraformのtfvarsで設定します。
export TF_VAR_api_token="your api token"
export TF_VAR_account_id="your account id"
以下が、terraformのコードです。localsの部分は、適宜変更してください。
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 4"
}
random = {
source = "hashicorp/random"
}
}
}
provider "cloudflare" {
api_token = var.api_token
}
provider "random" {
}
variable "account_id" {
}
variable "api_token" {
}
locals {
demo_zone_id = "your zone id"
public_domain = "demo.example.com"
team_name = "your team name"
allowed_email = [
"[email protected]"
]
}
resource "random_password" "demo_tunnel_secret" {
length = 32
}
resource "cloudflare_zero_trust_access_application" "demo_app" {
account_id = var.account_id
name = "demo app"
http_only_cookie_attribute = true
domain = local.public_domain
policies = [
cloudflare_zero_trust_access_policy.demo_policy.id
]
}
resource "cloudflare_zero_trust_access_policy" "demo_policy" {
account_id = var.account_id
name = "demo policy"
decision = "allow"
include {
email = local.allowed_email
}
}
resource "cloudflare_zero_trust_tunnel_cloudflared" "demo_tunnel" {
account_id = var.account_id
name = "demo tunnel"
secret = base64encode(random_password.demo_tunnel_secret.result)
}
resource "cloudflare_zero_trust_tunnel_cloudflared_config" "demo_config" {
account_id = var.account_id
tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.demo_tunnel.id
config {
ingress_rule {
hostname = local.public_domain
service = "http://whoami.default.svc.cluster.local"
origin_request {
connect_timeout = "2m0s"
access {
required = true
team_name = local.team_name
aud_tag = [cloudflare_zero_trust_access_application.demo_app.aud]
}
}
}
ingress_rule {
service = "http_status:404"
}
}
}
resource "cloudflare_record" "demo" {
zone_id = local.demo_zone_id
name = local.public_domain
content = "${cloudflare_zero_trust_tunnel_cloudflared.demo_tunnel.id}.cfargotunnel.com"
type = "CNAME"
proxied = true
}
このterraformのコードは、最低限の説明のため記述していますが、実用的にはモジュールに分けて再利用可能にしたり、環境ごとに設定を変えられるようにするなどの工夫が必要です。
#k3dを使ってk8sクラスタを作成する
以前の記事でhelmfileを使ってk8sクラスタを作成する方法を紹介したときのレポジトリを使います。
git clone https://github.com/cloudandbuild/helmfile-example.git
cd helmfile-example
git checkout add-cloudflare-tunnel
以前の記事で説明した手順を実施して、k8sクラスタを作成します。
#tunnel tokenを設定する
tunnel_tokenには、terraformで作成したtunnelのsecretを設定します。
- releases/cloudflare-tunnel-remote/config/default-secrets.yaml
secrets:
tunnel_token: "your tunnel token"
helmで暗号化します。
helm secrets encrypt -i releases/cloudflare-tunnel-remote/config/default-secrets.yaml
#helmfileでデプロイする
helmfile sync
#動作確認
指定したドメインにアクセスすると、最初は、Cloudflare Access
の認証画面が表示されます。この例では、Email認証を使っているので、指定したEmailアドレスにメールが届きます。
認証したあとに再度アクセスすると、whoamiのページが表示されます。DNSの伝搬する場合、数時間要することがありますが、Cloudflare tunnelは、本当にすぐ繋がります。
#参考リンク
#まとめ
今回は、Cloudflare Tunnelを使ってk8sのServiceを公開する方法について紹介しました。
おうちでKuberenetesを使っている方や、外からのWebhookをローカルのPCで受け取りたいなど、色んな用途で使えると思います。
Cloudflare Tunnelは無料でも使えるので、気軽に試してみてください。