[やってみた]Cloudflareを新規に導入することになったのでTerraformで構築してみた
概要
弊社では、AWSを中心にインフラ環境を構築しており、メディア配信についてはCloudfront×S3バケットで提供しています。
あるサービスにおいて、動画配信サービスに注力し始めているのですが、想定よりもコストが増大しており、この最適化を目的にCloudflareの導入を検討することになりました。
AWSについては基本的にTerraformで管理しており、CloudflareについてもTerraformで管理できそうだったので、トライしてみたときの記録をまとめます。
Cloudflare Terraform Provider
Terraformのコード一覧については以下を参照しました。
terraform {
required_providers {
cloudflare = {
source = "cloudflare/cloudflare"
version = "~> 5.8.0"
}
}
}
初期設定~その1~
アカウント作成
まず、最初にアカウントを作成します。ここでいうアカウントとは組織単位のことを指しており、操作するユーザとは異なります。
このとき、まだAPIトークンが存在しないので、管理画面上でアカウントを作成します。
- https://www.cloudflare.com/ja-jp/ にアクセスし、画面右上に存在する「ログイン」ボタンを押下します。
- 画面下部に存在する「Sign up」リンクを押下します。
- メールアドレスとパスワードを入力し、CAPTCHA認証を行ったうえで「Sign up」ボタンを押下します。
初期設定~その2~
APIトークン作成
Terraformの利用にあたり、APIトークンを作成する必要があります。
一番最初は管理コンソール上で一時利用のAPIトークンを作成し、Terraformで操作できるようにしてから改めてAPIトークンを作成する方式を取っています。
- 「アカウントの管理」>「アカウントAPIトークン」を押下します。
- 画面上部に存在する「トークンを作成する」ボタンを押下します。
- (一時利用のため)”追加のトークンを作成”の「テンプレートを使用する」ボタンを押下します。
- 権限が”アカウント”・”APIトークン”・”編集”になっていることを確認し、必要に応じて「クライアントIPアドレスフィルタリング」と「TTL」のパラメータを指定し「概要に進む」ボタンを押下します。
- 内容を確認し、「トークンを作成する」ボタンを押下する。
- 表示されるAPIトークンの値を記録し、以下Terraformのproviderブロックに設定します。
provider "cloudflare" {
api_token = "Hxxxxxxxxxxxxxxxo" // 手順6で確認したAPIトークンの値を指定
}
初期設定~その3~
Terraform設定
まずはTerraformでアカウントAPIトークンを作成するコードを記載します。
/*
* アカウント情報
*/
data "cloudflare_account" "hogehoge" {
filter = {
name = "hogehoge"
}
}
/*
* APIトークンの権限グループ一覧取得
*/
data "cloudflare_account_api_token_permission_groups_list" "all" {
account_id = data.cloudflare_account.hogehoge.account_id
}
locals {
api_token_permission_group_map = {
for group in data.cloudflare_account_api_token_permission_groups_list.all.result :
group.name => group.id...
}
api_token_permission_groups = {
// 使用する権限グループを指定
account_api_tokens_write = local.api_token_permission_group_map["Account API Tokens Write"][0]
account_settings_write = local.api_token_permission_group_map["Account Settings Write"][0]
account_rulesets_write = local.api_token_permission_group_map["Account Rulesets Write"][0]
workers_r2_storage_write = local.api_token_permission_group_map["Workers R2 Storage Write"][0]
zone_write = local.api_token_permission_group_map["Zone Write"][0]
}
}
/*
* アカウントAPIトークン
*/
resource "cloudflare_account_token" "for_terraform" {
account_id = data.cloudflare_account.hogehoge.account_id
name = "Create Additional Tokens"
policies = [
{
effect = "allow"
permission_groups = [
// ここに必要な権限を指定
{ id = local.api_token_permission_groups.account_api_tokens_write },
{ id = local.api_token_permission_groups.workers_r2_storage_write },
{ id = local.api_token_permission_groups.account_rulesets_write },
{ id = local.api_token_permission_groups.account_settings_write },
{ id = local.api_token_permission_groups.zone_write },
]
resources = {
// 権限の適用範囲を指定
"com.cloudflare.api.account.${data.cloudflare_account.hogehoge.account_id}" = "*"
}
},
]
condition = {
request_ip = {
in = [
// アクセス許可を与えるIPアドレスを指定
"123.45.67.89/32",
]
}
}
lifecycle {
ignore_changes = [
// TTL周りは更新されるため、管理対象外としている。
expires_on,
not_before,
]
}
}
こちらでterraform apply
を実行すると、APIトークンが作成されます。
その後、Cloudflareの管理画面にアクセスし、作成されたAPIトークンを確認します。
- 「アカウントの管理」>「アカウントAPIトークン」を押下します。
- terraformで作成したトークン名(今回は"Create Additional Tokens")の三点リーダーを押下し、「ロール」を押下する。
- トークン Create Additional Tokens をロールしてもよろしいですか?と表示されるので、「ロール」ボタンを押下する。
- 管理画面上でAPIトークンを作成したときと同様に、APIトークンの値を記録し、先ほど設定したproviderブロックのapi_tokenの値の差し替えます。
以上で、Terraformを使ってCloudflareのリソースを操作する準備が整いました。
次は実際にリソースを作成していきます。
R2 初期設定
AWSでいうS3バケットに相当するストレージサービスのR2をTerraformで構築してみます。
/*
* アカウント情報
*/
data "cloudflare_account" "hogehoge" {
filter = {
name = "hogehoge"
}
}
/*
* ゾーン(ドメイン)情報
*/
data "cloudflare_zone" "hogehoge_com" {
filter = {
name = "hogehoge.com"
}
}
# ===========================================
# R2
# ===========================================
resource "cloudflare_r2_bucket" "hogehoge" {
account_id = data.cloudflare_account.hogehoge.account_id
name = "hogehoge"
location = "APAC"
storage_class = "Standard"
}
resource "cloudflare_r2_custom_domain" "test_hogehoge" {
account_id = data.cloudflare_account.hogehoge.account_id
bucket_name = cloudflare_r2_bucket.hogehoge.name
domain = "test.hogehoge.com"
enabled = true
zone_id = data.cloudflare_zone.hogehoge_com.zone_id
min_tls = "1.3"
}
resource "cloudflare_r2_bucket_cors" "hogehoge" {
account_id = data.cloudflare_account.hogehoge.account_id
bucket_name = cloudflare_r2_bucket.hogehoge.name
rules = [{
allowed = {
methods = ["GET"]
origins = [
"http://localhost:3000",
"https://test.hogehoge.com",
]
}
}]
}
特定ドメインのアクセス制限
AWSで使用していた際、特定環境へのアクセスはWAFなどで制限をかけており、Cloudflareでも同様の設定を行います。
/*
* ゾーン(ドメイン)情報
*/
data "cloudflare_zone" "hogehoge_com" {
filter = {
name = "hogehoge.com"
}
}
/*
* 許可するIPアドレス一覧
*/
locals {
office_ip_list = {
tokyo_ip = "123.45.67.89"
osaka_ip = "23.45.67.89"
}
allowed_ips = values(local.office_ip_list)
# CloudflareのExpression用にフォーマット
allowed_ips_expression = "{${join(" ", local.allowed_ips)}}"
}
# ===========================================
# セキュリティルール
# ===========================================
resource "cloudflare_ruleset" "custom_security" {
zone_id = data.cloudflare_zone.hogehoge_com.zone_id
name = "default"
kind = "zone"
phase = "http_request_firewall_custom"
rules = [
{
action = "block"
expression = <<-EOT
(
not ip.src in ${local.allowed_ips_expression} and
http.host eq "test.hogehoge.com"
) or
(
not ip.src in ${local.allowed_ips_expression} and
http.host eq "test-2nd.hogehoge.com"
)
EOT
description = "TESTアクセス"
enabled = true
}
]
}
最後に
普段触っているAWSと比較すると、細かいところでエラーが発生しており、100%Terraformで管理はできていませんが、当初の目的はほぼTerraformで完結するところまで到達できました。
特に、アクセス制限におけるIP制限等については、管理画面からの設定だと煩雑になってしまうため、コードで管理できるのは非常に助かっています。
当記事がCloudflareをTerraformで管理したい方の一助になれば幸いです。
Discussion