AWS/Terraform

【Terraform】NLBにセキュリティグループをアタッチしてみた

今回は、2023/8/10にサポートされた、NLBへのセキュリティグループアタッチをTerraformで実行してみました。

2023/8/18にリリースされたAWSプロバイダ5.13.0以降から、NLBへのセキュリティグループアタッチがTerraformでできるようになったようです。

構成図

今回のTerraformの構築対象は以下です。

Terraformのバージョン情報

筆者が使用したTerraformのバージョンは以下です。

Terraform Core :1.5.5
AWSプロバイダ :15.13.1

tfファイルの内容

今回の構築に使用したtfファイルは以下です。
※バックエンドやプロバイダ指定の動作設定は省略しています(AWSリソースのコードのみ記述しています)。

infra.tf

NLB用セキュリティグループのインバウンドは便宜上、0.0.0.0/0 としています。
EC2に設定しているセキュリティグループのインバウンドルールは、NLBにアタッチするセキュリティグループIDのみを許可しています。

メタ引数である「for_each」の使い方については、以下の記事でご紹介しています。

【Terraform】3AZ分のサブネットを1つの定義で構築してみた今回はTerraformを用いて、AWS上に3AZ分のサブネットを1つの定義(リソースブロック)で構築しましたので、その方法をご紹介して...
############################################################################
# VPC
############################################################################
resource "aws_vpc" "vpc" {
  cidr_block = "10.0.0.0/16"

  tags = {
    Name = "test-vpc"
  }
}

############################################################################
# インターネットゲートウェイ
############################################################################
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "test-igw"
  }
}

############################################################################
# サブネット
############################################################################
# NLB用パブリックサブネット
resource "aws_subnet" "nlb_subnet" {
  for_each = {
    "a" = "10.0.0.0/24"
    "c" = "10.0.1.0/24"
  }
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "test-nlb-subnet-${each.key}"
  }
}

# EC2用パブリックサブネット
resource "aws_subnet" "ec2_subnet" {
  for_each = {
    "a" = "10.0.4.0/24"
    "c" = "10.0.5.0/24"
  }
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = each.value
  availability_zone = "ap-northeast-1${each.key}"
  tags = {
    Name = "test-ec2-subnet-${each.key}"
  }
}

############################################################################
# ルートテーブル
############################################################################
resource "aws_route_table" "public_route" {
  vpc_id = aws_vpc.vpc.id
  route {
    gateway_id = aws_internet_gateway.igw.id
    cidr_block = "0.0.0.0/0"
  }
  tags = {
    Name = "test-public-route"
  }
}

# サブネットとの紐づけ
resource "aws_route_table_association" "route_public_association_nlb" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  subnet_id      = aws_subnet.nlb_subnet["${each.key}"].id
  route_table_id = aws_route_table.public_route.id
}

resource "aws_route_table_association" "route_public_association_ec2" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  subnet_id      = aws_subnet.ec2_subnet["${each.key}"].id
  route_table_id = aws_route_table.public_route.id
}

############################################################################
# セキュリティグループ
############################################################################
# NLB用セキュリティグループ
resource "aws_security_group" "sg_nlb" {
  name        = "test-sg-nlb"
  description = "Allow http from NLB"
  vpc_id      = aws_vpc.vpc.id

  ingress {
    from_port = 80
    to_port   = 80
    protocol  = "tcp"
    cidr_blocks = [
      "0.0.0.0/0",
    ]
  }
  egress {
    cidr_blocks = [
      "0.0.0.0/0",
    ]
    from_port = 80
    protocol  = "tcp"
    to_port   = 80
  }
}

# EC2用セキュリティグループ
resource "aws_security_group" "sg_ec2" {
  name        = "test-sg-ec2"
  description = "Allow http from NLB"
  vpc_id      = aws_vpc.vpc.id

  egress {
    cidr_blocks = [
      "0.0.0.0/0",
    ]
    from_port = 80
    protocol  = "tcp"
    to_port   = 80
  }
  egress {
    cidr_blocks = [
      "0.0.0.0/0",
    ]
    from_port = 443
    protocol  = "tcp"
    to_port   = 443
  }
}

# EC2用セキュリティグループのインバウンドへNLBのセキュリティグループをソースに指定
resource "aws_security_group_rule" "sg_ec2_rule" {
  type                     = "ingress"
  to_port                  = 80
  protocol                 = "tcp"
  source_security_group_id = aws_security_group.sg_nlb.id
  from_port                = 80
  security_group_id        = aws_security_group.sg_ec2.id
}

ec2.tf

AMIは、Data sourceでAmazon Linux 2 x86の最新バージョンを取得して指定しています。
user_dataで、apacheをインストールしてindex.htmlを作成します。

##############################################################################
# EC2インスタンス
##############################################################################
resource "aws_instance" "ec2" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  ami                    = data.aws_ssm_parameter.amzn2_ami.value
  associate_public_ip_address = true
  instance_type          = "t2.micro"
  subnet_id              = aws_subnet.ec2_subnet["${each.key}"].id
  vpc_security_group_ids = [aws_security_group.sg_ec2.id]
  tags = {
    Name = "test-ec2-${each.key}"
  }
  user_data = <<EOF
#!/bin/bash
yum update -y
yum install -y httpd
systemctl start httpd
systemctl enable httpd
touch /var/www/html/index.html
echo "Hello AWS!" | tee -a /var/www/html/index.html
EOF
}

# 最新AMI取得用データ
data aws_ssm_parameter amzn2_ami {
  name = "/aws/service/ami-amazon-linux-latest/amzn2-ami-hvm-x86_64-gp2"
}

elb.tf

AWSプロバイダ5.13.0以降は、リソースブロック「aws_lb」でパラメータ「load_balancer_type」の値が「network」でも(NLBでも)、パラメータ「security_groups」を記述できるようになっています。
※今までは「load_balancer_type」が「application」の場合のみ(ALBのみ)対応だった。

############################################################################
# ELB
############################################################################
#---------------------------------------------------------------------------
# NLB
#---------------------------------------------------------------------------
# NLB本体
resource "aws_lb" "nlb" {
  name               = "test-nlb"
  internal           = false
  load_balancer_type = "network"
  security_groups    = [aws_security_group.sg_nlb.id]
  subnets = [
    aws_subnet.nlb_subnet["a"].id,
    aws_subnet.nlb_subnet["c"].id
  ]
  tags = {
    Name = "test-nlb"
  }
}

# NLBのターゲットグループ
resource "aws_lb_target_group" "tg_nlb" {
  name        = "test-tg-nlb"
  port        = 80
  protocol    = "TCP"
  vpc_id      = aws_vpc.vpc.id
  target_type = "instance"
}

# NLBのターゲットグループへのターゲット紐づけ
resource "aws_lb_target_group_attachment" "tg_nlb_attachment" {
  for_each = {
    "a" = "1"
    "c" = "2"
  }
  target_group_arn = aws_lb_target_group.tg_nlb.arn
  target_id        = aws_instance.ec2["${each.key}"].id
}

# NLBのリスナー設定(NLBとターゲットグループの紐づけ)
resource "aws_lb_listener" "listener_nlb" {
  load_balancer_arn = aws_lb.nlb.arn
  port              = 80
  protocol          = "TCP"
  default_action {
    type             = "forward"
    target_group_arn = aws_lb_target_group.tg_nlb.arn
  }
}

NLBにセキュリティグループがアタッチされていた

terraform applyの実行後、NLBが作成されてセキュリティグループがアタッチされていることを確認しました。

NLBにアクセスしてみる

作成したNLBの[詳細]タブ>DNS名をコピーします。

コピーしたDNS名をブラウザに入力して、無事アクセスできました。

NLBのセキュリティグループでアクセスを拒否してみる

NLBにアタッチしているセキュリティグループのインバウンドルールを削除すると、アクセスできなくなりました。
NLBにアタッチしているセキュリティグループが機能していることが分かりますね。

まとめ

今回は、TerraformでNLBへセキュリティグループをアタッチする方法をご紹介しました。

NLBのターゲットとなるEC2のセキュリティグループでは、NLBにアタッチされたセキュリティグループIDをインバウンドルールで許可するだけで、NLB以外からのアクセスを遮断できるのが便利ですね。

NLB自体はPrivateLink(VPCエンドポイント)を用いた各サービスとの通信要素としても使うので、利便性はまだまだありそう。

この記事をとおして、少しでも誰かのお役に立てれば幸いです。

参考情報

・AWS公式ドキュメント(Network Load Balancer now supports security groups)
https://aws.amazon.com/jp/about-aws/whats-new/2023/08/network-load-balancer-supports-security-groups/

・Terraform公式ドキュメント(Resource: aws_lb)
https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lb.html