Fastly Terraform Provider を使ったオリジン ACL/Firewall Rule の自動更新
Fastly Terraform Provider について
認知度はまだそれほど高くないのですが、Fastly からは専用の Terraform Provider が開発・公開されており、地道にアップデートが行われています。
私自身、直近でこの Provider の開発に携わらせてもらい、それなりの数のバグを直したり細かな機能追加を行ってきたので、1・2年前の品質と比べると、現在のバージョンはかなり安定して利用できる状態になっていると思います。ぜひ試してみてください。
Fastly の VCL/Wasm サービス設定の変更をコード管理できるのはもちろん (IaC)、WAF や TLS 証明書の管理にも幅広く対応しています。
Fastly が公開している Public IP list
こちらも同様にあまり知られていないのですが、Fastly は自社インフラの Public IP list を公開しており、最新のリストはいつでも API から取得できます。
これは何かというと、クライアント -> Fastly POP -> オリジン というリクエストフローにおいて (i.e., cache MISS/PASS)、Fastly POP がオリジンサーバーにアクセスする際に利用する IP アドレスを意味しています。つまり、オリジンからみた Fastly の src IP です。
また、Fastly のリアルタイムログ送信時にもデータはこの IP アドレス範囲内から送られてきます。
このリストを元にオリジンサーバー側で ACL/Firewall を適切に行うことで、Fastly ネットワーク以外からのオリジンへの直アクセスを禁止し、不要なセキュリティリスクを排除することができます。
突然のサービスダウン
「何も変更していないのに突然 503 エラーが頻発している!オリジンサーバーには全く問題はない!」
「急にログが送られてこなくなった!」
ある日突然サービスダウンに見舞われる事になります。
調べていくと、Fastly -> オリジンへの接続が確立できません。
そうです、上述の Public IP list を元に作成した ACL が作ったっきり放置され、きちんと最新のリストと同期・更新する運用がされていなかったのです。
これは実際に Fastly で働いていた経験からも、非常によくあるケースの1つでした。
その原因の一つは…
Public IP list の更新頻度
そこにはっきりと決まったスケジュールは存在しておらず、Fastly のインフラ増強や他の様々な理由で不定期に Public IP list が更新されることになります。この頻度は一般に非常に低く、多くても1年に1~2回というのがこれまでの実績だと思います。
この頻度故に更新が見逃されやすく、障害が起きて初めて気づく、という流れが起きてしまいます。CDN のように、全てのリクエストを吸収するフロントレイヤーにおいて、こうしたミスは致命的です。
Terraform を使った自動化
ではようやく本題に入りましょう。
Fastly Terraform Provider が提供している fastly_ip_ranges
data source を使う事ができます。
まずは data source をクエリして動作に問題が無いことを確認します。
terraform {
required_providers {
fastly = {
source = "fastly/fastly"
version = ">= 0.41.0"
}
}
}
provider "fastly" {
no_auth = true
alias = "no_auth"
}
data "fastly_ip_ranges" "fastly" {
provider = fastly.no_auth
}
output "fastly_ip_ranges" {
value = data.fastly_ip_ranges.fastly
}
$ terraform plan
Changes to Outputs:
+ fastly_ip_ranges = {
+ cidr_blocks = [
+ "103.244.50.0/24",
+ "103.245.222.0/23",
+ "103.245.224.0/24",
+ "104.156.80.0/20",
+ "140.248.128.0/17",
+ "140.248.64.0/18",
+ "146.75.0.0/17",
+ "151.101.0.0/16",
+ "157.52.64.0/18",
+ "167.82.0.0/17",
+ "167.82.128.0/20",
+ "167.82.160.0/20",
+ "167.82.224.0/20",
+ "172.111.64.0/18",
+ "185.31.16.0/22",
+ "199.232.0.0/16",
+ "199.27.72.0/21",
+ "23.235.32.0/20",
+ "43.249.72.0/22",
]
+ id = "1355856011"
+ ipv6_cidr_blocks = [
+ "2a04:4e40::/32",
+ "2a04:4e42::/32",
]
}
[...]
上記のように正しくデータが表示されていれば OK です。
AWS Security Group への適用
ここでは簡単な例として、特定の AWS Security Group へルールを追加してみます。
terraform {
required_providers {
fastly = {
source = "fastly/fastly"
version = ">= 0.41.0"
}
aws = {
source = "hashicorp/aws"
version = ">= 3.74.0"
}
}
}
provider "fastly" {
no_auth = true
alias = "no_auth"
}
data "fastly_ip_ranges" "fastly" {
provider = fastly.no_auth
}
provider "aws" {
# assume configurations are supplied via env variables
}
data "aws_security_groups" "fastly" {
filter {
name = "group-name"
values = ["fastly-src-ip"]
}
}
resource "aws_security_group_rule" "fastly-http" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = data.fastly_ip_ranges.fastly.cidr_blocks
ipv6_cidr_blocks = data.fastly_ip_ranges.fastly.ipv6_cidr_blocks
security_group_id = data.aws_security_groups.fastly.ids[0]
}
resource "aws_security_group_rule" "fastly-https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = data.fastly_ip_ranges.fastly.cidr_blocks
ipv6_cidr_blocks = data.fastly_ip_ranges.fastly.ipv6_cidr_blocks
security_group_id = data.aws_security_groups.fastly.ids[0]
}
上記の例では、まず aws_security_groups
data resource を使って対象となる fastly-src-ip
の Security Group id を取得し、aws_security_group_rule
resource でルールの追加を行っています。
今回はデモ目的で filter
によって取得される id が1つだけと分かっているので security_group_id = data.aws_security_groups.fastly.ids[0]
にようにリストの最初の要素だけを処理していますが、VPC が複数あり 同名の Security Group が複数存在するような場合には、結果に複数の id が含まれるため、aws_security_group_rule
resource で for_each
を使うなどし、適宜対処できると思います。
Terraform の醍醐味は、なんといってもそれを取り巻くエコシステムの充実にあります。
上記では AWS を例に挙げましたが、GCP や Azure にも専用の Provider は提供されており、同様の手段が取れることは言うまでもありません。オンプレミス環境にも活用できるでしょう。
apply
自動化に向けて
ここまで、Terrafrom を使って簡単に Fastly の Public IP list を取得し、任意のリソースに渡せることが分かりました。ここから先の実際の自動化の実運用は、現在の組織で運用されている CI/CD パイプラインによって使いやすい選択肢を取れると思います。
ACL の更新、という最小単位での処理に切り出しているのであれば、apply
を cron 的に実行し、オペレーターの介入なしにそのままシンプルに自動化してしまうのも1つですし (--auto-approve
)、安全バーを設けたい場合には plan
の差分によって任意のワークフローをトリガーし、Slack の通知によってオペレーターの承認を促す、という一般的な方法も取れると思います。
Discussion