はじめに
AWS上に構築する開発環境のALBに対して、セキュリティ面から特定のIPアドレスからのみアクセスを許可することにしました。しかし同じTerraformコードを使って本番環境も管理しており、本番はIP制限をかけてはいけないため、環境によって異なる設定を適用する必要がありました。
対象読者
- Terraformで複数環境を管理している人
- 開発環境のセキュリティを強化したい人
この記事からわかること
- Terraformで環境別にセキュリティグループルールを制御する方法
- 条件分岐を使った柔軟な設定の実装
- tfvarsファイルを使った環境固有の設定管理
この記事では説明しないこと
- AWSセキュリティグループの基本概念
- Terraformの変数の使い方
- ALB(Application Load Balancer)の設定方法
状況
セキュリティグループのingressルールでCIDRブロックが固定値(0.0.0.0/0)になってて、環境に応じた制御の仕組みはありませんでした。
# 修正前:すべての環境で全IPアドレスを許可
resource "aws_security_group_rule" "alb_ingress_https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # 固定値
security_group_id = aws_security_group.alb.id
}
環境
- Terraform v1.5.7
方針検討
検討した方法
1. 条件分岐(三項演算子)を使う方法
cidr_blocks = var.environment == "development" ? var.alb_allowed_ips : ["0.0.0.0/0"]
- メリット: シンプルで読みやすい
- デメリット: 複雑な条件には向かない
今回の要件には十分なのでこれでいきましょう
2. count/for_eachでリソースを分ける方法
resource "aws_security_group_rule" "alb_ingress_https_dev" {
count = var.environment == "development" ? 1 : 0
# 開発環境用の設定
}
resource "aws_security_group_rule" "alb_ingress_https_prod" {
count = var.environment == "production" ? 1 : 0
# 本番環境用の設定
}
- メリット: 環境ごとに完全に異なる設定が可能
- デメリット: リソースが重複して管理が複雑
メンテしずらいため却下...
解決策
Terraformの条件分岐(三項演算子)を使って、環境変数の値に応じてCIDRブロックを切り替える方法で実装します。
実装手順
1. 変数定義(variables.tf)
まず変数を定義します。設定し忘れた時に色々すり抜けて本番に適用されてもいいように、alb_allowed_ipsのデフォルト値は全IP許可にしています。
variable "environment" {
description = "Environment name"
type = string
}
variable "alb_allowed_ips" {
description = "Allowed IP addresses for ALB access"
type = list(string)
default = ["0.0.0.0/0"] # デフォルトは全IP許可
}
2. セキュリティグループルールの実装
シンプルに三項演算子で環境別に切り替えます。HTTP/HTTPSなど複数のルールがあったので同じロジックを適用しました。
security.tf ※ファイル名は任意
# HTTPアクセス
resource "aws_security_group_rule" "alb_ingress_http" {
type = "ingress"
from_port = 80
to_port = 80
protocol = "tcp"
# 環境に応じてCIDRブロックを切り替え
cidr_blocks = var.environment == "development" ? var.alb_allowed_ips : ["0.0.0.0/0"]
security_group_id = aws_security_group.alb.id
}
# HTTPSアクセス
resource "aws_security_group_rule" "alb_ingress_https" {
type = "ingress"
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = var.environment == "development" ? var.alb_allowed_ips : ["0.0.0.0/0"]
security_group_id = aws_security_group.alb.id
}
3. 環境別設定ファイル
tfvarsファイルでアクセスを許可するCIDR指定。あとで絶対「このIPなんだっけ?」となるので、コメントで用途を記載推奨です。
例えば開発環境:development.tfvars、本番環境:production.tfvarsとする場合の例↓
development.tfvars ※記載のIPは適当です
environment = "development"
# ALB Access Control
# 特定のIPアドレスのみ許可
alb_allowed_ips = [
"203.0.113.10/32", # オフィスIP1
"203.0.113.20/32", # オフィスIP2
"198.51.100.0/24" # 検証用端末
]
production.tfvars
environment = "production"
# 本番環境では変数を指定しない(デフォルトの0.0.0.0/0が適用される)
4. 適用方法
実際に適用するときはこんな感じです。planで確認してからapplyします。(applyをいきなり実行してもOKです。お好みで)
# 開発環境への適用
terraform plan -var-file=development.tfvars
terraform apply -var-file=development.tfvars
# 本番環境への適用
terraform plan -var-file=production.tfvars
terraform apply -var-file=production.tfvars
ボツ案
途中、localsブロックで環境別の設定をまとめた方が楽かな?と思いました。
実際にやろうとするとmapのキーが存在しないときのエラーハンドリングが面倒だったので、シンプルな三項演算子に落ち着きました。
# イマイチだった方法
locals {
cidr_blocks = {
development = var.alb_allowed_ips
production = ["0.0.0.0/0"]
}
}
# 使うとき
cidr_blocks = local.cidr_blocks[var.environment]
ポイント
-
デフォルト値で事故を防止する: 何より本番に誤った値をapplyするのが怖いので、デフォルトは本番の設定
["0.0.0.0/0"]にしておきます - 運用負担を感じたら...: 開発メンバーが増えたりリモートワークしたりで設定するIPが増えると面倒なので、VPN使用などしたほうがよいと思います
ひとこと
Terraform、tfファイルの作成は割と直感的。ドリフトしたときが...